首页 > 代码库 > Python开发【Tornado】:异步Web服务
Python开发【Tornado】:异步Web服务
异步Web服务
前言:
到目前为止,我们已经看到了许多使Tornado成为一个Web应用强有力框架的功能。它的简单性、易用性和便捷性使其有足够的理由成为许多Web项目的不错的选择。然而,Tornado受到最多关注的功能是其异步取得和提供内容的能力,它有着很好的理由:它使得处理非阻塞请求更容易,最终导致更高效的处理以及更好的可扩展性。在本章中,我们将看到Tornado异步请求的基础,以及一些推送技术,这种技术可以使你使用更少的资源来提供更多的请求以编写更简单的Web应用。
大部分Web应用(包括我们之前的例子)都是阻塞性质的,也就是说当一个请求被处理时,这个进程就会被挂起直至请求完成。在大多数情况下,Tornado处理的Web请求完成得足够快使得这个问题并不需要被关注。然而,对于那些需要一些时间来完成的操作(像大数据库的请求或外部API),这意味着应用程序被有效的锁定直至处理结束,很明显这在可扩展性上出现了问题。
不过,Tornado给了我们更好的方法来处理这种情况。应用程序在等待第一个处理完成的过程中,让I/O循环打开以便服务于其他客户端,直到处理完成时启动一个请求并给予反馈,而不再是等待请求完成的过程中挂起进程。
我们将展示这个应用的三个不同版本:首先,是一个使用同步HTTP请求的版本,然后是一个使用带有回调函数的Tornado异步HTTP客户端版本。最后,我们将展示如何使用Tornado 2.1版本新增的gen模块来使异步HTTP请求更加清晰和易实现。
1、同步
记住我们在顶部导入了Tornado的httpclient模块:我们将使用这个模块的HTTPClient类来执行HTTP请求。之后,我们将使用这个模块的AsyncHTTPClient
import tornado.httpserver import tornado.ioloop import tornado.options import tornado.web import tornado.httpclient from tornado.options import define, options define("port", default=8000, help="run on the given port", type=int) class IndexHandler(tornado.web.RequestHandler): def get(self): client = tornado.httpclient.HTTPClient() response = client.fetch("http://www.cnblogs.com/lianzhilei") # 访问url,并返回response self.write(""" <div style="text-align: center"> <div style="font-size: 72px">Time Cost</div> <div style="font-size: 72px">%s</div> </div>"""%(response.request_time) ) # 访问开销 if __name__ == "__main__": tornado.options.parse_command_line() app = tornado.web.Application(handlers=[(r"/", IndexHandler)]) http_server = tornado.httpserver.HTTPServer(app) http_server.listen(options.port) tornado.ioloop.IOLoop.instance().start()
这个程序的结构现在对你而言应该已经很熟悉了:我们有一个RequestHandler类和一个处理到应用根路径请求的IndexHandler。在IndexHandler的get方法中,实例化了一个Tornado的HTTPClient类,然后调用结果对象的fetch方法,fetch方法会返回一个HTTPResponse对象,fetch方法返回的HTTPResponse对象允许你访问HTTP响应的任何部分
到目前为止,我们已经编写了 一个请求API并向浏览器返回结果的简单Tornado应用。尽管应用程序本身响应相当快,但是向API发送请求到获得返回的搜索数据之间有相当大的滞后。在同步(到目前为止,我们假定为单线程)应用,这意味着同时只能提供一个请求。所以,如果你的应用涉及一个2秒的API请求,你将每间隔一秒才能提供(最多!)一个请求。这并不是你所称的高可扩展性应用,即便扩展到多线程和/或多服务器 。
为了更具体的看出这个问题,我们对刚编写的例子进行基准测试。你可以使用任何基准测试工具来验证这个应用的性能,不过在这个例子中我们使用优秀的Siege utility工具进行测试。它可以这样使用:
[root@localhost siege-4.0.2]# siege http://192.168.1.210:8000/ -c100 -t3s ** SIEGE 4.0.2 ** Preparing 100 concurrent users for battle. The server is now under siege... HTTP/1.1 200 0.09 secs: 208 bytes ==> GET / HTTP/1.1 200 0.19 secs: 208 bytes ==> GET / HTTP/1.1 200 0.27 secs: 208 bytes ==> GET / HTTP/1.1 200 0.34 secs: 208 bytes ==> GET / HTTP/1.1 200 0.44 secs: 208 bytes ==> GET / HTTP/1.1 200 0.54 secs: 208 bytes ==> GET / HTTP/1.1 200 0.62 secs: 208 bytes ==> GET / HTTP/1.1 200 0.72 secs: 207 bytes ==> GET / HTTP/1.1 200 0.79 secs: 208 bytes ==> GET / HTTP/1.1 200 0.87 secs: 208 bytes ==> GET / HTTP/1.1 200 0.95 secs: 208 bytes ==> GET / HTTP/1.1 200 1.02 secs: 207 bytes ==> GET / HTTP/1.1 200 1.10 secs: 208 bytes ==> GET / HTTP/1.1 200 1.17 secs: 207 bytes ==> GET / HTTP/1.1 200 1.29 secs: 207 bytes ==> GET / HTTP/1.1 200 1.36 secs: 207 bytes ==> GET / HTTP/1.1 200 1.44 secs: 208 bytes ==> GET / HTTP/1.1 200 1.56 secs: 207 bytes ==> GET / HTTP/1.1 200 1.64 secs: 208 bytes ==> GET / HTTP/1.1 200 1.79 secs: 207 bytes ==> GET / HTTP/1.1 200 1.92 secs: 207 bytes ==> GET / HTTP/1.1 200 2.08 secs: 207 bytes ==> GET / HTTP/1.1 200 2.23 secs: 207 bytes ==> GET / HTTP/1.1 200 2.34 secs: 207 bytes ==> GET / HTTP/1.1 200 2.42 secs: 208 bytes ==> GET / HTTP/1.1 200 2.52 secs: 208 bytes ==> GET / HTTP/1.1 200 2.67 secs: 207 bytes ==> GET / HTTP/1.1 200 2.86 secs: 207 bytes ==> GET / HTTP/1.1 200 2.94 secs: 208 bytes ==> GET / Lifting the server siege... Transactions: 29 hits Availability: 100.00 % Elapsed time: 2.99 secs Data transferred: 0.01 MB Response time: 1.39 secs Transaction rate: 9.70 trans/sec Throughput: 0.00 MB/sec Concurrency: 13.43 Successful transactions: 29 Failed transactions: 0 Longest transaction: 2.94 Shortest transaction: 0.00
Python开发【Tornado】:异步Web服务