Flask 已死,FastAPI 永生

好险,刚刚要写一个新项目,执行了 pip install flask。等得无聊所以打开了知乎,突然看到首页推荐的这篇《Flask 已死,FastAPI 是未来》。看到一半吓得我赶紧按下 Ctrl-C,然后重新执行了 pip install fastapi。好快!不愧是 FastAPI,连安装都这么快。

Flask!Flask!你怎么了?Flask~Flask~你不能死呀!我跟你相依为命、同甘共苦了这么多年,何况我的书还没有写完,想不到今天白发人送黑发人……

咳咳,说回正题,2021 年我写过一篇《请不要把 Flask 和 FastAPI 放到一起比较》。不是同一个等级的框架拿到一起比较没有意义。这篇《Flask 已死,FastAPI 是未来》从一开始的标题就是错的。

除了比较上的问题,上一篇文章里最后提的问题现在有答案了吗?

各种对比文章贴出来的 hello world benchmark 对于开发生产应用来说有多大的意义?这里的 benchmark 背后有没有任何的 hacky?异步是否等同于高性能还是要看情况?框架本身的性能在一个请求处理流程中占多大的影响?asyncio 的生态怎么样?

同时我也指出了一些 FastAPI 的问题——现在 FastAPI 营销至上的项目运作方式改变了吗?

从长远看这些大都是一些临时问题,而且 FastAPI 作者已经开始全职开发开源项目,这些问题在未来应该都会慢慢得到改善。指出这些是希望更多的人可以客观看待 FastAPI,吹捧并不能让一个东西变得更好,参与开发、介绍用法和回答社区提问是比盲目吹捧更有意义的事情。我当然期待 FastAPI 能够越来越好,也期待看到有更多优秀的 Python 框架出现,但我不喜欢过度炒作、盲目的吹捧和错误的对比。

如果一个人即没有深入了解 Flask,也对 FastAPI 的整体状况一知半解,然后突然跳出来写了一篇《Flask 已死,FastAPI 是未来》,他的目的是什么?炒作啊,朋友。标题党也就罢了,到处都是逻辑漏洞和事实错误这也行?

精彩片段赏析

前两天在 Twitter 上对这篇文章发了一点想法。大概是我的语言能力退化,导致很多人没看懂我的反讽和调侃……所以还是好好说话,摘评几条原文的内容:

其实我这篇文章,也可以叫做「Django和Flask之死」,但是在框架应用场景角度来说Flask和FastAPI对比更合适,而Django直接和FastAPI相比有一些差别。在一些商业场景(例如CMS)Django依然是首选,而Flask(甚至FastAPI)看起来更像个「玩具」,所以Django短时间不会被取代(Flask这些年不是也是没取代的了嘛)。
Django REST Framework(DRF)
Django主旨是为了在后端生成 HTML,而不是创建现代前端(如 React、Vue.js )或与其通信的其他系统使用的 API。所以FastAPI其实和Django REST Framework直接对标,它们主要场景都是构建 Web API,但是名字上也可以看出来,DRF还是依托于Django框架,所以缺点一样。

既然作者能看出来 Django 和 FastAPI 有差别,也了解「FastAPI其实和Django REST Framework直接对标」,那么就看不出来 Flask 和 FastAPI 在框架应用场景角度有差别?所以为什么不用 APIFlask 这种和 FastAPI 对标的框架,而是挑了 Flask?

Flask本来的优点是灵活性和极简主义,但这意味着很多很多组件需要自己造,这个要不然需要公司够大专门有开发者开发和维护,要不然得个人能力极强,否则插件很难达到生产级别,这就造成Flask的第三方插件质量层次不齐,且无法保证长期维护。就像我上面说的那些库,现在来看,其中已经很多不再维护了。

这点蛮中肯,Flask 扩展的质量和维护状态确实参差不齐,这也是为什么我要发起濒危 Flask 扩展拯救计划并创建这个 Flask 扩展状态监控项目。那请问 FastAPI 呢?首先 FastAPI 是否有这么多「FastAPI 扩展」可用?其次 FastAPI 的第三方插件就不会出现这些问题?说到底这是所有开源项目和社区生态都会遇到的问题。

所以即便是今天,你想用Flask构建一个API服务,还是要东拼西凑各个组件,其中某些组件有些没及时更新的地方要自己动手解决,这对于老手来说还好,对于新人来说很痛苦,尤其是你想要应用现在最新的实践方案和理念,只能望而兴叹。

首先作者了解 Flask 和 FastAPI 相比,后者的主要场景是「主要场景都是构建 Web API」,接着又说 Flask 生态构建 API 需要东拼西凑。听起来不太对劲?

另外 Flask 生态中类似 FastAPI 这种提供 API 整体解决方案的框架都被作者忽略了?随便列几个:

东拼西凑就一定是缺点?不熟悉这些工具的人选择打包方案,熟悉的人选择自己组装,这有什么问题吗?

这个时候Flask并不想接受改变,社区迟迟的不加入aio的支持,另外Flask原作者也去写rust了,把项目交给了2个维护者(现在只剩一个人了)。

到这里文章就开始有些不对劲了,喜剧/戏剧效果逐渐增加……Flask 开始被拟人化了。Flask 是谁?是谁要求改变?Flask 说了啥表示不想改变?

Flask 作者把项目交给了 2 个维护者?现在只剩一个人了?!Flask 真的好惨……可是这些事情都是什么时候发生的?虽然我最近两年开始工作已经没怎么给 Flask 提交代码,不知道哪天被踢出群聊。但是至少我从 18 年到现在一直是 Flask 的维护者,这些事情我怎么一点都不知道?

在Flask的那个时代,代码执行是单线程、同步的,这意味着要依次处理请求,前一个请求完成前,其他请求消耗在等待 I/O 操作上。而asyncio是最好的解决方案,它让I/O成为异步操作,无需等待任务完成即可获取任务结果并继续处理其他任务请求。

好奇 Flask 的那个时代是哪个时代呀?听起来像是妈妈在讲小时候的故事呢。Flask 2.0 已经添加了有限的 async 支持Quart (Flask 的 ASGI 实现)已经合并到 pallets 组织,作者正在推动 ASGI 支持的 Flask。这些都是这个时代的 Flask 在发生的事情哦。

而FastAPI天生支持并发和异步代码,代码写对了就可以在效率上达到极致,所以它被认为是目前最快的Python框架,效率和NodeJS或Go接近。当速度和性能至关重要时,FastAPI 是最佳选择。

嗯,这个我无从反驳。你说快就是快,宇宙最快 Web 框架。

另外由于集成了Pydantic,所以非常容易在项目中添加ORM(如SQLAlchemy),从请求中获得的对象可以直接传递到数据库,因为已经做过数据验证。反之亦然,可以将从数据库获取的对象直接返回。
相对的,Flask这方面的缺失的。

完蛋,Pydantic 对 Flask 发起了单方面安装制裁。当你在 Flask 项目里试图引入 Pydantic 的时候,就会出现安装错误“Warnning: Pydantic is strictly forbidden to installed into a Flask project. Your behavior has been reported to PAFAFC (Pydantic and FastAPI and friends Committee).”

再补充一点。FastAPI无论看项目名字还是介绍都能感觉出来它是用于构建API服务的,事实上FastAPI自己的核心代码也确实是这样的,可以说它不是一个传统的、完全自己实现的框架,它更像是一个集各家之长的框架,从一个空壳开始,把需要的、适合的组件组装起来。例如它没有模版引擎,如果你确实需要用它实现一个web应用要渲染模版,你可以再组合你想要的选择,当然还可以用Starlette内置的Jinja2(是的,也是Flask内置的)

作者对于 Flask 和 FastAPI 两者定位不同的认识真是越来越清晰了呢。不过「组合我想要的选择」不就变成前面说的 Flask 那样东拼西凑了嘛,不好。还有就是 FastAPI 为啥要在介绍模板引擎的文档里让我安装 Jinja2 呢?看来「相对的,FastAPI这方面是缺失的」。

上面提的是FastAPI的优势,但是也不能说明Flask已死,我为什么会这么觉得呢? 主要还是看开发者和用户的人气。

重头戏来了,下面终于到了对于「Flask 之死」的凄惨景观的详细描写(注:这几点后来作者看到了我在 Twitter 上发的东西又自己做了补充和修改。这里我引用的是原文)。

1. 社区活跃度。例如项目的Issue和Pull Request数量,Flask都是个位数,和FastAPI相比根本不是一个水平。这其实侧面证明项目不再活跃,因为如果活跃的话,老的用户会有更多需求,它们不来说明已经离开,而新的用户意味着有各种问题,文档即便足够全,其实我们要知道还是会有很多用户来提问和贡献代码的,没有就说明用得少。

如果你经常参与开源项目或是自己开发了开源项目,通常会了解到,一个流行的项目想要把所有的 issue 和 pull request 都处理掉是很难的事情。而 Flask 能做到这一点反而是维护团队的努力尽责和项目本身足够成熟的体现:

blank

以及对 issue 和 pull request 快速响应的体现:

blank

如果你想知道有多少人在用 Flask 和 FastAPI,有两个参考数据,一个是 GitHub 上开源项目的用户数量统计。这些可以在两个项目的 GitHub 主页直接看到。

FastAPI

blank

Flask

blank

另一个数据是 PyPI 下载量,这个可以在 PyPI Download Stats 上看到。

FastAPI

blank

Flask

blank

另一方面,不同的项目对 issue 的使用策略是不同的。有的使用自己的 issue tracker,有些鼓励用户来提问。Issue 和 pull requests 不仅不能用来表示项目活跃度,过多的数量反而能说明一个项目的维护状况堪忧。堆积的 PR 对于用户和开发者来说都是灾难。以 FastAPI 为例,你看看现在的 PR 列表里都是什么?项目不如改名叫「FastAPI 文档大全」好了。你觉得这样会让提交代码贡献,修复 bug 的人有很好的体验?

blank

FastAPI 有不少 issue 在建议作者创建开发团队,摆脱当前这种低效的维护模式。类似的抱怨有很多,我不想去翻历史,这里仅以置顶 issue 里的一则回复举例:

blank

评论中提到的是这个 PR。一个 bug 在 2022 年就有人试图修复,但是到现在都没有被处理。而且报告相关 bug 的 issue 已经被转换成讨论。最后一条是用户在建议把这个讨论转换回 issue:

blank

没错,FastAPI 本来有几百个 issue(好活跃,对吧),某个时间点被批量转换为讨论,谁知道里面有多少是没有被处理的 bug report。谁知道这 646 个 PR 里有多少是需要被优先处理的 PR。我在 2020 年也提议把文档翻译放到单独的仓库,后来这个 issue 也被转成讨论了。到现在贡献者依然被这个问题困扰

blank

2. 讨论度。也就是博客文章,Stackoverflow等网站咨询和讨论的热度,其实可以感受到,已经没什么人写Flask相关的内容了。

作者写文章是可以随口就来的?博客文章不好统计,那么以 Stack Overflow 来看。抛开问题总数量,因为两个项目创建时间不同。但就提问频率,以上次发推文和现在两次截图的数据来看,和 Flask 相关的问题都创建的更频繁。

FastAPI

blank

Flask

blank

3. 开发迭代的频率。翻一下commits,可以看到Flask只有一个维护者会偶尔修修bug,没有什么大的功能特性开发。

开发迭代的频率和活跃度也是作者强行建立的联系,不过结合 FastAPI 的运作方式,两位作者倒也是「双向奔赴」了。「偶尔修修bug,没有什么大的功能特性开发」有什么问题吗?这就是维护者应该做的事情,大的功能特性又不是每天都有。而且作者是不是没有看 FastAPI 的 commits 历史?你肯定猜不到三千多个 commits 里有一千多是 bot 在更新 changelog。再猜猜 FastAPI 最新一个 release 都有哪些新功能?11 个改动里有 5 个是在更新 README 上的赞助商列表哦:

blank

4. 灵魂人物的影响力。Flask的灵魂人物,也就是项目的发起人其实早就不再参与项目了,搜一下项目贡献记录可以看到Armin Ronacher上次参与开发已经是6年前了。

这条实在不知道说什么好。我创建了项目我就得绑死在项目上直到生命最后一刻是吗?引用一下我在 Twitter 上的回复:

开源项目一定不要创建 GitHub org,项目再流行也要放在自己的账号下。不要成立什么开发团队,PR 要自己慢慢 review,用户抱怨多了,你自然知道哪个 PR 比较重要。还有就是用自己的账号或头像来设置 bot,这样即使你哪天兴趣转移了,用户会以为你一直在参与。斯人已去,灵魂犹在。

我真的佩服 FastAPI 作者这一招用自己的账号来作为 bot,用户以为你在活跃,而这个 PR 哪天能够被 merge 还要多烧几炷香:

blank

一边说自己忙得不行,一边又坚持把项目放在自己个人账号下,坚持自己 review PR。这就是灵魂人物?

后记

终于写完了。没想到只是偶然在一台没有屏蔽推荐首页的电脑上打开了知乎,最后要浪费这么多时间和精力。我想肯定会有人看了开头就急着评论,说「FastAPI 如何好用」或是「Flask 如何不好用」甚至是「Python 不如 XXX」。这些都是离题的讨论,我不完全否认这些观点,也并没有谈及这些。这篇文章只有两个目的:

  • 强调不应该把两者放到一起比较
  • 指出那篇「引战文」中的错误观点和事实偏差,顺便指出一些 FastAPI 的问题

对于程序员来说,你可以使用任何框架,你也可以使用任何编程语言。程序员不就是要随时接受改变,随时学习新的东西吗?没人要你一辈子只能用 Flask 或是 Python。另一方面,即使别人都用 FastAPI 了,我用 Flask 又会怎么样?工具而已,只是按照需要做出适当或喜欢的选择。但是你起码要看一看文章再发表评论,而且也不用看到什么观点就相信什么观点(尤其是没有论据、结论先行的文章)。

同时建议 FastAPI 推介者保留一点体面。新事物的建立不一定要踩在已有事物的坟墓上。挑起对立只会导致 Python 社区的混乱。最后再引述我在上一篇文章的想法作结:

从长远看这些大都是一些临时问题,而且 FastAPI 作者已经开始全职开发开源项目,这些问题在未来应该都会慢慢得到改善。指出这些是希望更多的人可以客观看待 FastAPI,吹捧并不能让一个东西变得更好,参与开发、介绍用法和回答社区提问是比盲目吹捧更有意义的事情。我当然期待 FastAPI 能够越来越好,也期待看到有更多优秀的 Python 框架出现,但我不喜欢过度炒作、盲目的吹捧和错误的对比。


相关链接

Flask 已死,FastAPI 永生》上有5条评论

  1. 头像Yin

    特地去看了那篇“Flask 已死”的文章。我觉得那位作者对Django 的理解也不是很到位。文章说 “Django适合老手,不适合新手”。其实恰恰相反,而且定制方面也可以很好。感觉那篇文章就是一个道行最多1年以下的人写的。

    回复
  2. 头像Aber

    我倒认为一个已经趋于稳定的框架不应该有那么多的活跃度,像 flask 这种轻量级框架更是不需要频繁更新,没 BUG 也没有很好的 feature 更个屁,有刷 PR 的嫌疑。fastapi 一大堆问题还不修是比较过分。

    不过开源是这样的,名气大于代码。有名字自然有很多人来做周边设施,fastapi 出名也挺好的,各种服务也都通过支持 fastapi 来支持了 ASGI 接口。

    回复
  3. 头像Shane

    tiangolo 有和 Kenneth Reitz 一样“关注开发者体验”的理念,我认为把 pydantic + starlette 结合起来就是这样的天才设计。但看完作者指出 tiangolo 的种种问题后,我才发现 FastAPI 理应更好,它后续发展也让人担忧,如果我的某些项目 codebase 变大,怀疑 FastAPI 还能否适用…

    我在网上找 FastAPI 替代,发现了 litestar2.0,看起来值得一试

    回复
  4. 头像沉序

    牛的,看了一会儿发现有的地方是反讽,真有意思。知乎就是一个垃圾桶。我若花开,蝴蝶自来,没必要

    回复

撰写评论

电子邮件地址不会被公开,必填项已用 * 标出。