10

我一直在使用烧瓶,我的一些路由处理程序开始计算可能需要几分钟才能完成。使用烧瓶的开发服务器,我可以使用 app.run(threaded=True) 并且我的服务器将在执行这些多分钟计算时继续响应其他请求。

现在我开始使用 Flask-SocketIO,但我不确定如何做同样的事情。我知道我可以在 python 开始其中一个计算时显式地在 python 中生成一个单独的线程。这是唯一的方法吗?或者对于flask-socketio,是否有相当于线程=真的东西。(或者,更有可能,我只是完全糊涂了。)

谢谢你的帮助。

4

1 回答 1

22

Flask/Werkzeug 中线程模式的思想是让开发服务器能够同时处理多个请求。在默认模式下,服务器一次只能处理一个请求,如果客户端在服务器已经在处理前一个请求时发送请求,那么第二个请求必须等到第一个请求完成。在线程模式下,Werkzeug 为每个传入请求生成一个线程,因此可以同时处理多个请求。您显然正在利用线程模式来处理需要很长时间才能返回的请求,同时保持服务器对其他请求的响应。

请注意,当您移出开发 Web 服务器并进入生产 Web 服务器时,这种方法很难正确扩展。对于基于工作人员的服务器,您必须选择固定数量的工作人员,这为您提供了可以拥有的最大并发请求数。

另一种方法是使用基于协程的服务器,例如 Flask 完全支持的 gevent。对于 gevent,只有一个工作进程,但其中有多个轻量级(或“绿色”)线程,它们协作允许彼此运行。在这种模式下让事情正常运行的关键是确保这些绿色线程不会滥用它们获得的 CPU 时间,因为一次只能运行一个。如果做得好,服务器可以比我上面描述的多工人方法更好地扩展,并且您可以轻松地以这种方式处理成百上千的客户端。

所以现在你要使用Flask-SocketIO,而这个扩展需要使用gevent。如果这个要求的原因不清楚,与 HTTP 请求不同,SocketIO 使用 WebSocket 协议,这需要长期连接。使用 gevent 和 green 线程可以拥有大量持续连接的客户端,这对于多个工作人员来说是不可能的。

问题是你的计算太长,对 gevent 类型的服务器不友好。为了使其工作,您需要确保您的计算函数经常产生,以便其他线程有机会运行并且不会饿死。例如,如果你的计算函数有一个循环,你可以这样做:

def my_long_calculation():
    while some_condition:
        # do some work here

        # let other threads run
        gevent.sleep()

sleep()函数基本上会暂停您的线程并切换到需要 CPU 的任何其他线程。最终控制权将交还给您的函数,然后它将继续进行下一次迭代。您需要确保睡眠调用不会太间隔(因为这会使应用程序的其余部分无响应)或不会太近(因为这可能会减慢您的计算速度)。

所以回答你的问题,只要你在long计算中yield正确,你不需要做任何特殊的处理并发请求,因为这是gevent的正常运行模式。

如果由于任何原因无法使用 yield 方法,那么您可能需要考虑将 CPU 密集型任务卸载到另一个进程。也许使用 Celery 将这些作为作业队列完成。

对不起,冗长的回答。希望这可以帮助!

于 2015-06-18T18:35:33.963 回答