tornado 异步非阻塞 实验
来源:互联网 发布:淘宝售前和售后的区别 编辑:程序博客网 时间:2024/06/08 17:17
参考
tornado官方文档
Tornado中异步非阻塞地使用MySQL
Tornado-MySQL 0.5.1
使用tornado让你的请求异步非阻塞
根据这篇文章来做的,我的实验失败了:Tornado 异步笔记:异步任务
前言
目前用到的都是同步请求。当遇到查数据库等耗时操作的时候,请求会一直被阻塞。
实验
我们使用sql语句cmds = "select sleep(1)"
来模拟长时间的数据库查询。
使用siege做压测。
另外,还可以用ab压测: ab -c 10 -n 10 http://10.9.40.173:8888/asynctask?t=1
同步 V.S. Tornado-MySQL异步
只贴出handler部分的代码。
其中IndexHandler为同步请求,路由为‘/’
AsynHandler为异步请求,路由为‘/asyn’
cmds = "select sleep(1)"MYSQL_POOL = pools.Pool(dict(host=host, port=port, user=usr, passwd=passwd, db=db) , max_idle_connections=5, max_open_connections=10)class IndexHandler(tornado.web.RequestHandler): def get(self): conn = MySQLdb.connect(host, usr, passwd, db, port) cur = conn.cursor() cur.execute(cmds) rep = cur.fetchall() self.write({'res': rep})class AsynHandler(tornado.web.RequestHandler): @tornado.web.asynchronous @tornado.gen.coroutine def get(self): cur = yield MYSQL_POOL.execute(cmds) self.write({'res': cur.fetchall()}) self.finish()
在同步的情况下,浏览器发起请求后会被阻塞,一直等到上一条查询完了,才能接受下一个请求。假设数据库请求耗时1s,同时发起调用的话,第1个请求会在大概1s后得到响应,第2个请求会在大概2s后得到响应。。。
在异步的情况下,则不需要等待数据库查询结束,可以在得到结果前接收下一个请求。假设数据库请求耗时1s,同时发起调用的话,理想情况下所有请求都会在1s后得到响应。
我们参照官网使用工具siege进行测试
模拟10个用户同时发出1次请求:
同步请求的测试结果是:
siege 10.9.40.173:8888 -c10 -r1** SIEGE 3.0.8** Preparing 10 concurrent users for battle.The server is now under siege.. done.Transactions: 10 hitsAvailability: 100.00 %Elapsed time: 10.06 secsData transferred: 0.00 MBResponse time: 5.12 secsTransaction rate: 0.99 trans/secThroughput: 0.00 MB/secConcurrency: 5.09Successful transactions: 10Failed transactions: 0Longest transaction: 10.05Shortest transaction: 1.00
总共用时10.06 secs,平均响应时间是5.12 secs,跟我们预想的一样,最快的响应是1 secs(第一个请求),最慢的响应是10 secs(最后一个请求)。平均耗时大概是(1+2+3+…+10)/10=5.5 secs
而异步请求的测试结果是:
siege 10.9.40.173:8888/asyn -c10 -r1** SIEGE 3.0.8** Preparing 10 concurrent users for battle.The server is now under siege.. done.Transactions: 10 hitsAvailability: 100.00 %Elapsed time: 2.04 secsData transferred: 0.00 MBResponse time: 1.03 secsTransaction rate: 4.90 trans/secThroughput: 0.00 MB/secConcurrency: 5.04Successful transactions: 10Failed transactions: 0Longest transaction: 1.06Shortest transaction: 1.01
总共用时2.04 secs,平均响应时间是1.03 secs,达到了异步请求的理论速度。最快的响应是1.01 secs,最慢的响应也才1.06 secs。
不需要返回结果的异步请求
class AsynPingNoResHandler(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.baidu.co$ # do something others self.finish('It works') @tornado.gen.coroutine def ping(self, url): os.system("ping -c 2 {}".format(url)) return 'after'
需要返回结果的异步请求
再来一个利用tornado.gen.coroutine
的例子,我参考伯乐在线的文章做的实验,但是没有得到文章的结果:
class AsyncTaskHandler(tornado.web.RequestHandler): @tornado.web.asynchronous @tornado.gen.coroutine def get(self): t = self.get_argument('t') response = yield tornado.gen.Task(self.query, t) #print 'response', response self.write({'res': response}) self.finish() @tornado.gen.coroutine def query(self, t): conn = MySQLdb.connect(host, usr, passwd, db, port) cur = conn.cursor() cur.execute(cmdsn % t) rep = cur.fetchall() return rep
这个代码的测试结果是:
siege 10.9.40.173:8888/asynctask?t=1 -c10 -r1** SIEGE 3.0.8** Preparing 10 concurrent users for battle.The server is now under siege.. done.Transactions: 10 hitsAvailability: 100.00 %Elapsed time: 10.07 secsData transferred: 0.00 MBResponse time: 9.45 secsTransaction rate: 0.99 trans/secThroughput: 0.00 MB/secConcurrency: 9.39Successful transactions: 10Failed transactions: 0Longest transaction: 10.06Shortest transaction: 9.05
不知道为啥,这种代码的表现是,10个并发全部查询完才有一次性返回。可以说这个代码根本没有达到异步的效果。甚至比同步还差!
同步是1s的时候返回第1个请求,2s的时候返回第2个请求…
这种方式,在最后第10s的时候一次性返回10个请求,每个请求的响应时间都是10s。求高手指点一下这是为啥。
伯乐在线的文章的作者解释:
使用 yield task的方式,准确来说是协程。只是在网络IO中tornado的主线程不至于被 handler里的代码 block,单个handler还是被网络耗时任务所block的。如果并发连接比较小的情况下,很有可能其性能还不如同步的写法好。
具体以你自己的测试结果为准吧,在底层上,tornado这样的做法在IO层面上是异步的。
个人认为,使用协程跳到另一个函数去执行ping/sql操作,就阻塞在另一个函数里了,只是没有阻塞在handler的get中,对于整个程序而言,那还是被block了。。
fetch
如果tornado.gen.coroutine
如此不堪的话,那它还有什么用?参照官网的例子,官网例子是用来fetch的。
同步fetch
class OfficialSynHandler(tornado.web.RequestHandler): def get(self): print '# in get', datetime.datetime.now() client = tornado.httpclient.HTTPClient() response = client.fetch("http://www.csdn.net/") print 'response', response self.write('ok')
siege测试结果
siege 10.9.40.173:8888/officialsyn -c10 -r1** SIEGE 3.0.8** Preparing 10 concurrent users for battle.The server is now under siege.. done.Transactions: 10 hitsAvailability: 100.00 %Elapsed time: 2.05 secsData transferred: 0.00 MBResponse time: 0.51 secsTransaction rate: 4.88 trans/secThroughput: 0.00 MB/secConcurrency: 2.47Successful transactions: 10Failed transactions: 0Longest transaction: 1.03Shortest transaction: 0.15
ab压测
Document Path: /officialsyn/Document Length: 306 bytesConcurrency Level: 10Time taken for tests: 0.016 secondsComplete requests: 10Failed requests: 0Non-2xx responses: 10Total transferred: 4450 bytesHTML transferred: 3060 bytesRequests per second: 626.25 [#/sec] (mean)Time per request: 15.968 [ms] (mean)Time per request: 1.597 [ms] (mean, across all concurrent requests)Transfer rate: 272.15 [Kbytes/sec] received
异步fetch
class OfficialAsynHandler(tornado.web.RequestHandler): @tornado.gen.coroutine def get(self): print '# in get', datetime.datetime.now() client = tornado.httpclient.AsyncHTTPClient() response = yield tornado.gen.Task(client.fetch, "www.csdn.net") print 'response', response self.write('ok')
siege测试结果
siege 10.9.40.173:8888/officialasyn -c10 -r1** SIEGE 3.0.8** Preparing 10 concurrent users for battle.The server is now under siege.. done.Transactions: 10 hitsAvailability: 100.00 %Elapsed time: 1.03 secsData transferred: 0.00 MBResponse time: 0.01 secsTransaction rate: 9.71 trans/secThroughput: 0.00 MB/secConcurrency: 0.06Successful transactions: 10Failed transactions: 0Longest transaction: 0.02Shortest transaction: 0.00
ab压测
Document Path: /officialasyn/Document Length: 306 bytesConcurrency Level: 10Time taken for tests: 0.016 secondsComplete requests: 10Failed requests: 0Non-2xx responses: 10Total transferred: 4450 bytesHTML transferred: 3060 bytesRequests per second: 640.78 [#/sec] (mean)Time per request: 15.606 [ms] (mean)Time per request: 1.561 [ms] (mean, across all concurrent requests)Transfer rate: 278.46 [Kbytes/sec] received
可见,使用异步fetch的话每个请求都响应得比较快,平均响应时间短。
个人总结
如果要在tornado中实现非阻塞的mysql查询,不要自己折腾去写yield/coroutine/gen这些异步模块,直接用Tornado-MySQL就好了。还有可以考虑用celery等任务队列。
如果是不需要返回结果的异步操作,通过tornado的IO循环,把可以把耗时的任务放到后台异步计算,请求可以接着做别的计算。
如果是fetch的话使用yield异步模块可以有较好的效果,减少了平均响应时间,因此可以用来做REST API的通信。
- tornado 异步非阻塞 实验
- tornado异步请求非阻塞
- Tornado 异步非阻塞 分析
- 真正的 Tornado 异步非阻塞
- 使用tornado的异步非阻塞
- tornado使用celery的请求异步非阻塞
- 使用tornado让你的请求异步非阻塞
- Python Tornado 异步和非阻塞I/O
- 使用tornado如何实现请求异步非阻塞?
- 利用tornado使请求实现异步非阻塞
- 简单介绍tornado是如何实现异步非阻塞的
- 使用tornado让你的请求异步非阻塞
- 使用tornado让你的请求异步非阻塞
- Tornado官方文档(二)【概述,异步和非阻塞IO】
- tornado 数据库非阻塞方法
- Tornado用户指引(一)-----------异步和非阻塞I/O
- 阻塞与非阻塞 异步非阻塞
- 同步阻塞, 同步非阻塞,异步阻塞,异步非阻塞
- Spring源码分析【8】-MyBatis注解方法不能重载
- iOS-分段控件控制页面切换
- System.out.println() 和 log4j 的 Logger 循环输出100万次。
- Excel Sheet Column Number
- PHP开发面试题目总结
- tornado 异步非阻塞 实验
- 金蝶打印时选择使用套打秒退
- linux菜鸟学习----MySQL导入.sql文件及常用命令
- DIV+CSS右列宽度自适应布局的不同实现方法
- 关于Keil编译后Code RO RW ZI !
- nginx配置优化+负载均衡+动静分离详解
- ShellExecute与ShellExecuteEx的用法
- PL/SQL存储函数
- Matrix factorization