走向 async/await 之路#
看到彭總寫的文章這破 Python,感慨頗多,我也來灌水吧。
首先,我司算是在國內比較敢於嘗試新東西的公司吧,最直接的體現就在於我們會及時跟進社區相關基礎服務的迭代,並且敢於去嘗試新的東西。嗯,從去年 6 月到現在,我司在線上推行了很長一段時間的 async/await ,並且引入新的注入 Sanic 這樣全新的框架,但是不得不說,我們現在要對 async/await 暫時地說再見了。
我們為什麼選用 async/await ?#
和我們組具體場景有關,我們組有相當一部分場景,是根據不同的 URL 去不同的子服務請求數據,組合之後,再進行下一步的統一處理。那麼這個時候,傳統的同步的方式在數據源越來越雜的情況下就顯得很無奈。
我們當時有這樣幾個選擇:
-
維護進程 / 線程池,利用通用的進程 / 線程來處理請求
-
利用 Gevent 這樣第三方的 coroutine+EventLoop 方案
-
使用 async/await + asyncio 這一套
首先,1 被我們排除了,原因很簡單,太重了。2 最開始也被我們暫時性地排除,當時我們對於 monkey-patch 這樣看起來不太清真的方式心存畏懼
於是我們就很歡喜鼓舞地選擇了 3,利用 async/await + asyncio 這一套方案
事實上最開始的效果還是很美妙的。然而,在後面會發現這一套操作其實是在吃屎 QwQ
走向 async/await#
我們為什麼放棄 async/await?#
其實幾個老生重談的問題
1. 代碼層面的傳染性#
Python 官方的 coroutine 實現,其實是基於 yield/yield from 這一套生成器的魔改的,那麼這也意味著你需要入口處開始,往下逐漸地遵循 async/await 的方式進行使用。那麼在同一個項目裡,充斥著同步 / 異步的代碼,相信我,維護起來,某種意義上來說算是一種災難。
2. 生態與兼容性#
async/await 目前的兼容性真的讓人很頭大,目前 async/await 的生效範圍僅限於 Pure Python Code。這裡有個問題,我們很多在項目中使用的諸如 mysqlclient 這樣的 C Extension ,async/await 並不能覆蓋。
同時,目前而言,async/await 的周邊真的堪稱一個非常非常大的問題,可以說處於一個 Bug 隨處見,發現沒人修的狀態。比如 aiohttp 的對於 https 鏈接所存在的鏈接洩漏的問題。再比如 Sanic 的一團亂麻的設計結構。
我們在為生產項目調研一門新的技術的時候,我們往往會著重去考察一個新的東西,它對於現有的技術是否能覆蓋我們的服務,它的周邊是否能滿足我們日常的需求?目前而言 async/await 周邊一套並不能滿足
3. 性能問題#
目前而言,PEP 3156 提出的 asyncio 是 async/await 官方推薦的事件循環的搭配。但是目前而言官方的實現欠缺很多,比如之前 aiohttp 針對於 https 的鏈接洩漏的問題,底層其實可以追溯至 asyncio 的 SSL 相關的實現。所以我們在使用的時候,往往會選用第三方的 loop 進行搭配。而目前而言第三方的 Loop 而言目前主流的實現方式均是基於 libuv/libev 進行魔改。而這樣一來,其性能和 Gevent 不相上下,甚至更低(畢竟 Greenlet 避免了維護 PyFrameObject 的開銷)
所以,為了我們的頭髮著想,目前我們將選擇逐漸地將 async/await 從我們的線上代碼中退役,最遲今年年底前,完成我們的去 async/await 的操作。
我們替代品是什麼?#
目前而言,我們準備使用 Gevent 作為替代品(嗯,真香)
原因很簡單:
-
目前發展成熟,無明顯大的 Bug
-
周邊發展成熟,對於 Pure Python Code,可以 Monkey-Patch 一把梭遷移存量代碼,對於 C Extension 有豆瓣內部生產驗證過的 Greenify 來作為解決方案
-
底層的 Greenlet 提供了對應的 API ,在必要的時候可以方便地對協程的切換做上下文的 trace。
關於 async/await 其他一些想說的東西#
首先而言,async/await 是個好東西,但是現在不實用。這一點其實要看社區去進一步摸索相關的使用方法。
說到這裡,很多人又想問我,你對於 ASGI 和 Django Channel 這樣的東西怎麼看?
首先我們要明確一點 ASGI 其實並不是為了 async/await 所設計,其最初的設計思路,是為了解決 PEP333/PEP3333 WSGI 協議在面對越來越複雜的網絡協議模型力不從心的問題。而 Django Channel 也是為了解決這個問題,從而對於 ASGI 進行實現的產物(最開始是解決 Websocket?)。這一套的確解決了很多問題,比如 Django Channel 2.0 中可以很方便地實現 WebSocket Boardcast,但是他們和 async/await 其實關聯並不大。
今年 PyCon 2018 上,Django 組的 Core 來介紹說,Channel 2.0 增加了對 async/await 的支持。未來 Django 也可能會增加對應的支持。但是問題在於,一旦到了使用 async/await 的時候,目前整體的生態,依舊是讓人最為擔心的,也是最為薄弱的點 。
所以,你好 async/await,再見 async/await!