首頁 > 後端開發 > Python教學 > 使用tornado的coroutine進行編程

使用tornado的coroutine進行編程

高洛峰
發布: 2016-10-17 14:13:53
原創
1177 人瀏覽過

在tornado3發布之後,強化了coroutine的概念,在非同步程式設計中,取代了原來的gen.engine, 變成現在的gen.coroutine。這個裝飾器本來就是為了簡化在tornado中的非同步程式設計。避免寫回呼函數, 使得開發更符合正常邏輯思考。一個簡單的例子如下:


class MaindHandler(web.RequestHandler):

   @asynchronous

   yncHTTPClient()

       resp = yield client .fetch(https://api.github.com/users")

       if resp.code == 200:

          resp = escape.json_decode p. (json.dumps(resp, indent=4, separators=(',', ':')))

       else:

           resp = {"message": "errornrite fp. json.dumps(resp, indent =4, separators={',', ':')))

       self.finish()

在yield語句之後,ioloop將會註冊該事件,等到resp 這個過程是異步的。這裡使用json.dumps,而沒有使用tornado自帶的escape.json_encode,是因為在建立REST風格的API的時候,往往會從瀏覽器存取取得JSON格式的資料。 ,在瀏覽器端顯示查看的時候會更友善。的回答escape並不打算提供全部的json功能,使用者可以自己直接使用json模組。必須使用異步的函式庫。庫可以在這裡找到。包括用的比較多的MongoDB的Driver。

在3.0版本之後,gen.coroutine模組顯得比較突出。 coroutine裝飾器可以讓原本靠回呼的非同步程式看起來像是同步程式設計。其中便是利用了Python中生成器的Send函數。在生成器中,yield關鍵字往往會與正常函數中的return相比。它可以被當成迭代器,從而使用next()傳回yield的結果。但是生成器還有另一個用法,就是使用send方法。在生成器內部可以將yield的結果賦值給一個變量,而這個值是透過外部的生成器client來send的。舉個例子:

def test_yield():

   pirnt "test yeild"

   says = (yield)

"__main__":

   client = test_yield ()

   client.next()


   client.send("hello world")

輸出結果如下:


test yeildello它的client使用send方法,原來函數繼續運作。而這裡的gen.coroutine方法就是非同步執行所需的操作,然後等待結果回傳之後,再send到原函數,原函數則會繼續執行,這樣就以同步方式寫的程式碼達到了非同步執行的效果。

Tornado非同步程式設計


使用coroutine實現函數分離的非同步程式設計。具體如下:

@gen.coroutine

def post(self):

   client = AsyncHTTPClient()

   client = AsyncHTTPClient()

  

   if resp == 200:

       body = escape.json_decode(resy.body)

          logger.error("client fetch error % d, %s" % (resp.code, resp.message))


   self.write(escape.json_encode(body))

   self.finish()


;

@gen.coroutime

def post(self):

   resp = yield GetUser()

   self.write(resp)

   self.write(resp):

   client = AsyncHTTPClient()

   resp = yield client.fetch("https://api.github.com/users")

   if resp.code == 200:p else:

       resp = {"message": "fetch client error"}

       logger.error("client fetch error %d, %s" resp. (resp)

這裡,當把非同步封裝在一個函數中的時候,並不是像普通程式那樣使用return關鍵字進行返回,gen模組提供了一個gen.Return的方法。是透過raise方法實現的。這個也是和它是使用生成器方式實現有關的。


使用coroutine跑定時任務


Tornado中有這麼一個方法:


tornado.ioloop.IOLitime)阻塞版本,它接受一個時間長度和一個函數這兩個參數。表示多少時間之後呼叫該函數。在這裡它是基於ioloop的,因此是非阻塞的。此方法在客戶端長連接以及回調函數程式設計中使用的比較多。但是用它來跑一些定時任務卻是無奈之舉。通常跑定時任務也沒必要使用到它。但是我在使用heroku的時候,發現沒有註冊信用卡的話僅僅能夠使用一個簡單Web Application的託管。不能加入定時任務來跑。於是就想出這麼一個方法。在這裡,我主要使用它隔一段時間透過Github API介面去抓取資料。大自使用方法如下:


裝飾器


 def sync_loop_call(delta=60 *​​ 1000):

.

 """

     def wrap_loop(func):

         @wraps(func)

         @gen.coroutine

.       options.logger.info("function %r start at %d" %

                                 (func.__name__, int(time.time())))         yield func(*args, **kwargs)

             except Exception, e:  r error: %s" %

                                   "function %r end at %d" %

                                 (func.__name__, int(time.time)) ))           wrap_func)

         return wrap_func

     return wrap_loop

     return wrap_loop

(delta=10 * 1000)

 def worker():

     """

     Do something

__  name__ == "__main__":

 worker()

 app.listen(options.port)

 tornado.ioloop.IOLoop.instance().start()

這樣做之後,當Web Application啟動之後,定時任務就會隨著跑起來,而且因為它是基於事件的,並且非同步執行的,所以並不會影響Web服務的正常運行,當然任務不能是阻塞的或計算密集型的。我這裡主要是抓取數據,而且用的是Tornado自帶的非同步抓取方法。


在sync_loop_call裝飾器中,我在wrap_func函數上加了@gen.coroutine裝飾器,這樣就保證只有yeild的函數執行完之後,才會執行add_timeout操作。如果沒有@gen.coroutine裝飾器。那麼不等到yeild返回,就會執行add_timeout了。

完整地例子可以參考我的Github,這個專案搭建在heroku上。用於展示Github用戶活躍度排名和用戶區域分佈。可以存取Github-Data查看。由於國內heroku被牆,需要翻牆才能進入。

總結


Tornado是一個非阻塞的web伺服器以及web框架,但是在使用的時候只有使用異步的庫才會真正發揮它異步的優勢,當然有些時候因為App本身要求並因為App本身要求不是很高,如果不是阻塞特別嚴重的話,也不會有問題。另外使用coroutine模組進行非同步程式設計的時候,當把一個功能封裝到一個函數中時,在函數運行中,即使出現錯誤,如果沒有去捕捉的話也不會拋出,這在調試上顯得非常困難。

來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板