【问题标题】:Use signal handle to shut down flask-socketio server使用信号句柄关闭 flask-socketio 服务器
【发布时间】:2019-11-20 19:50:25
【问题描述】:

我正在使用 macOS Mojave 和 Python 3.7。在我的工作中,我想从信号处理程序中完全关闭 flask-socketio 服务器,在我的代码中定义为 SignalHandler。我注意到,如果没有信号处理程序,可以使用 Ctrl-C 完全关闭服务器。但是,信号处理程序对我的工作来说是必需的。我在网上搜索并找不到在我的情况下关闭服务器的解决方案。供您参考,我找到了通过“call flask-socketio stop from HTTP or SocketIO handler function”或“shut down a gevent (pywsgi) server gracefully”关闭服务器的解决方案。

示例代码如下:

import os
import threading
import signal
import requests

from flask import Flask, send_from_directory
from flask_socketio import SocketIO, Namespace
import eventlet


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

    def run(self):
        app = Flask(__name__, template_folder="templates",
                    static_folder="templates/static")

        app.config['SECRET_KEY'] = 'Secret!'
        socketio = SocketIO(app, engineio_logger=True, logger=True)

        # Create a URL route in our application for "/"
        @app.route('/')
        def test_page():
            """
            This function loads the homepage
            """
            return send_from_directory(
                os.path.join(app.root_path, 'templates'),
                "index1.html"
            )

        @app.route('/stop', methods=['POST'])
        def shutdown_server():
            """
            This function stops the flask-socketio server
            """
            print("Received request to shut down the server.")
            socketio.stop() #something wrong here, but don't know how to solve
            return "The server has been shut down."

        class MyCustomNamespace(Namespace):
            def on_connect(self):
                print("Client just connected")

            def on_disconnect(self):
                print("Client just left")

            def on_messages(self, data):
                print(f"\nReceived data from client: \n {data}\n")
                return data

        socketio.on_namespace(MyCustomNamespace('/channel_A'))

        try:
            eventlet.wsgi.server(
                eventlet.wrap_ssl(eventlet.listen(("localhost", 8080)),
                                  certfile='server.crt',
                                  keyfile='server.key',
                                  server_side=True), app)
        except Exception as e:
            print(f"Website is not established due to:\n{e}")


# Terminate code from shell
class SignalHandler(object):
    def __init__(self):
        pass

    def __call__(self, signum, frame):
        print("Shutting down the website.")

        # Begin 'something' here to shut down the server...
        shutdown_server = requests.post("https://localhost:8080/stop", data=None)
        print(f"Shut down the server feedback: {shutdown_server}")
        # 'Something' ends here

        print("The website has been shut down.")


if __name__ == '__main__':

    WebsiteCreator().start()

    # If the following part is not included, the server can be shut down using Ctrl-C
    handler = SignalHandler()
    signal.signal(signal.SIGINT, handler)

在代码中,我在一个线程中运行 flask-socketio 服务器。我想通过在 SignalHandler 中执行一些操作来关闭服务器。

然而,当我使用 Ctrl-C 退出系统时,出现了一些异常:

^C
Shutting down the website.
(23066) accepted ('127.0.0.1', 49720)
Received request to shut down the server.
127.0.0.1 - - [22/Nov/2019 13:08:18] "POST /stop HTTP/1.1" 200 0 0.000365
wsgi exiting
Exception ignored in: <module 'threading' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py'>
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py", line 1308, in _shutdown
    lock.acquire()
  File "web_app.py", line 74, in __call__
    shutdown_server = requests.post("https://localhost:8080/stop", data=None)        
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/requests/api.py", line 116, in post
    return request('post', url, data=data, json=json, **kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/requests/api.py", line 60, in request
    return session.request(method=method, url=url, **kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/requests/sessions.py", line 533, in request
    resp = self.send(prep, **send_kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/requests/sessions.py", line 646, in send
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/eventlet/hubs/kqueue.py", line 105, in wait
    readers.get(fileno, hub.noop).cb(fileno)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/eventlet/greenthread.py", line 221, in main
    result = function(*args, **kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/eventlet/wsgi.py", line 818, in process_request
    proto.__init__(conn_state, self)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/eventlet/wsgi.py", line 357, in __init__
    self.handle()
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/eventlet/wsgi.py", line 390, in handle
    self.handle_one_request()
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/eventlet/wsgi.py", line 419, in handle_one_request
    self.raw_requestline = self._read_request_line()
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/eventlet/wsgi.py", line 402, in _read_request_line
    return self.rfile.readline(self.server.url_length_limit)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/socket.py", line 589, in readinto
    return self._sock.recv_into(b)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/eventlet/green/ssl.py", line 241, in recv_into
    return self._base_recv(nbytes, flags, into=True, buffer_=buffer)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/eventlet/green/ssl.py", line 256, in _base_recv
    read = self.read(nbytes, buffer_)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/eventlet/green/ssl.py", line 176, in read
    super(GreenSSLSocket, self).read, *args, **kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/eventlet/green/ssl.py", line 150, in _call_trampolining
    return func(*a, **kw)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/ssl.py", line 926, in read
    raise ValueError("Read on closed or unwrapped SSL socket.")
    r = adapter.send(request, **kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/requests/adapters.py", line 498, in send
ValueError: Read on closed or unwrapped SSL socket.
Removing descriptor: 8
142f38bdaaf34c7e8883e99a766fe310: Unexpected error "Read on closed or unwrapped SSL socket.", closing connection
    raise ConnectionError(err, request=request)
requests.exceptions.ConnectionError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))

请给我一些想法!提前致谢。

【问题讨论】:

  • Flask-SocketIO 服务器包含一个stop() 方法,您可以使用它来通知服务器关闭。您的情况的复杂性是需要从处理程序调用此方法。您可以做的是从您的信号处理程序向服务器发送请求,等待请求返回,然后退出处理程序并让应用程序继续让服务器正常结束。
  • @Miguel 感谢您的回复!我会试试的。
  • @Miguel 嗨,我尝试发送请求以停止服务器。但提出了一些例外情况。您可以查看更新后的问题以获取信息。是不是因为代码没有等待请​​求返回?我该如何解决这个问题?
  • 这些错误是典型的终止服务器进程,我不确定是否存在实际问题。每当我关闭我的 gevent 服务时,我都会收到一系列中止连接的错误,如果你不想看到它们,我只会捕获并忽略它们。
  • 我没见过这个错误,但很可能与没有等到服务器完全关闭才退出进程有关。

标签: server python-3.7 shutdown flask-socketio


【解决方案1】:

这是我在项目中使用的,使用 gevent 效果很好。确保在初始化信号后启动服务器。信号应该是相同的,只是减去 gevent 部分。

  def shutdown():
        print('Shutting down ...')
        server.stop(timeout=60)
        exit(signal.SIGTERM)
  gevent.signal(signal.SIGTERM, shutdown)
  gevent.signal(signal.SIGINT, shutdown) #CTRL C
  server.serve_forever()

【讨论】:

  • 嗨,是的,它适用于 gevent。但我需要找到使用 eventlet 的解决方案。但仍然感谢您的想法和帮助!
  • 你可以使用 signal.signal 来镜像这个。
  • 感谢您的想法。其实我一开始就是这么做的。但是不能直接在shutdown函数中停止服务器。从@Miguel 给出的文档中,我需要通过调用 HTTP 请求或 SocketIO 处理函数来停止 flask-socketio 服务器。
猜你喜欢
  • 2013-05-13
  • 1970-01-01
  • 1970-01-01
  • 2016-05-24
  • 1970-01-01
  • 2010-10-20
  • 2015-07-17
  • 1970-01-01
  • 2021-03-25
相关资源
最近更新 更多