框架的异步任务与AsyncHTTPClient"/>
Python3学习(五十九):Python的Tornado框架的异步任务与AsyncHTTPClient
转载自.html
高性能服务器Tornado
Python的web框架名目繁多,各有千秋。正如光荣属于希腊,伟大属于罗马。Python的优雅结合WSGI的设计,让web框架接口实现千秋一统。WSGI 把应用(Application)和服务器(Server)结合起来。Django 和 Flask 都可以结合 gunicon 搭建部署应用。
与 django 和 flask 不一样,tornado 既可以是 wsgi 应用,也可以是 wsgi 服务。当然,选择tornado更多的考量源于其单进程单线程异步IO的网络模式。高性能往往吸引人,可是有不少朋友使用之后会提出疑问,tornado号称高性能,实际使用的时候却怎么感受不到呢?
实际上,高性能源于Tornado基于Epoll(unix为kqueue)的异步网络IO。因为tornado的单线程机制,一不小心就容易写出阻塞服务(block)的代码。不但没有性能提高,反而会让性能急剧下降。因此,探索tornado的异步使用方式很有必要。
Tornado 异步使用方式
简而言之,Tornado的异步包括两个方面,异步服务端和异步客户端。无论服务端和客户端,具体的异步模型又可以分为回调(callback)和协程(coroutine)。具体应用场景,也没有很明确的界限。往往一个请求服务里还包含对别的服务的客户端异步请求。
服务端异步方式
服务端异步,可以理解为一个tornado请求之内,需要做一个耗时的任务。直接写在业务逻辑里可能会block整个服务。因此可以把这个任务放到异步处理,实现异步的方式就有两种,一种是yield挂起函数,另外一种就是使用类线程池的方式。请看一个同步例子:
1 2 3 4 5 6 | class SyncHandler(tornado.web.RequestHandler): def get( self , * args, * * kwargs): # 耗时的代码 os.system( "ping -c 2 www.google" ) self .finish( 'It works' ) |
使用ab测试一下:
1 | ab -c 5 -n 5 http: //127 .0.0.1:5000 /sync |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | Server Software: TornadoServer/4.3 Server Hostname: 127.0.0.1 Server Port: 5000 Document Path: /sync Document Length: 5 bytes Concurrency Level: 5 Time taken for tests: 5.076 seconds Complete requests: 5 Failed requests: 0 Total transferred: 985 bytes HTML transferred: 25 bytes Requests per second: 0.99 [#/sec] (mean) Time per request: 5076.015 [ms] (mean) Time per request: 1015.203 [ms] (mean, across all concurrent requests) Transfer rate: 0.19 [Kbytes/sec] received |
qps 仅有可怜的 0.99,姑且当成每秒处理一个请求吧。
下面祭出异步大法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class AsyncHandler(tornado.web.RequestHandler): @tornado .web.asynchronous @tornado .gen.coroutine def get( self , * args, * * kwargs): tornado.ioloop.IOLoop.instance().add_timeout( 1 , callback = functools.partial( self .ping, 'www.google' )) # do something others self .finish( 'It works' ) @tornado .gen.coroutine def ping( self , url): os.system( "ping -c 2 {}" . format (url)) return 'after' |
尽管在执行异步任务的时候选择了timeout 1秒,主线程的返回还是很快的。ab压测如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 | Document Path: /async Document Length: 5 bytes Concurrency Level: 5 Time taken for tests: 0.009 seconds Complete requests: 5 Failed requests: 0 Total transferred: 985 bytes HTML transferred: 25 bytes Requests per second: 556.92 [#/sec] (mean) Time per request: 8.978 [ms] (mean) Time per request: 1.796 [ms] (mean, across all concurrent requests) Transfer rate: 107.14 [Kbytes/sec] received |
上述的使用方式,通过tornado的IO循环,把可以把耗时的任务放到后台异步计算,请求可以接着做别的计算。可是,经常有一些耗时的任务完成之后,我们需要其计算的结果。此时这种方式就不行了。车道山前必有路,只需要切换一异步方式即可。下面使用协程来改写:
1 2 3 4 5 6 7 8 9 10 11 12 13 | class AsyncTaskHandler(tornado.web.RequestHandler): @tornado .web.asynchronous @tornado .gen.coroutine def get( self , * args, * * kwargs): # yield 结果 response = yield <
|
发布评论