Python web框架 Tornado異步非阻塞
異步非阻塞
阻塞式:(適用於所有框架,Django,Flask,Tornado,Bottle)
一個請求到來未處理完成,后續一直等待
解決方案:多線程,多進程
異步非阻塞(存在IO請求): Tornado(單進程+單線程)
使用異步非阻塞,需要遵循Tornado框架內部規則,gen
多個連接請求,連接給服務端,如果是有異步非阻塞的話,服務端會接收所有的請求交由后台處理,等待其他鏈接的同時,原先連接不斷開,直至返回后台處理完成的結果!
外部請求,連接服務端 或在select中創建Future對象,然后服務端再把請求交給業務處理平台,此時select監聽的列表中又會生成一個socket對象,當業務平台對請求處理完成之后就會把信息返回到服務端的select監聽列表中,同時對這個Future對象賦值,用於標記服務端是否要給客戶端返回請求信息。
執行流程,本質上都是返回一個future對象,如果對這個對象被set_result了就返回值,否則就是夯住,一直保持連接,不終止請求。
1、基本使用
裝飾器 + Future 從而實現Tornado的異步非阻塞
class
AsyncHandler(tornado.web.RequestHandler):
@gen
.coroutine
def
get(
self
):
future
=
Future()
future.add_done_callback(
self
.doing)
yield
future
# 或
# tornado.ioloop.IOLoop.current().add_future(future,self.doing)
# yield future
def
doing(
self
,
*
args,
*
*
kwargs):
self
.write(
'async'
)
self
.finish()
當發送GET請求時,由於方法被@gen.coroutine裝飾且yield 一個 Future對象,那么Tornado會等待,等待用戶向future對象中放置數據或者發送信號,如果獲取到數據或信號之后,就開始執行doing方法。
異步非阻塞體現在當在Tornaod等待用戶向future對象中放置數據時,還可以處理其他請求。
注意:在等待用戶向future對象中放置數據或信號時,此連接是不斷開的。
2、同步阻塞和異步非阻塞對比
同步阻塞
class SyncHandler(tornado.web.RequestHandler): def get(self): self.doing() self.write('sync') def doing(self): time.sleep(10)
異步非阻塞
class AsyncHandler(tornado.web.RequestHandler): @gen.coroutine def get(self): future = Future() tornado.ioloop.IOLoop.current().add_timeout(time.time() + 5, self.doing) yield future def doing(self, *args, **kwargs): self.write('async') self.finish()
3、httpclient類庫
Tornado提供了httpclient類庫用於發送Http請求,其配合Tornado的異步非阻塞使用。
import
tornado.web
from
tornado
import
gen
from
tornado
import
httpclient
# 方式一:
class
AsyncHandler(tornado.web.RequestHandler):
@gen
.coroutine
def
get(
self
,
*
args,
*
*
kwargs):
print
(
'進入'
)
http
=
httpclient.AsyncHTTPClient()
data
=
yield
http.fetch(
"http://www.google.com"
)
print
(
'完事'
,data)
self
.finish(
'6666'
)
# 方式二:
# class AsyncHandler(tornado.web.RequestHandler):
# @gen.coroutine
# def get(self):
# print('進入')
# http = httpclient.AsyncHTTPClient()
# yield http.fetch("http://www.google.com", self.done)
#
# def done(self, response):
# print('完事')
# self.finish('666')
application
=
tornado.web.Application([
(r
"/async"
, AsyncHandler),
])
if
__name__
=
=
"__main__"
:
application.listen(
8888
)
tornado.ioloop.IOLoop.instance().start()