同步異步I/O客戶端python
from tornado.httpclient import HTTPClient,AsyncHTTPClient def ssync_visit(): http_client = HTTPClient() response = http_client.fetch('www.baidu.com') # 阻塞,直到網站請求完成 print(response.body) def hendle_response(response): print(response.body) def async_visit(): http_client = AsyncHTTPClient() http_client.fetch('www.baidu.com',callback=hendle_response) # 非阻塞 async_visit()
協程mysql
一、編寫協程函數git
from tornado import gen # 引入協程庫 from tornado.httpclient import AsyncHTTPClient @gen.coroutine def coroutine_visit(): http_client = AsyncHTTPClient() response = yield http_client.fetch('www.baidu.com') print(response.body)
二、調用協程函數github
因爲Tornado協程基於python的yield關鍵字實現,因此不能調用普通函數同樣調用協程函數web
協程函數可經過如下三種方式調用sql
下面是一個經過協程函數調用協程函數的例子mongodb
@gen.coroutine def outer_coroutine(): print('開始調用另外一個協程') yield coroutine_visit() print('outer_coroutine 調用結束')
outer_coroutine和coroutine_visit都是協程函數,他們之間能夠經過yield關鍵字進行調用
IOLoop 是Tornado的主事件循環對象,Tornado程序經過它監聽外部客戶端的訪問請求,並執行相應的操做,當程序還沒有進入IOLoop的runing狀態時,能夠經過run_sync()函數調用協程函數,好比:
from tornado import gen # 引入協程庫 from tornado.ioloop import IOLoop from tornado.httpclient import AsyncHTTPClient @gen.coroutine def coroutine_visit(): http_client = AsyncHTTPClient() response = yield http_client.fetch('http://www.baidu.com') print(response.body) def func_normal(): print('開始調用協程') IOLoop.current().run_sync(lambda: coroutine_visit()) print('結束協程調用') func_normal()
本例中run_sync()函數將當前函數的執行進行阻塞,直到被調用的協程執行完成數據庫
Tornado 要求協程函數在IOloop的running狀態才能被調用,只不過run_sync函數自動完成了啓動,中止IOLoop的步驟,他的實現邏輯爲:啓動IOLoop-調用被lambda封裝的協程函數-中止IOLoop編程
當tornado程序已經處於running 狀態時協程的調用以下:網絡
def func_normal(): print('開始調用協程') IOLoop.current().spawn_callback(coroutine_visit) print('結束協程調用') func_normal()
開始調用協程
結束協程調用
本例中spawn_callback函數不會等待被調用的協程執行完成,而協程函數將會由IOLoop在合適的時機進行調用,而且spawn_callback函數沒有提供電泳返回值的方法,因此hi能用該函數調用沒有返回值的協程函數
三、在協程中調用阻塞函數
在協程中直接調用阻塞函數會影響協程自己的性能,因此tornado提供了在協程中利用線程池調度阻塞函數,從而不影響協程自己繼續執行的方法,實例代碼以下:
from concurrent.futures import ThreadPoolExecutor from tornado import gen thread_pool = ThreadPoolExecutor(2) def mySleep(count): import time for i in range(count): time.sleep(1) @gen.coroutine def call_backing(): print('開始調用當前函數') yield thread_pool.submit(mySleep,10) print('結束調用') call_backing()
四、在協程中的等待多個異步調用
tornado容許在協程中用一個yield關鍵字等待多個異步調用,只需把這些調用用列表或字典的方式傳遞給yield關鍵字便可
實例以下:
from tornado import gen # 引入協程庫 from tornado.ioloop import IOLoop from tornado.httpclient import AsyncHTTPClient @gen.coroutine def coroutine_visit(): http_client = AsyncHTTPClient() list_response = yield [http_client.fetch('http://www.baidu.com'), http_client.fetch('http://www.sina.com'), http_client.fetch('http://www.163.com') ] for response in list_response: print(response.body) def func_normal(): print('開始調用協程') IOLoop.current().run_sync(lambda: coroutine_visit()) print('結束協程調用') func_normal()
字典同理,再也不演示
Tornado 網站
異步化,協程化
當大量客戶端高併發請求場景出現時,須要用到兩種方式改變同步的處理請求流程
一、異步化
from tornado import web,httpclient import tornado class MainHandler(tornado.web.RequestHandler): @tornado.web.asynchronous def get(self): http = tornado.httpclient.AsyncHTTPClient() http.fetch('http://www.baidu.com',callback=self.on_response) def on_response(self,response): if response.error: raise tornado.web.HTTPError(500) self.write(response.body) self.finish()
用@tornado.web.asynchronous 定義HTTP訪問處理函數get(),當get函數返回時對該訪問的請求還沒有完成,因此tornado沒法發送響應給客戶端,只有隨後的回掉函數中的finsh函數被調用時,tornado才知道本次處理已經完成,能夠發送響應給客戶端
異步雖然提升了併發能力,可是編程方法更繁瑣
二、協程化
tornado 協程結合同步異步的優勢,
import tornado.web import tornado.httpclient class MainHandler(tornado.web.RequestHandler): @tornado.gen.coroutine def get(self): http = tornado.httpclient.AsyncHTTPClient() response = yield http.fetch('http://www.baidu.com') self.write(response.body)
用tornado.gen.coroutine裝飾MainHandler的get(),post()函數
使用異步對象處理耗時操做,好比AsyncHTTPClient
調用yield關鍵字獲取異步對象的處理結果
下各項同步(阻塞)的,若是在 tornado 中按照以前的方式只用它們,就是把 tornado 的非阻塞、異步優點削減了。
解決方法
tornado.gen.sleep()
或者tornado.ioloop.IOLoop.instance().add_timeout
greenlet-tornado 能夠實現用專門的庫來實現tornado 的異步而不使用裝飾器的異步