Manjusaka

Manjusaka

前往 async/await 之路

走向 async/await 之路#

看到彭總寫的文章這破 Python,感慨頗多,我也來灌水吧。

首先,我司算是在國內比較敢於嘗試新東西的公司吧,最直接的體現就在於我們會及時跟進社區相關基礎服務的迭代,並且敢於去嘗試新的東西。嗯,從去年 6 月到現在,我司在線上推行了很長一段時間的 async/await ,並且引入新的注入 Sanic 這樣全新的框架,但是不得不說,我們現在要對 async/await 暫時地說再見了。

我們為什麼選用 async/await ?#

和我們組具體場景有關,我們組有相當一部分場景,是根據不同的 URL 去不同的子服務請求數據,組合之後,再進行下一步的統一處理。那麼這個時候,傳統的同步的方式在數據源越來越雜的情況下就顯得很無奈。

我們當時有這樣幾個選擇:

  1. 維護進程 / 線程池,利用通用的進程 / 線程來處理請求

  2. 利用 Gevent 這樣第三方的 coroutine+EventLoop 方案

  3. 使用 async/await + asyncio 這一套

首先,1 被我們排除了,原因很簡單,太重了。2 最開始也被我們暫時性地排除,當時我們對於 monkey-patch 這樣看起來不太清真的方式心存畏懼

588d9201f5b14873b0504e3a6ab7b402

於是我們就很歡喜鼓舞地選擇了 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 作為替代品(嗯,真香)

原因很簡單:

  1. 目前發展成熟,無明顯大的 Bug

  2. 周邊發展成熟,對於 Pure Python Code,可以 Monkey-Patch 一把梭遷移存量代碼,對於 C Extension 有豆瓣內部生產驗證過的 Greenify 來作為解決方案

  3. 底層的 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!

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。