tornado 第一篇

 一:異步和非阻塞IOhtml

  實時的web特性一般須要每一個用戶一個大部分時間,在傳統的同步web服務器中,這意味着須要給每一個用戶分配一個專用的線程,這樣的開銷是十分巨大python

  tornado使用啦一種單線程事件循環的方式,這意味着全部的應用代碼都應該是異步和非阻塞的,由於在同一時刻只有一個操做是有效的web

  1,阻塞服務器

    一個函數在等到返回值等都是阻塞的,網絡

    一個函數能夠在某些方面阻塞而在其餘方面不阻塞,舉例說明。tornado,httpclient在默認設置下講阻塞與DNS解析,可是在其餘網絡請求時不會阻塞(爲了減輕這種影響,能夠用ThreadeResolver 或經過正確配置libcurl使用tornado.curl_htpclient),在Tornado的上下文中咱們一般討論網絡I/O上下文阻塞,雖然各類阻塞已經被最小化啦curl

  2,異步異步

    一個異步函數在在它結束前就已經返回啦,並且一般會在程序中觸發一些動做而後在後頭執行一些任務,這裏有幾種類型的異步接口async

    1,回調函數ide

    2,返回一個佔位符(Future,Promise,Deferred)函數

    3,傳送一個隊列

    4,回調註冊

    一個簡單的同步異步函數

    

from tornado.httpclient import HTTPClient
from tornado.concurrent import Future
def synchronous_fetch(url):
    http_client = HTTPClient()
    def handle_response(response):
        callback(response.body)
    http_client.fetch(url,callbace=handle_response)

    在一次經過Future替代回調函數

    

def async_fetch_future(url):
    http_client=HTTPClient()
    my_future=Future()
    fetch_future=http_client.fetch(url)
    fetch_future.add_done_callback(
        lambda f:my_future.set_result(f.result)
    )

    return my_future

    原始的Future是很複雜的,可是Futures是tornado中推薦使用的一種作法,由於他有兩個優點

    錯誤處理是經過Future.result 函數能夠簡單拋出一個異常,還有就是攜程兼容比較好

   

rom tornado import gen

@gen.coroutine
def fetch_coroutine(url):
    http_client = AsyncHTTPClient()
    response = yield http_client.fetch(url)
    raise gen.Return(response.body)

    語句 raise gen.Return(response.body) 在 Python 2 中是人爲設定的, 由於生成器不容許又返回值. 爲了克服這個問題, Tornado 協程拋出了一個叫作 Return 的特殊異常. 協程將會像返回一個值同樣處理這個異常.在 Python 3.3+ 中, return response.body 將會達到一樣的效果.

 

二:協程

  tornado中推薦協程來編寫異步代碼,協程使用python中關鍵件yield替換鏈式回調實現掛起和繼續協程的執行(像在gevent中使用輕量級線程合做的方法有時也稱做爲協程,可是在Tornado中全部的協程使用異步函數來實現明確的上下文切換)

  看下協程的代碼

from tornado import gen
from tornado import HTTPClient
def fetch_coroutie(url):
    http_client=AsyncHTTPClient() 
  respone
=yield http_client.fetch(url)

  # raise gen.Return(respone.body)
  
  return respone.body

 

   python3.5 async和awiat 

   python3.5 引入啦async和await 從tornado4.3開始,在協程基礎上你可使用這個來代替yield,簡單的經過使用async def foo()來替代 @gen.coroutine 裝飾器,用await來代替yield,能夠看下下面的例子

  

async def fetch_coroutine(url):
    http_client = AsyncHTTPClient()
    response = await http_client.fetch(url)
    return response.body

    一個含有yield的函數時是一個生成器,全部的生成器都是異步的,調用它時將會返回一個對象而不是將函數運行完成,@gen.coroutine修飾器經過yeild表達式經過產生一個Future對象和生成器進行通訊

    能夠看下一個協程裝飾器內部循環的簡單版本

    

def run(self):
    future=self.gen.send(self.next)
    
    def callback(f):
        self.next=f.result()
        self.run()
    future.add_done_callback(callback)

    

有時你並不想等待一個協程的返回值. 在這種狀況下咱們推薦你使用 IOLoop.spawn_callback, 這意味着 IOLoop 負責調用. 若是它失敗了, IOLoop 會在日誌中記錄調用棧:  同時注意spawn_callback調用的函數,也必須是協程函數

# The IOLoop will catch the exception and print a stack trace in # the logs. Note that this doesn't look like a normal call, since # we pass the function object to be called by the IOLoop.
IOLoop.current().spawn_callback(divide, 1, 0)


  協程模式
   1,結合callbacks
      
爲了使用回調來代替Future與異步代碼進行交互,將這個調用裝在task中,這將會在你生成的Future對象中添加一個回調參數
      
@gen.coroutine
def call_task():

    yield gen.Task(some_function, other_args)
    #把yeild換成gen_Task  

     2,調用阻塞函數

      在協程中調用阻塞函數的最簡單方法是使用ThreadPoolExecutor  這將返回與協程兼容的Futures

      

thread_pool = ThreadPoolExecutor(4)

@gen.coroutine
def call_blocking():
    yield thread_pool.submit(blocking_func, args)

      3,並行

        協程裝飾器識別列表或者字典中的Futures,而且並行等待這些Fuures

    

@gen.coroutine
def parallel_fetch(url1,url2):
    resp1,resp2 = yield [http_client.fetch(url1),
                         http_client.fetch(url2)]



@gen.coroutine
def parallel_fetch_dict(urls):
    responses = yield {url: http_client.fetch(url)
                        for url in urls}

      4,交叉存取技術(項目通常用到比較多)

        有時保存一個Future比馬上yield它更有用,你能夠等待它以前執行其餘操做

        

def get(self):
    fetch_future = self.fetch_next_chunk()
    while True:
        chunk = yield fetch_future
        if chunk is None:break
        self.write(chunk)
        fetch_future= self.fetch_next_chunk()
        yield self.flush()

      5,循環

        由於python沒法使用forwhile循環yield迭代器,而且捕獲yield的返回結果,相反,你須要將循環和訪問結果區分開來,

      

import motor
db = motor.MotorClient().test

@gen.coroutine
def loop_example(collection):
    cursor = db.collection.find()
    while (yield cursor.fetch_next):
        doc = cursor.next_object()

      6,在後臺運行

@gen.coroutine
def minute_loop():
    while True:
        yield do_something()
        yield gen.sleep(60)

# Coroutines that loop forever are generally started with
# spawn_callback().
IOLoop.current().spawn_callback(minute_loop)

 

 

更過內容能夠參考:http://tornado-zh-cn.readthedocs.io/zh_CN/latest/guide/coroutines.html#python-3-5-async-await

相關文章
相關標籤/搜索