异步请求处理程序在工作池中处理阻塞任务的性能

3
这个脚本的性能如何?以下是它的复制链接:http://tornadogists.org/2185380/
from time import sleep
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
from tornado.web import Application, asynchronous, RequestHandler
from multiprocessing.pool import ThreadPool

_workers = ThreadPool(10)

def run_background(func, callback, args=(), kwds={}):
    def _callback(result):
        IOLoop.instance().add_callback(lambda: callback(result))
    _workers.apply_async(func, args, kwds, _callback)

# blocking task like querying to MySQL
def blocking_task(n):
    sleep(n)
    return n

class Handler(RequestHandler):
    @asynchronous
    def get(self):
        run_background(blocking_task, self.on_complete, (10,))

    def on_complete(self, res):
        self.write("Test {0}<br/>".format(res))
        self.finish()

HTTPServer(Application([("/", Handler)],debug=True)).listen(8888)
IOLoop.instance().start()
  1. 我的应用程序将有超过1,000个请求/秒
  2. 每个请求将持续2-30秒,平均约6秒
    • 简单地平均使用sleep(6)
  3. 通过使用像redis BLPOPQueue.get_nowait()这样的方法来阻塞IO

你的任务是否需要大量CPU资源?如果是这样,你可能需要将其分布在多个CPU上。不过,要实现这一点,你需要使用另一种没有GIL(全局解释器锁)的Python实现,比如Jython或IronPython。 - Burhan Khalid
1个回答

2

总体模式很好,但要注意由于全局解释器锁(GIL)的存在,你的线程池只能使用单个CPU,需要使用多个进程才能充分利用可用的硬件资源。

仔细观察数据后发现,如果你的请求真的平均每个都要用6秒,那么10个线程太少了。每秒会有6000秒的工作量,因此你需要在所有进程中至少拥有6000个线程(假设6秒只是在等待外部事件并且Python进程中的CPU成本可以忽略不计)。我不确定现代系统可以处理多少线程,但是6000个Python线程听起来不是一个好主意。如果你的确有每个请求需要6秒阻塞(而且每秒有数千个请求),那么将这些阻塞函数转换为异步函数是值得的。


抱歉回复晚了,没有在帖子中看到。我已经更新了我的代码以反映您提出的问题。仍在进行中...但我的技术正在不断改进。感谢您的建议,Ben! - Steve Peak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接