原文地址:https://github.com/xtg20121013/blog_xtg
閱讀知識點準備:
tornadis用法
concurrent.futures.ThreadPoolExecutor任務用法
orm的用法-sqlalchemy的用法
apscheduler的TornadoScheduler的用法
main.py裏面依次做了三件事:
tornado的application的加入
redis訂閱加入
TornadoScheduler的加入
這三個異步任務都加入到tornado的loop循環中了
TornadoScheduler 負責刷新所有緩存,從數據庫重建緩存並通知其他節點,用於定時任務校準緩存,通過yield tornadis.call來完成任務的;返回方式是raise tornado.gen.Return(reply)
查詢數據的時候還涉及了其他方式的使用異步blog_info = yield thread_do(BlogInfoService.get_blog_info, db),這個和路由裏面的handler的處理方法是一樣的(tornado的application),例如pager = yield self.async_do(ArticleService.page_articles, self.db, pager, article_search_params)
thread_do和async_do實際上都是concurrent.futures.ThreadPoolExecutor.submit,
這個線程池做的操作基本上是sqlalchemy的操作,比如add_comment這種純存保存到數據庫的操作:
comment_to_add = Comment(content=comment['content'], author_name=comment['author_name'], author_email=comment['author_email'], article_id=article_id, comment_type=comment['comment_type'], rank=comment['rank'], floor=floor, reply_to_id=comment['reply_to_id'], reply_to_floor=comment['reply_to_floor']) db_session.add(comment_to_add) db_session.commit()
,或者query = db_session.query(Article)這種從數據庫裏面查詢數據返回對象的操作,這兩個都是同步操作
但是這個submit和coroutine交叉使用就讓人迷糊了,怎麼做到的呢。
class ThreadPoolExecutor(_base.Executor): def submit(self, fn, *args, **kwargs): with self._shutdown_lock: if self._shutdown: raise RuntimeError('cannot schedule new futures after shutdown') f = _base.Future() w = _WorkItem(f, fn, args, kwargs) self._work_queue.put(w) self._adjust_thread_count() return f
猜測submit操作會交出原來的協程的使用權,等到子線程把這種耗時的操作完成之後,來通知原來線程的事件循環,原來協程完成了,可以往下運行了.
繼續分析:submit及時返回一個future可以讓事件循環知道,但是查詢狀態是未完成的,就先調度其他的協程;future變量可以跨線程的,所以一旦子線程完成是可以修改結果和狀態(done),事件循環運行到這個協程再通過get_result傳遞實際的值
這裏面有個重要的概念,result = yield future,左邊並不是直接得到的是future,而是先不斷向上傳出生成器,直到傳到事件循環,事件循環檢查future對象狀態,通過send future的結果即f.result()不斷向下直到等號右邊變量:
loop——yield——>f3——yield——>f2——yield——>f1——yield——>future(yield 只負責傳出生成器,再次調用生成器f3.send(None),f1.send(something),something才被傳遞到等號左邊的result