threading =真正的烧瓶 - socketio

时间:2015-06-17 20:41:52

标签: flask flask-socketio

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

现在我开始使用Flask-SocketIO,我不确定如何做同等的事情。我明白,只要它启动其中一个计算,我就可以在python中显式生成一个单独的线程。这是唯一的方法吗?或者是否有类似于threaded = true的东西,用于烧瓶 - socketio。 (或者,更有可能的是,我只是完全糊涂了。)

感谢您的帮助。

1 个答案:

答案 0 :(得分:17)

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

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

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

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

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

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

        # let other threads run
        gevent.sleep()

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

所以要回答你的问题,只要你在长时间计算中正确屈服,你就不需要做任何特别的事情来处理并发请求,因为这是gevent的正常操作模式。

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

很抱歉这个冗长的回答。希望这有帮助!