6

我已经利用 tornado 开发了一个用 python 编写的相当广泛的 http 服务器。如果不进行任何特殊设置,服务器会阻塞请求并且一次只能处理一个。这些请求基本上是访问数据(mysql/redis)并以 json 格式打印出来。在最坏的情况下,这些请求可能需要一秒钟以上的时间。问题是一个请求进来需要很长时间(3 秒),然后一个简单的请求立即进来,需要 5 毫秒来处理。好吧,因为第一个请求需要 3 秒,所以第二个请求在第一个请求完成之前不会开始。所以第二个请求需要超过 3 秒的时间来处理。

我怎样才能使这种情况变得更好?无论其他请求如何,我都需要开始执行第二个简单请求。我是 python 新手,对 apache/php 更有经验,没有两个单独的请求相互阻塞的概念。我已经研究了 mod_python 来模拟 php 示例,但这似乎也被阻止了。我可以更改我的龙卷风服务器以获得我想要的功能吗?在我读到的所有地方,它都说龙卷风非常擅长处理多个同时请求。

这是我正在使用的演示代码。我有一个 sleep 命令,我用它来测试并发是否有效。sleep 是测试并发性的公平方法吗?

import tornado.httpserver
import tornado.ioloop
import tornado.web
import tornado.gen
import time

class MainHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    @tornado.gen.engine

    def handlePing1(self):
        time.sleep(4)#simulating an expensive mysql call
        self.write("response to browser ....")
        self.finish()

    def get(self):
        start = time.time()
        self.handlePing1()
        #response = yield gen.Task(handlePing1)#i see tutorials around that suggest using something like this ....

        print "done with request ...", self.request.path, round((time.time()-start),3)



application = tornado.web.Application([
        (r"/.*", MainHandler),
])

if __name__ == "__main__":
    http_server = tornado.httpserver.HTTPServer(application)
    port=8833;
    http_server.listen(port)
    print "listening on "+str(port);
    tornado.ioloop.IOLoop.instance().start()

谢谢你的帮助!

4

2 回答 2

4

编辑:记住 Redis 也是单线程的,所以即使你有并发请求,你的瓶颈也会是 Redis。您将无法处理更多请求,因为 Redis 将无法处理它们。

Tornado 是单线程、基于事件循环的服务器。

从文档中:

通过使用非阻塞网络 I/O,Tornado 可以扩展到数万个打开的连接,使其成为长轮询、WebSocket 和其他需要与每个用户建立长期连接的应用程序的理想选择。

龙卷风中的并发是通过异步回调实现的。这个想法是在主事件循环(单线程)中尽可能少地做,以避免阻塞和通过回调延迟 i/o 操作。

如果使用异步操作对您不起作用(例如:没有用于 MySQL 或 Redis 的异步驱动程序),您处理更多并发请求的唯一方法是运行多个进程。

最简单的方法是使用 HAProxy 或 Nginx 等反向代理来处理你的龙卷风进程。tornado 文档推荐使用 Nginx:http ://www.tornadoweb.org/en/stable/overview.html#running-tornado-in-production

您基本上在不同的端口上运行您的应用程序的多个版本。前任:

python app.py --port=8000
python app.py --port=8001
python app.py --port=8002
python app.py --port=8003 

一个好的经验法则是为服务器上的每个核心运行 1 个进程。

Nginx 将负责平衡每个传入请求到不同的后端。因此,如果其中一个请求很慢(约 3 秒),那么您有 n-1 个其他进程正在侦听传入请求。有可能——而且很有可能——所有进程都忙于处理一个缓慢的请求,在这种情况下,当任何进程空闲时,请求将被排队并处理,例如。完成处理请求。

我强烈建议您在尝试 HAProxy 之前先从 Nginx 开始,因为后者更高级一些,因此正确设置会更复杂(需要调整很多开关)。

希望这可以帮助。关键要点:Tornado 非常适合异步 I/O,而对于 CPU 繁重的工作负载则不太适合。

于 2013-09-03T20:59:56.607 回答
0

我有同样的问题,但没有龙卷风,没有 mysql。您是否与所有服务器共享一个数据库连接?

我创建了一个multiprocessing.Pool. 每个都有自己的init函数提供的数据库连接。我将慢速代码包装在函数中并将map其放入池中。所以我没有共享变量和连接。

睡眠不会阻塞其他线程,但数据库事务可能会阻塞线程。

您需要在代码顶部设置池。

def spawn_pool(fishes=None):
    global pool
    from multiprocessing import Pool
    def init():
        from storage import db #private connections
        db.connect() #connections stored in db-framework and will be global in each process
    pool = Pool(processes=fishes,initializer=init)

if __name__ == "__main__":
    spawn_pool(8)


from storage import db #shared connection for quick-type requests.

#code here

if __name__ == "__main__":
    start_server()

许多并发的快速请求可能会减慢一个大请求,但这种并发只会放在数据库服务器上。

于 2013-08-13T23:03:35.387 回答