asyncio 系列堪称甜美的毒药。
我司目前在一些场合的使用 asyncio 已经一年多了,包括使用 Sanic , aiohttp 等目前在这个圈子内相对比较重要的东西。
体验就是,利好明显,但是你永远不知道什么时候会给你一些新的惊喜。
我分为几个方面来说吧。
第一个,asyncio 本身的 API 现在并不成熟
举一个大家用的比较多的一个函数 gather18.5.3. Tasks and coroutinesdocs.python.org
我们来看下它的实现
gather 函数为了兼容的考虑,允许用户传入多个 future 对象,或者是 coroutine。 但是个人觉得,这样兼容考虑,不同类型的参数传入,其最终表现行为应该一致。但是你看,在这里,传入 coroutine 和 future 的行为最终并不一致。
这就有时会留一下一些隐患。
第二,asyncio 在一些使用场景下会产生跟文档不一致的行为
我司有个场景是在 Gunicorn + Gevent + Flask 的代码中使用 aiohttp 的 client (这里也有深坑,等下说)来加速代码。
肯定,我们要自己维护事件循环了。对于默认的自带的事件循环而言,我们可以直接在默认情况下,使用 get_event_loop函数获取事件循环,如果没有则会自动创建一个。
但是如果经过 Gevent Patch Threading 后,这一行为会发生改变,如果没有则会抛出异常。
所以在一些特定场景下,asyncio 本身的一致性并不能得到确保(当然这个不是 asyncio 的锅,但却也是坑)
第三,asyncio 周边奇坑无比
首先说,aiohttp 吧,这个算是 asyncio 周边中最杀手锏的应用了,毕竟是 async 的世界里作为 requests 的唯一可用替代品,但是,前几天,我们就被坑哭了。
aiohttp client 在巅峰时期,直接在服务器上干出了 17K 的 CLOSE_WAIT窝草,当时都吓尿了好嘛。然后,查啊,查啊,定位出原因了。
aiohttp client 是用他们自己实现的一个叫做 TCPConnector 的东西来实现发起,关闭,管理连接池的。但是,但是,但是,如果里面一个参数不设置的话,那么针对一些 https 连接,将存在连接泄漏的风险。
详见:Client Referenceaiohttp.readthedocs.io
这么重要的参数居然不是一个默认参数????而且参数描述这里也有坑
请注意,enable_cleanup_closed在 __init__ 里面默认值是一个 Bool ,但是在文档中说这里却是一个 tuple ???????
???????
????
?
黑人问号脸啊兄弟!
好了,吐槽完 aiohttp ,来说说 Sanic ,Sanic 我之前专门写过一篇文章来吐槽Manjusaka:Sanic 的若干吐槽zhuanlan.zhihu.com
除了这里的一些问题,还比如 @方正 老哥说的一堆 issue 没修的问题,比如 ujson ,当时我也是受害者= =提了 PR 后没跟了,不知道现在怎么样了(毕竟官方不太知道他们怎么思考的)
所以你们看,现在 async 社区里最具有代表性的两个库都这德行,其余的库质量,也非常堪忧啊(更别提有基于堪称毒瘤 pymysql 的 aiomysql 的存在)
结论
async 这一套,堪称甜美的毒药,如果有需求,会给你带来较大的利好。但是同时也会时不时的给你一个超大的 surprise 。
现在这一套里真正里可以生产使用还很遥远,如果真正需求到了, async 这一套能较好的改善你们目前的窘境的话。请务必做好随时踩坑,看源码查坑的准备。
嗯= =反正我觉得 async 药丸。。这大概可能是和我智商太低了相关,不,一定是这样的,感觉这辈子都学不会了(逃