【问题标题】:Flask + Gevent, monkey.patch_all() breaks flask烧瓶 + Gevent,monkey.patch_all() 打破烧瓶
【发布时间】:2021-05-17 00:26:22
【问题描述】:

我需要运行 monkey.patch_all(),因为我的应用程序中有一个 Flask 服务器并结合了 ValvePython 库(我收到有关无法切换到其他线程的错误)但是我遇到了问题。 我已经在没有 ValvePython 的情况下对此进行了测试,以确保问题没有严格地悬而未决。

我如何启动服务器:

from gevent import monkey; monkey.patch_all();
if __name__ == "__main__":
    # Create PyQt5 app
    app = QApplication(sys.argv)

    # Flask server
    server = Server('Ryder Engine')

    # Create the custom window (The initialize function creates all the
    # server endpoints dynamically via the add_endpoint function
    window = RyderDisplay()
    window.initialize(server)

    # Run Server
    threading.Thread(target=server.run, daemon=True).start()

    # Start the app
    sys.exit(app.exec())

我的服务器类:

import socket
from flask import Flask, Response, request
from gevent.pywsgi import WSGIServer

class EndpointAction(object):
    def __init__(self, action):
        self.action = action
        self.response = Response(status=200, headers={})

    def __call__(self, *args):
        self.action(request.get_json())
        return self.response

class Server(object):
    def __init__(self, name):
        self.app = Flask(name)

    def run(self, port=9520):
        http_server = WSGIServer(('0.0.0.0', port), self.app)
        http_server.serve_forever()

    def add_endpoint(self, endpoint=None, endpoint_name=None, handler=None):
        self.app.add_url_rule(endpoint, endpoint_name, EndpointAction(handler), methods=['POST'])

其中一个端点绑定到的 PyQt5 应用程序的主页。这是在 Window 对象中实例化的。其余的通过 json 配置文件通过 HomeConfigurationParser 类实例化。

class Home(object):
    # Class constructor
    def __init__(self, window, server : Server):
        self._window = window
        self._client = Client()
        self._server = server

        self._client.subscribeToRyderEngine()
        server.add_endpoint('/status', 'status', self.newStatus)

    # UI Elements
    def create_ui(self, path):
        # Initialize
        path =  path + '/config.json'
        self._fps, self._ui = HomeConfigurationParser.parse(self._window, self._client, self._server, path)

        # Refresher
        self._timer = QTimer()
        self._timer.timeout.connect(self.update)
        self._timer.start(1000 / self._fps)

    def newStatus(self, request):
        self._status = request

    def update(self):
        # Update UI
        for elem in self._ui:
            elem.update(self._status)
        # Reset
        if self._status is not None:
            self._status = None

我的问题是通过运行 monkey.path_all() 服务器不再处理请求,换句话说,它基本上忽略了所有 add_endpoint 函数调用。服务器端点必须在运行时添加,我无法通过函数上方的@直接在代码中添加它们。

为什么会发生这种情况,我该如何解决?

编辑:添加了更多代码。服务器与 PyQt5 接口并行运行。服务器用于接收数据,然后相应地更新 PyQt5 接口

【问题讨论】:

    标签: python flask pyqt5 gevent


    【解决方案1】:

    Flask 源代码 (here) 中的 add_url_rule 有一个 @setupmethod 装饰器,它表示“在处理第一个请求后忽略我”。 (这是@app.before_first_request 机制的关键部分。)

    如果您在一个线程中启动 Flask,然后从另一个线程调用 add_endpoint,则您的调用线程将与您的应用程序的第一个请求竞争。线程安全也存在​​一些严重的问题,这些问题会导致调用从 Flask 主线程外部对 Flask 内部产生副作用的方法。

    在你的位置上,我会重新安排以确保所有add_endpoint 调用都发生在server.run 启动应用程序之前。

    你可能仍然有猴子补丁的问题,但我会先处理这个问题。

    【讨论】:

    • 我添加了更多信息,以便更好地理解我在做什么。我已经改变了事情的顺序,使得服务器是最后一个启动的东西并且问题仍然存在。如果我在没有线程的情况下启动服务器,我可以在控制台中看到确实收到了请求(如果我离开 http_server.serve_forever(),这会无限期冻结我的 pyqt 应用程序,而如果我没有,服务器不会收到任何东西功能但不会冻结我的 pyqt 应用程序)但是当我在自己的线程中启动它时,我看不到控制台中发生任何事情。
    • 我有一个在线程中启动 Flask 的应用程序,但没有猴子补丁和 Qt。我唯一的建议是从类似的东西开始,然后添加补丁,如果可行,添加 Qt。
    • 一个区别,这可能并不重要:我通过app.run 在自己的线程中启动 Flask 应用程序。如果您这样做并将debug=True 作为参数传递,您还需要传递use_reloader=False。
    猜你喜欢
    • 2013-08-19
    • 1970-01-01
    • 2015-08-19
    • 1970-01-01
    • 1970-01-01
    • 2012-12-27
    • 2020-11-23
    • 1970-01-01
    • 2019-01-04
    相关资源
    最近更新 更多