Как сделать sleep на N секунд либо до прихода HTTP запроса (асинхронный cron с http сервером)?
Есть некая питоновская программа (клиент), которая периодические делает некоторую работу (вечный цикл, в котором она лезет на сервер, проверяет состояние и если надо - делает дела, в конце итерации - sleep() на 5 минут, чтобы к следующей итерации скорее всего появилась новая работа). Естественно, если новые данные появляются сразу после обработки - они будут обработаны только через 5 минут - это нехорошо.
Хочется оптимизировать ее немного. Пусть клиент в состоянии ожидания крутит простой веб-сервер, при появлении новой задачи сервер делает HTTP запрос на этот URL, и клиент, получив запрос прекращает ожидание, начинает обработку и затем снова приходит к этому ожиданию. Таким образом обработка будет запускаться сразу же после появления новой работы.
Вижу два варианта:
1. Нужен какой-то 'cron с http сервером'. Чтоб ему сказать - "запускай скрипт каждые 5 минут, либо сразу по приходу HTTP запроса".
2. Сделать это в самой программе. Но тогда нужно вместо sleep() как-то запустить веб-сервер на 5 минут. А при приходе запроса - досрочно его прибить.
Есть какой-то готовый простой вариант решения для этого (задача кажется достаточно типовой)? Пока что все, что приходит на ум - это "изобретение велосипеда", причем какими-то хитрыми и сложными способами.
Lynn «Кофеман», без слипа получится, что работа появляется раз в день, допустим, а клиент будет грузить сервер нон-стоп почти на сто процентов. "Есть там что новое? А сейчас? А щас? А щас появилось? А может сейчас есть?". Вот и нужно как-то не допускать тысячи бесполезных запросов в минуту.
Lynn «Кофеман», по HTTP хочется, чтобы приходило просто уведомление, оповещение, сигнал с минимумом информации "что-то случилось, самое время разобраться и если надо - поделать их" (самих данных может быть очень много). Но HTTP запрос - штука ненадежная. Могут быть любые сетевые проблемы иногда, а серверная часть оповещений должна быть легкая, а не так, что если вдруг клиент выключился - сервер без остановки пытается его долбить неделю. (представим, что таких клиентов тысяча, и из этой тысячи десяток мертвые)
Поэтому хочется иметь дублирующий механизм который просто по тайм-ауту будет выполнять проверки. (нет новостей 5 минут? Окей, проверим сами). Но он не запускается если оповещения приходят достаточно часто.
Представим аналогию, что мы работаем с почтой. Клиент - что-то вроде fetchmail, раз в 5 мин проверяет и качает почту по POP3. И мы хотим чтобы почтовый сервер нас оповещал сразу же при приходе нового письма, и мы сразу же скачали почту.
Но целиком полагаться на HTTP не можем, поэтому нужно оставить запуск через 5 минут после предыдущего запуска. Это позволит восстановиться после любого сетевого сбоя, потеряв не более 5 минут.
alexvdem, не хотелось опираться на внешний сервис, но идея интересная. Это еще и бесплатно? Для питона есть либы чтобы слушать оповещения с таймаутом, не знаете?
Дмитрий, да, такое будет работать. Просто в скрипте надо будет добавить эту проверку времени файла. Просто, думал, может быть есть вариант как-то красивее это сделать (тут у нас и cron и http).
Поэтому хочется иметь дублирующий механизм который просто по тайм-ауту будет выполнять проверки. (нет новостей 5 минут? Окей, проверим сами). Но он не запускается если оповещения приходят достаточно часто.
Чтобы не проверять параллельный запуск: 1 демон, который каждые 5 минут делает работу. Сделав работу он слушает tcp-порт или даже unix-сокет, и ждет команды на досрочный запуск (http здесь избыточен).
Если удобнее по http, а впиливать в демон даже урезанный http-сервер не хочется (или он уже есть), то отдельно стоящий веб-сервер, запускаемые из него скрипты, которые посылают стандартный unix сигнал демону. Нужно будет передавать pid для этого, но вероятно это проще сделать через systemd.
alexvdem, видимо, нужно пояснить. Я видел, что там написано. Однако, думаю, вы и сами за минуту можете накидать несколько "относительно честных способов", как маркетологи могут предлагать бесплатную услугу, однако, ее настоящее использование будет все таки стоит денег. Именно поэтому я и спросил, про ваш личный опыт и ваше мнение. То, что есть питоновские либы я тоже видел (вопрос ведь был не о том), но сходу не увидел в них функционал о приеме push уведомлений. Вполне вероятно, что он там есть, но опять же по этой причине и спросил ваше мнение.
В любом случае, спасибо вам за ваш первый комментарий, в нем была новая и интересная информация.
Ярослав, без sleep как раз никаких проблем. Делаем запрос с таймаутом в 3 минуты, сервер либо за 3 минуты пришлёт данные, либо случится таймаут, но в процессе ожидания ресурсы системы тратиться не будут. Сервер, соответственно, делает long poll ответ.
Но, как тут уже посоветовали, это прекрасный повод попробовать вебсокеты. Тем более что для python есть библиотеки и для чистых вебсокетов, и для socket.io.
Декоратор, который реализует отложенный запуск функции. Уверен, в питоне есть неблокирующие таймеры, достаточно погуглить "python debounce".
Функция-обработчик, вызываемая по запросу извне, также вызывает себя через декоратор для самостоятельного отложенного запуска. Если обработчик будет вызван раньше указанного в декораторе времени, то самостоятельный запуск будет отложен.
Ну так сделайте не один usleep(300000), а 300 штук usleep(1000). И в промежутках проверяйте ваши HTTP, или что-нибудь еще, семафор например. Будет и 5 минут, и ежесекундная реакция. Чай у вас не RealTimeOS, задержки не смертельны?
а кто мешает вам собственно сделать собственно самый простой вариант
у вас в кроне раз в 5 инут запускается пхпшный файл
Если же вам нужно запусти ть прям сейчас то все что вам нужно по завершению подачи данных, просо запустить этот файл через wget или curl
Виктор Таран, ну не пхпшный, питоновский, но это не играет особой роли. Решение должно быть очень легким для сервера. Сама обработка может быть длительной (работа с базой, обращение к другим API разным, таймауты итд). Поэтому не хочется грузить сервер долгими запросами (когда он ждет завершения скрипта), (а таких клиентов может быть много). Поэтому, если сервер как-то дергает скрипт - он это делает с таймаутом в 3-5 секунд на все, чисто чтоб передать сам сигнал, а обработка со стороны клиента должна происходить уже потом.
Виктор Таран, не уверен, что я правильно понял, в конце чего? end.py - это наш обработчик? Мешает то, что end.py (обработчик) может работать работать три часа. Клиентов очень много. Сотни запущенных wget'ов не хочется иметь.... тем более, как-то это неправильно - серверу ведь реально не нужно ждать завершения обработки на клиенте. И результат с клиента не нужно получать никакой. Поэтому все эти wget'ы - будут эдакой бесполезной кинетической скульптурой в напоминание о том, что сделано все неэффективно :-) Если я, конечно, правильно понял идею.
И еще - придется как-то (на стороне сервера) решать проблему с недопущением повторного запуска. (если первый скрипт еще не отработал, а мы снова запускаем его же, могут быть разные чудеса)
Для правильного вопроса надо знать половину ответа
Если уж клиент всё равно постоянно висит в памяти, то используйте вебсокеты. Тогда сервер сможет моментально извещать клиентов о появлении нового задания.
Целый веб-сокет сервер, только чтобы "пнуть" демона? Который по-сути будет работать только для 1 клиента, вряд ли соединение будут держать постоянно, для таких редких запросов это напрасная трата ресурсов, а значит моментальности не будет тоже (да и не нужна она здесь). Не проще ли тогда через http?
да вы правы дальше шутка
ssh server 'echo "что-то случилось" > /pipe_name'
для нагруженного сервера не пойдёт но 10запросов в минуту нормально
дальше тоже шутка
есть ещё забытая технология только под windows: Mailslot позволяет послать короткое сообщение
тут не шутка
если на одной машине pipe name, dbus, Mailslot (его хоть и можно на разных машинах, но практика показала что лучше не надо)
если на разных то сокеты, вебсокеты, web.серверы
Строго говоря, не факт, что автор будет использовать именно time.sleep, а не что-то такое, что не будет реагировать на сигналы, да и управлять процессом через посылку сигналов внешним костылём это бееее, а не решение.
Был совет использовать Firebase Cloud Messaging (FCM), которым доставляются push уведомления на устройство. Немного странно, но похоже, его невозможно использовать на десктопе в качестве клиента (принимать сообщения). В python библиотеке нет кода для этого. Даже задал вопрос разработчику PyFCM, его ответ:
from http.server import BaseHTTPRequestHandler, HTTPServer
class RequestHandler(BaseHTTPRequestHandler):
# implementation
httpd = HTTPServer((self.address, self.port), RequestHandler)
httpd.timeout = 1
while True:
httpd.handle_request()
# check if quit
Использование метода handle_request вместо serve_forever() позволяет нам получать управление каждый раз по обработке каждого запроса и проверять, не пора ли выйти или исполнить другие действия. А использование timeout=1 дает нам возврат через 1 секунду даже если запросов не было. В итоге