Delphi.RobustServer. Работа с длинными операциями

Короткие запросы хороши тем, что мы можем получить ответ сразу, а вот как обрабатывать длинные запросы? На StackOverflow Remy Lebeau советует дать ответ о старте работы клиенту сразу, и саму работу выносить в отдельный поток.  Я уже писал на эту тему. В данном посте, попробовал реализовать эту идею, в применении к моему RobustServer и вот что у меня получилось.

Для начала я написал класс TLongTaskThread

Он позволил мне создавать потоки, в которых выполнялась бы долгая работа. У каждого экземпляра такого класса есть Guid и Progress, которые бы  позволяли отслеживать прогресс выполнения работ.

В Main, я создал экземпляр TThreadList, в который добавляю потоки, после их создания

Также добавил несколько событий, OnStart, OnProgress, OnFinish, на которые можно подписаться. Например отправлять ответный запрос о выполнении, отправлять информацию о выполнении на почту, ну и так далее, что как говорится душе угодно.

Посмотрим пример применения данного класса

Я создаю экземпляр данного класса, по аналогии с анонимным потоком. В теле потока у нас TProc, внутри которой выполняется простой цикл с засыпанием, имитирующим долгую работу потока )))

Далее, мы подписываем события, которые нам нужны, добавляем поток в TThreadList, который позволит нам безопасно работать с потоками, запускаем наш поток в работу, и наблюдаем за прогрессом.

В ответ, как видно, мы получили ответ, что работа началась и GUID.

События я подписал самым простым способом

По хорошему, здесь надо ещё сделать TThread.Synchronize, но это уже несколько другая задача и в сервер вряд ли кто-то будет отправлять события, рабочий вариант ответным запросом, на почту или ещё куда-то. Поэтому здесь, я этим заморачиваться не стал.

Получение прогресса запросом

Чтобы получить прогресс долгого запроса ещё одним запросом, я оформил такой метод

И вот как он у меня работает

То есть, в ответ json, внутри которого содержится прогресс нашего долгого запроса.

Обработка ошибок долгого запроса

Если в процессе работы долгого запроса произойдет ошибка, что делать в таком случае? Как было видно из кода выше, у нас есть специальное событие OnException, и всё, что нам нужно сделать – подписаться на это событие. В моем самом простом случае это выглядит так…

Посмотрим как это работает на практике.  Раскомментирую часть кода, вызывающую ошибку

И посмотрим на GUI нашего сервера

Ошибка пришла по назначению.

Закрытие сервера и уничтожение долгих потоков

Я сделал это таким образом. В части закрытия долгих потоков, закрытие сервера выглядит так

То есть, в нормальном случае, у нас поток бы отработал и уничтожился самостоятельно, потому что в конструкторе моего класса стоит

Но остановка / закрытие сервера это некий экстренный случай, и поэтому мы инвертируем эту строчку и затем, уничтожаем этот поток. Надо сказать, что метод Free у TThread перекрыт довольно грамотно, и там уже есть вся необходимая логика, которая делает Terminate потоку, и лишь затем его уничтожает

 

 

 

 

This entry was posted in Delphi. Bookmark the permalink.

Leave a Reply