跳至主要內容
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?

4563博客

全新的繁體中文 WordPress 網站
  • 首頁
  • 2021 年,用 Python 部署异步网络服务的最佳实践是什么?
未分類
22 1 月 2021

2021 年,用 Python 部署异步网络服务的最佳实践是什么?

2021 年,用 Python 部署异步网络服务的最佳实践是什么?

資深大佬 : LeeReamond 11

先说几句题外话,前两天看见一个帖子,提到异步框架的,里面很多人推荐 fastapi 。我个人说来很惭愧,学习 python 的 web 框架,入门是 flask,异步是 aiohttp,一直与 django 和 fastapi 这类主流的、用的人比较多的框架无缘。

所以这次也是想学习一下 fastapi,看看相对于一直使用的原生 aiohttp 有什么区别。

根据我个人理解,异步从 python3.4 版本提出以来,现在已经不是像 3.6 版本时候那样大家都不会用,现在用异步的人应该越来越多了。目前主流不管是公司内部服务,还是生产级服务,如果上 python 的话,如果要用异步的话,应该是很多人使用 django 的 asgi,一些人使用 fastapi,几乎没有人使用 aiohttp 这样。tornado 我不太了解,因为我最初接触异步是 3.5 时代,彼时 tornado 的异步是用猴子补丁实现的,所以一直也没做接触,不知道现在是怎么样了。

使用异步框架当然第一步还是看性能,我去 fastapi 官网看了一下教学,教学写的很友好,直接就推荐了 fastapi+uvicorn 的部署方案。

官网上写了 fastapi 是最快的框架之一,我们都知道 python 异步刚出的时候有很多昙花一现的框架,比如 Vibora,japronto 这些,性能做的都非常夸张,单例可以达到十万 qps,实际上是用 py 胶水封装了一下 c 框架而已,性能高也很正常,可惜这些开发社区做了 demo 出来以后都不怎么活跃了,bug 不修,没法投入生产级。

倒是 aiohttp 这个一上来看起来就很弱的,表现也不怎么亮眼的,一直更新到现在,投入生产级也完全没问题了,说句题外话,我个人使用起来主要优势就是用的熟,想实现什么效果几乎以前都做过,很快都能找到解决方案,所以学习 fastapi 对我来说倒是要考虑学习成本问题。

=====================================================================

说回正题,关于压力测试,我在虚拟机上用 wrk 进行压测,测试结果 fastapi 其实表现并不好,想问一下各位 fastapi 用的比较熟练的大佬,是我部署错误,还是它的性能表现就是这样的。

另外想问一下切换到生产级服务的话,fastapi 这条路线目前坑度怎么样,比如 web 部署里的一些常用插件,cors,basic auth,jwt 等等,还有中间件开发,支持 ws 协议等等,目前这些坑都踩的差不多了吗?这个框架从名字来看就可以看出是为 api 设计的,如果用来一体化部署 spa 之类的,有额外的坑吗?

谢谢大家

=====================================================================

附一些压测数据

#笔记本随手测一下,虚拟机给了 8 核心,所以用 16 线程 500 并发进行测试  # fastapi + uvicorn 部署,单进程 Running 20s test @ http://127.0.0.1:8000   16 threads and 500 connections   Thread Stats   Avg      Stdev     Max   +/- Stdev     Latency    20.88ms    3.51ms  50.90ms   95.22%     Req/Sec     0.96k    66.29     1.21k    83.54%   95737 requests in 20.01s, 13.70MB read Requests/sec:   4784.35 Transfer/sec:    700.83KB  # aiohttp + 自带服务部署,单进程 Running 20s test @ http://127.0.0.1:8000   16 threads and 500 connections   Thread Stats   Avg      Stdev     Max   +/- Stdev     Latency    12.90ms    2.94ms  58.01ms   94.81%     Req/Sec     1.57k   156.69     1.82k    80.90%   156446 requests in 20.05s, 24.32MB read Requests/sec:   7803.19 Transfer/sec:      1.21MB  # aiohttp + gunicorn(uvloop 模式) ,单进程 Running 20s test @ http://127.0.0.1:8000   16 threads and 500 connections   Thread Stats   Avg      Stdev     Max   +/- Stdev     Latency     6.12ms    1.08ms  23.27ms   90.35%     Req/Sec     3.28k   216.93     5.05k    72.67%   327908 requests in 20.10s, 50.97MB read Requests/sec:  16315.63 Transfer/sec:      2.54MB 

一般来说这些框架都会自带一个 web 服务,可以用来做测试什么的,一般因为稳定性,性能等等原因,都不会用在生产环境部署。但是根据这个单线程测试,fastapi 实际上单进程只有 aiohttp 的 60%,如果用 gunicorn 部署的话(值得吐槽的是 gunicorn 似乎本身也是 python 中不算快的部署方式。。),fastapi+uvicorn 的组合只有 aiohttp+gunicorn 25%左右的性能

然后是多进程 prefork 测试,采用 8 线程部署服务。

# fastapi 8 线程 Running 20s test @ http://127.0.0.1:8000   16 threads and 500 connections   Thread Stats   Avg      Stdev     Max   +/- Stdev     Latency     6.53ms    1.94ms  33.58ms   79.44%     Req/Sec     3.09k   476.62     4.02k    60.60%   307858 requests in 20.07s, 28.48MB read Requests/sec:  15341.09 Transfer/sec:      1.42MB  # fastapi 增大 echo 报文长度 Running 20s test @ http://127.0.0.1:8000   16 threads and 500 connections   Thread Stats   Avg      Stdev     Max   +/- Stdev     Latency     9.56ms    6.59ms  51.53ms   61.89%     Req/Sec     2.18k     1.18k    6.39k    87.10%   217184 requests in 20.05s, 24.85MB read Requests/sec:  10834.00 Transfer/sec:      1.24MB  # aiohttp 8 线程 Running 20s test @ http://127.0.0.1:8000   16 threads and 500 connections   Thread Stats   Avg      Stdev     Max   +/- Stdev     Latency     2.77ms    1.34ms  16.60ms   65.92%     Req/Sec     7.30k     2.28k   19.59k    73.53%   726936 requests in 20.10s, 123.40MB read Requests/sec:  36170.63 Transfer/sec:      6.14MB 

可以看到同样地,fastapi 性能只有 aiohttp 的三成左右。另外值得吐槽的是使用长报文测试下,fastapi 的 echo 性能衰退又有点厉害啊,直接掉三成。

大佬有話說 (23)

  • 資深大佬 : TypeError

    https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=fortune&l=zijzen-1r

    看这个 benchmark,FastAPI 比 aiohttp 高不少,
    我感觉 Python 异步框架没必要追求极致性能,我倾向顺手和生态成熟的,
    目前线上生产环境 Tornado 、aiohttp 都有,更喜欢 Tornado,
    接下来可能迁到 Go 了,性能方面还是静态语言优势大

  • 主 資深大佬 : LeeReamond

    @TypeError 不知道它这个怎么测试的,aiohttp 倒是和我测的差不多,即使 prefork 也有一个上限,大概四万 qps 左右,并不能 8 线程就 8 倍可用性。fastapi 我已经用 uvicorn 部署了,理论上已经是最佳配置了,不知道它这个怎么搞出来五万 qps 的。

    go 和 py 这种属于无谓争论,用 py 上生产肯定还是看重开发效率,py 内部换个框架都要问一问有没有坑的问题,换 go 就更苦了,go 目前这个生态。。。

  • 資深大佬 : Carry0317

    请教 我想提供一个 gpu 的服务 用哪种方式性能最高

  • 資深大佬 : so1n

    可能有些配置没写对吧.
    fastapi 的底子就是 starlette, 但为了做类型转化和参数校验,性能会比 starlette 略差一点. 然后 aio 的库相比其他同类的库都不太好(可能是出现太早的原因)

  • 主 資深大佬 : LeeReamond

    @Carry0317 这篇帖子跟 GPU 没什么关系吧。你的意思是想提供一个高可用的 gpu 服务接口?

  • 主 資深大佬 : LeeReamond

    @so1n 就是很简单的按照 quick start 定义了一个异步函数,绑定到’/’,返回一个 echo,没有其他任何东西。部署方面,8 个 fork,关闭 log 。uvicorn main:app –worker 8 –log-level error,不知道有哪里配置还能提高性能的。

    aio 库性能差我觉得应该没有这个说法。python 的异步从一开始就没有什么黑魔法,当初 dableaz 在 pycon 花半小时就实现一个功能完整的 eventloop,可以完全替代原生进行 basic tcp socket programming 的。所以 eventloop 相同的情况下,实现方面完全可追溯,封装程度其实区别不大,影响也不大,理论上无所谓 aio 库慢与否,实践当中我也从没听说过有人说 aiolibs 里面的东西比同类慢。

  • 資深大佬 : ManjusakaL

    直接 Gevent 不香么…
    asyncio 那么多

  • 主 資深大佬 : LeeReamond

    @ManjusakaL 我觉得你对屎可能有些误解。即使在 python3.5 时代,原生异步也并不屎,这种用户态完全可控、可预测的状态显然是更优的设计。gevent 无法做到以上任何一项,用户用脚投票也说明了这点。

  • 資深大佬 : ManjusakaL

    @LeeReamond 很抱歉,可能用

  • 資深大佬 : ManjusakaL

    @LeeReamond BTW 你对于 asyncio 的可预测存在误解
    event loop 是无法真正意义上做到“可预测”的
    此处“可预测”指 A task 执行完后能预测下一个 task 是 B 还是 C
    无论是 asyncio 还是 Gevent 我们都只能做到一个基础的保证,即正常情况下,我能在一个切出点后能切入执行后续代码
    但是在生产环境中,配合 Python 的 GIL,能做到这点也是奢望
    随手举例时间,我们在请求 www.v2ex.com 的时候,会通过 gethostbyname ( Linux 下,参见 https://man7.org/linux/man-pages/man3/gethostbyname.3.html )来做 DNS 解析,而这个函数是不可调度函数,所以那么一旦 DNS 解析出现问题,那么可能炸整个 event loop 导致所有 task 不可调度. 而这样复杂的可能阻塞整个 event loop 调度情况还有很多,此处不一一列举. 诚然我们可以通过很多额外的手段来尽可能规避这种情况. 但是就其本身而言,无论 asyncio 还是 Gevent,其所要面对的问题都是一样的

  • 主 資深大佬 : LeeReamond

    @ManjusakaL 认真看完了,大佬确实经验丰富。我因为学习异步的时候已经出现原生异步了,所以对猴子补丁天生有不信任,承认错误。我们在简单的生产环境(非内部管理平台)中使用原生异步体验良好,可能有些过于信任。

    搜了一下你说的 BPO-30698,ssl 链接泄露应该如何理解,似乎不是一个导致明文泄露的恶性 bug,而是导致内存不能回收的问题,不知是否理解正确。在 17/18 年左右倒是听说过有人 aiohttp 框架出现 ssl 内存泄露,我从未遇到过类似问题,以为在新版中已经修好了。看了这个 issue,不理解如何复现。

    你在帖子中提到的同步异步混合,以及生态不支持 c 插件等问题,我个人理解这两个目前已经不是问题,我的理解中异步代码中首先不应存在同步内容,我从未体验过同步异步同时维护的复杂度。另外生态方面主要是接入后端,python 本身的阻塞实现倒是能用附带线程池的方式梭掉,顺带 cython 还能解决掉 gil,而后端方面,mysql 和 redis,oracle 也有异步连接方式,我使用 aiolibs 的库体验良好,可能是接入服务数少,我个人而言这方面没什么不满。

    另外大佬这么推崇猴子补丁,有没有 gevent 系列比较合适的入门文章,我想完整评估一下 gevent 相对于原生异步方案的性能和稳定性

  • 資深大佬 : wdhwg001

    fastapi 不是要用 gunicorn 套 uvicorn 吗? techempower 是开源的,可以去看他们的部署和代码。

  • 資深大佬 : wdhwg001

    另外 techempower 里的 fastapi 代码是有轻微作弊的,主要是 ujson,不过也不严重,你甚至可以用 orjson 跑的更高一点。

    我的观点是常量级差距都不用太在意的,fastapi 还有完善的 openapi 支持什么的,那些要更吸引人一点。

  • 資深大佬 : wdhwg001

    另外 asyncio 应该是大势所趋了,生态在逐步完善,但是距离 wsgi 时代还是有差距的,然而依旧是好兆头。
    其实单说 fastapi 也是问题多多的,缺少 session 支持是一个,不完全遵守 asgi 是一个,中间件还有闭包引用不可靠的问题,自带路由是遍历而不是树优化也是一个问题。但即使如此,fastapi 的设计也依然是比 flask 好一些的。
    而且其实更大的坑是 orm,gino 和 tortoise 都有各自的坑,django 的 async orm 还在难产,我这边项目用的 tortoise,设计上基本就是抄 django 了,没什么创新点。

  • 主 資深大佬 : LeeReamond

    @wdhwg001 gunicorn 套 uvicorn 怎么实现,感觉这两个不能互相套啊

    上说的 ssl 泄露的 issue 我看倒是确实没人理。印象中 17 年左右 stackoverflow 的 asyncio 区还是极端冷清的,不知道现在怎么样。我个人体验来讲,倒是 3.5 时代感觉原生异步的学习过程很底层,从生成器概念一步步概念学上来,最近两年倒是完全感觉在使用高级 api,完全没有底层的感觉了,基本和写同步代码没有任何区别,只是外面要套一层扳机而已。

  • 主 資深大佬 : LeeReamond

    @wdhwg001 orm 方面我是完全不做任何希望了,我觉得以 python 社区的生产能力 orm 大概是要永远难产下去了。我个人使用体验上倒是没体会到 orm 对开发速度有多大帮助,都是直接操作数据库,所以倒是感觉不很有所谓

  • 資深大佬 : spcharc

    aiohttp 库 contributor 路过,很惭愧只做了一点微小的工作(大概+366 −48 这样子)
    我感觉 aiohttp,一般不是配合 aiodns 来用吗?
    官方都提供了 pip install aiohttp[speedups]这种安装方式来捆绑销售 aiodns,不就是因为一旦 dns 服务不稳定,就可能阻塞整个 loop 嘛?
    另外官方也推荐搭配使用 uvloop,比 asyncio 自带 loop 速度快,也没有上面提到的 https 泄露之类的问题
    而且 loop (不管是 python 官方的还是第三方的)都提供了 run_in_executor 的吧,有可能长时间阻塞的函数都应该用这个来运行来避免 loop 阻塞啊

  • 主 資深大佬 : LeeReamond

    @spcharc 我印象中确实是有见过生产级部署以后出现莫名泄露的问题的帖子,大概几年前。我自己没遇到过。另外我对上说的猴子布丁原理上性能高于 libuv 仿品很好奇

  • 資深大佬 : so1n

    @LeeReamond 我不是说性能,比如 aiohttp 的 client 就一堆隐藏坑,aioredis 停止更新,集群到现在都没支持等等

  • 資深大佬 : Carry0317

    @LeeReamond 是的 高可用的 gpu 服务接口

  • 資深大佬 : wdhwg001

    @LeeReamond Django 系的 orm 大多数时候够用了,可以不用去手写 crud 的,除非你用到了很复杂的用法或者特殊的函数。totorise 作为 django orm 的残品其实也勉强够用,觉得遗憾的地方只是没有新东西,没有 flask 到 fastapi 的那种换代感。
    另外你倒是看一下源码啊。

  • 資深大佬 : wdhwg001

    @LeeReamond 另外这个泄露的问题如果用 uvloop 是不会出现的,同时只能通过重写 asyncio 解决,而重写现在还在进行中。

  • 主 資深大佬 : LeeReamond

    @so1n redis 集群不是自身特性,为什么需要客户端支持呢
    @Carry0317 目前按照我这个帖子,纯 py 的方法就是 uvloop+aiohttp+gunicorn 。上有老哥提到 fastapi 部署是 uvicorn+gunicorn 但他没说怎么做,我暂不理解。另外如果你的应用层封装简单,也可以试试 vibora,japronto 这类 c 库,应该可以获得最大转发效率,比较基础的使用上应该也没什么坑,大概吧。不过你这个 gpu 业务本身需要 python+http 转发本身就挺奇怪的

文章導覽

上一篇文章
下一篇文章

AD

其他操作

  • 登入
  • 訂閱網站內容的資訊提供
  • 訂閱留言的資訊提供
  • WordPress.org 台灣繁體中文

51la

4563博客

全新的繁體中文 WordPress 網站
返回頂端
本站採用 WordPress 建置 | 佈景主題採用 GretaThemes 所設計的 Memory
4563博客
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?
在這裡新增小工具