去 async/await 之路
看到彭總寫的文章 ofollow,noindex">這破 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 中可以很方便的實現 Socket/">WebSocket Boardcast,但是他們和 async/await 其實關聯並不大。
今年 PyCon 2018 上,Django 組的 Core 來介紹說,Channel 2.0 增加了對 async/await 的支援。未來 Django 也可能會增加對應的支援。但是問題在於,一旦到了使用 async/await 的時候,目前整體的生態,依舊是讓人最為擔心的,也是最為薄弱的點 。
所以,你好 async/await,再見 async/await!