gevent (py)wsgi изящное завершение работы

Единственный известный мне способ (из поиска и проверки источника gevent) изящно отключить сервер на основе gevent WSGI:

server = gevent.wsgi.WSGIServer(('', 80), someWSGIApp)
def shutdown():
  print('Shutting down ...')
  server.stop(timeout=60)
  exit(signal.SIGTERM)
gevent.signal(signal.SIGTERM, shutdown)
server.serve_forever()

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

Проблема в том, что с приведенным выше казалось бы кодом сервер действительно ожидает макс. 60 секунд, но все TCP-соединения разрываются сразу после получения SIGTERM. Однако гринлеты продолжают делать то, что они делали (например, спят), пока не закончат или не истечет тайм-аут.

Любые идеи?


person Mansour    schedule 16.08.2013    source источник
comment
вы нашли способ сделать это?   -  person Tommaso Barbugli    schedule 23.08.2013


Ответы (3)


Вы можете решить проблему, запустив сервер в одном потоке и выключив его в другом. Код ниже работает в Python 3.7.

from gevent.pywsgi import WSGIServer
import signal
import threading

# define your app here
app = ...

server_address = ("localhost", 4000)


class WebServer(threading.Thread):
    def __init__(self):
        super().__init__()

    def run(self):
        global server
        server = WSGIServer(server_address, app)
        server.serve_forever()


def shutdown(num, info):
    print(f'Shutting down website server...\n'
          f'{num} {info}')
    server.stop()
    server.close()


if __name__ == "__main__":
    server = None
    WebServer().start()

    signal.signal(signal.SIGINT, shutdown)
person Tyrion    schedule 21.10.2019
comment
Это работает частично. Если перед отправкой SIGINT в очереди находится более одного запроса, после отправки SIGINT завершается только один, а остальные сбрасываются (сброс соединения TCP). - person Rahul Shaw; 13.07.2020

Как сказано в строке документации в методе остановки сервера (gevent.baseserver.BaseServer:stop)

Stop accepting the connections and close the listening socket.

If the server uses a pool to spawn the requests, then :meth:`stop` also 
for all the handlers to exit. 
If there are still handlers executing after *has expired (default 1 second), 
then the currently running handlers in the pool are killed.

Я не пробовал это, но если документы верны, вы сможете получить изящную остановку примерно так:

from gevent.pool import Pool

pool_size = 8
worker_pool = Pool(pool_size)
gevent.wsgi.WSGIServer(('', 80), someWSGIApp, spawn=worker_pool)
person Tommaso Barbugli    schedule 23.08.2013

но все TCP-соединения разрываются сразу после получения SIGTERM.

У меня была похожая, но не идентичная проблема...

... моя проблема заключалась в том, что процесс Python завершался, даже если соединения все еще выполнялись. Я решил это, добавив gevent.get_hub().join() после server.serve_forever()

server = gevent.wsgi.WSGIServer(('', 80), someWSGIApp)
gevent.signal(signal.SIGTERM, server.stop)
server.serve_forever()
gevent.get_hub().join()
person Michal Charemza    schedule 09.08.2019