想要使用 FastAPI 重构项目,应该如何快速入门?

2021-01-29 21:34:18 +08:00
 LeeReamond

大家好,我有一个 Python web 项目需要在近期重构。

对我来说我的需求主要有两点,其一是我需要后端有较高的可用性,所以我必须要使用异步特性,其二是由于我与后端数据库交互中有很多涉及高级特性的操作,并不适合使用 ORM,大多数情况下需要直接写 SQL 。所以这几天在论坛里收集信息之后决定使用 FastAPI 这个框架。

我原先的项目是使用 aiohttp 构建的,基本上属于毛坯房中的毛坯房,涉及到参数读取、权限、数据库操作等各方面都需要自行配置。因为在同步时代也是使用 flask,这方面倒是没有给我造成太多困扰。根据以往的经验,项目中通常要配置大段的业务代码实现对输入参数的可靠读取,以及发生错误时返回完善的异常信息,而 fastapi 在这方面的自动化确实是很吸引人。

我在粗略通读 fastapi 的文档后,觉得还是需要来 v2 问一下使用过的大佬的经验,以免走弯路。再加上本着学习一门新工具的态度,我觉得还是要先问一下,不适合直接把以前的做法往上面套。

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

我目前的问题是以下几点:

1 、我需要开发一个 SPA 网页项目,即引擎需要进行 html/js/css 的服务,同时使用 API,而不单纯是一个 API 接口,这似乎与这个框架的设计初衷不符,所以产生了两个问题。

其一是在文件服务的部分,我在粗读文档的过程中没有找到比较完善的项目模块化配置信息,也就是各种静态文件应该如何妥善安排结构、模板,并妥善地提供服务等等,我想知道 fastapi 是不是不适合做这方面的操作,有没有过去使用中遇到坑的朋友。

其二是在接口方面,fastapi 有完善的支持,并且能自动生成文档,但其中一些特性是我不需要的。举例来说,自动文档功能不是我所需要的,在大多数时候我不想暴露接口的详细信息,只需要在错误时返回可供 debug 的异常信息即可,对于如何关闭文档功能这方面,我在粗读 fastapi 文档时没有找到太多信息。

2 、关于权限系统,在接口的参数读取方面,fastapi 的文档中的说明令人欣喜,可以让我摆脱大段的业务代码维护可靠读取和可靠异常返回,但是关于权限我并未在文档中获取到太多信息。我目前的项目有一套依赖于 jwt 的权限认证,我想知道 fastapi 在这方面有没有集成,比如像 django 那样的有一套完善权限解决方案,还是我仍需要像以前一样自己实现一套权限系统。

3 、FastAPI 完全支持 OpenAPI, 但是我个人对这个标准不是十分熟悉,我想知道使用,或者不使用 openapi 这套规范是否会对我的项目产生什么影响?

4 、后端服务的连接方面有一些问题,如上文所述我需要在项目中直接连接关系型数据库,以及 redis 缓存服务器,在以往的框架使用经验中我需要自行配置这些客户端。但是由于 fastapi 同时需要使用另一个我没接触过的引擎 uvicorn 进行服务,我不太了解这个引擎的规则,例如通常我们在一个业务节点中(比如单台服务器)通常 prefork 出多个监听进程来利用 CPU 的多核心,我想知道 fastapi 里通用的配置规范是什么,哪些代码在 fork 前执行,哪些代码在 fork 之后,因为涉及交互,以及 logging 相关的操作,我希望节点能最大限度地公用连接。

比如 log 操作当中,多节点共同对一个文件写入日志,单纯写入并没有什么问题,但遇到日志过长,需要滚动的情况,fastapi 应该如何处理,让 logging 系统可以正常地清空不需要的内容?

5 、我目前的需求是,尽快地将项目的模块化配置好,然后实现一套完整的路由映射、权限管理、之后能让我比较快地进入到业务代码的开发(如同使用所有其他项目一样),由于业务代码逻辑复杂且繁重,工期还需要尽量快,这也是我使用 python 作为开发语言的原因,我想问一下有没有什么网上有的比较好的教学可以帮助我。

谢谢大家。

7323 次点击
所在节点    Python
39 条回复
laminux29
2021-01-30 00:54:04 +08:00
1.不建议选择 Oracle,这玩意是个烂心大苹果,不仅难用,麻烦,而且 D 版 bug 多,正版死贵用不起。现阶段建议使用微软的 MSSQL,基本上高端数据库该有的功能,它都有。

2.大内存服务器,你别只看着一线品牌全新款,那肯定死贵。洋垃圾 E7 + 国产 2 线主板 + 插满 128G 内存,也就 3 千不到,只是新手去买容易踩雷。

3.Python 属于胶水语言,你找模块,找组件,完全没必要只在 Python 框架下找组件。你可以直接去找 top 3,然后看看它支持什么语言,接着用 Python 甚至 DB 做中转。

复杂系统一般都是这样拆分的,比如 java 写业务,python 写爬虫,C++处理高性能数据计算,MSSQL 处理核心数据,redis 做热数据缓存,mongodb 做龟速大批量计算,然后利用 http 或 pb 或 db 做数据中转与传输等等。
neoblackcap
2021-01-30 00:55:22 +08:00
鉴于你的需求,我提几个问题
1. 高可用跟异步特性有什么关系?
2. 高可用跟必须使用异步特性有什么关系?
3. 你对 fastapi 有多了解?
4. 你对 Python asyncio 有多了解?
5. SPA 程序为什么还需要后端提供服务? single page application 一般特指使用前端框架或者类似技术实现的程序,此类应用恰恰需要前后端分离。后端单纯提供接口。如果需要提供页面访问能力的,恰恰是传统的服务器渲染架构。
6. 为什么你认为用一个你自己都不是很熟悉的框架可以快速保质不超工期完成你的需求?

不管你最后使用什么框架,但是我觉得你如果思考清楚了以上几点之后。你可以得到更好技术评估结果
LeeReamond
2021-01-30 01:01:40 +08:00
@so1n 大佬能不能讲一下 logroate,我搜了搜 logroate+python 没搜到什么有效的信息。大概看下来这似乎是一个 linux 的计划任务工具,属于第三方进程监控文件,然后当文件过大时触发清理?按我目前的理解,如果想用这个记录日志的话,是正常使用 web 框架写程序,正常记录日志,然后再附加一个第三方的清理进程这样吗?

计算机系统的功底不扎实,当清理触发的时候,程序内的文件描述符会怎么样,会不会出现原进程都无法继续写入日志的问题。我看网上一些中文资料里写的范例,触发之后都是要重启 nginx 的
LeeReamond
2021-01-30 01:08:46 +08:00
@neoblackcap 大佬勿喷,当然是因为有不清楚的地方所以才来问的,我当然不是什么都懂,我只是说了我的逻辑,如果你觉得我逻辑哪里有问题可以指出,而不是像这样问我都了解什么。

关于第五点,我在楼上的回复里有过说明,我认为文件服务交给业务而不是 nginx 可以让部署更加方便。我再举一个例子是,实际服务里面往往也不是一个纯粹的 SPA,大部分内容交给 SPA 操作,但实际仍有可能嵌入一些单个页面,比如从外部(同事,或者第三方)引入一个独立的 html+js+css 结构的页面想要嵌入 SPA 的某个路由当中。这种问题当然有很多种解决方法,前端可以解决,用 nginx 的方法也可以解决,我只是觉得这种方式更直接,而且在我看来无论是文件服务交给 nginx 或者交给业务节点,并没有什么明显的 drawback 。

关于第六点,人非生而知之者,不能说不懂的就全部否掉不用了,总要慢慢学
LeeReamond
2021-01-30 01:10:43 +08:00
@laminux29 大佬为什么提到大内存服务器的问题,我这帖子里没提到啥服务器的内容啊。。
neoblackcap
2021-01-30 01:44:49 +08:00
@LeeReamond 我觉得你说的都没有问题,我只是作为一个旁观者问一下你的想法。
打个比方,你认为一个技术一定能解决你的问题。你总能说出具体的原因吧。
比如 go 写的程序效率很有可能比 Python 写的性能高。因为 go 不是解释语言。对不对?

我看你写的话感觉你是被潮流所迷惑了。自我写 Python 以来,Django 日常被喷。然而它却一直在更新。还引入了新的技术,制定了了新的协议。潮流并非一定是潮流。好比 Django 有 instagram 这样的成功案例,其他的都没有。你的业务还比 instagram 的并发高么?

至于你回答的 SPA 就很好了。你显然可以按这样的方式回去思考相关的点。不过我要提一句,真的是没有区别吗?其实我认为无论是性能还是维护都有很大的区别。这就看你的需求。我不知道,所以只能由你自己回答。

第六点恰恰是我想提个醒,程序员总是高估自己,低估问题的复杂度

我提问题并不是抬杠,毕竟我直接跟你讲“小黄鸭调试法”你也不知道我想表达什么吧?如果你作为一个决策人,你自己都搞不懂为什么要选这个技术栈,这个技术栈有什么优势劣势。那么如何说服其他人呢?当然如果你是全干的那就随你吧。

其实网上的很多回答并不能直接采用,你不懂,我跟你讲 fastapi 坑很多,无脑上 Django/Flask/Tornado/Bottle,你怎么知道对还是不对?
LeeReamond
2021-01-30 02:00:17 +08:00
@neoblackcap 感谢回复,这个帖子基本上还是调研范围,毕竟已经有一个验证比较成熟的方案,想换框架是因为一些肉眼可见的好处(参数注入等),因为确实目前用的不是说不能用,但是用起来并不开心。路由表打开一看几十行,参数处理要写上千行,比如现在要重构了,让我再重新写一遍,肯定是不开心的。

异步的问题我觉得是这样,因为要频繁和数据库交互,IO 复用肯定比线程模型优势大,比如每次一个请求被访问,最基础的会产生一次权限表请求,然后业务方面或许会产生到不同数据表的若干次请求,比如这样的 10 个小请求,然后再加一次复杂连表计算,这个连表需要计算较长时间。那么理论上我当然希望这若干个请求同时发送,回调业务处理,让通信消耗的时间少一些,我觉得在这方面 asyncio 相比于线程模型要好。比如 django 用 wsgi 部署,echo 的通信延迟可能在几十毫秒的数量级,加入后端请求后在一百到两百毫秒,而异步可以缩减到个位数,django 的 asgi 我不是很了解
neoblackcap
2021-01-30 02:40:04 +08:00
@LeeReamond
你这个问题有试验吗?每次的请求都是建立 TCP 连接,到解析请求内容,你这个 asyncio 怎么就让通讯消耗的时间少一些呢?
IO 复用不是银弹,asyncio 在 linux 下基本上是 epoll 的封装。它只能解决并发的问题。但是如果你一个请求需要进行 10s 钟的计算,那么每个请求的耗时并不会降低。
所以你如果认为 asyncio 能帮你降低单次请求耗时,我认为你的实验是有问题。
fastapi 之所以看起来比一般的框架快,只不过是它的 asgi server 实现得更加高效罢了。同样的道理,如果去掉所有中间件的并使用一样的 asgi server 实现,它们也不会相差很大。类似的结果,你自己之前也有说明。没有数量级的差别(aiohttp vs fastapi)

你也不说具体的设计指标。我现在就只看到数万的 qps benchmark 。要知道现实往往没有那么高的并发,而且 TechEmpower 那个 benchmark 我都无力吐槽了。要想往前排,大家都是各种 hack,看看就好。认真你就输。

而且你了解 fastapi 跟你之后 cython 计算模块整合的详情吗?所有这些 IO 复用的框架都不能在处理请求的时候进行 CPU 密集型操作,你知道不?

PS:你要讲究压榨机器性能,Python 想都不用想就排除了。单单线程残废就是一个死结。寄希望于框架帮你实现大并发,我只能说一句“没有银弹”。任何的高并发都建立于你对系统、程序等等多方面的了解。
so1n
2021-01-30 03:12:19 +08:00
@LeeReamond 一般 web 进程会通过 supervisor 等来托管的,logrotate 在切换日志时会发送一个信号给守护进程, 守护进程根据信号重新打开日志文件,这时候日志流就会写入到新的文件里面.如果不用守护进程,或者守护进程不支持该功能,logrotate 支持另一种方法:迁移文件再清空当前文件, 程序日志流写的目标不变,文件内容变,这种方式只有瞬间有极多的日志流才会出现漏几行日志的情况,一般的业务量不会导致漏日志
popil1987
2021-01-30 06:11:00 +08:00
用 fastapi 做过几个项目,特来回答下
1.1
模块化: https://fastapi.tiangolo.com/tutorial/bigger-applications/
静态文件: https://fastapi.tiangolo.com/tutorial/static-files/
模板: https://fastapi.tiangolo.com/advanced/templates/
个人只使用 api,直接把前端编译好让 nginx 提供服务
1.2
app = FastAPI(docs_url=None)
2
没有权限系统集成
3
openapi 是文档系统,是否需要得自己评估
4
架构不一样,处理方法不一样。如果我处理日志问题的话,用一个队列比较好。对 fastapi 来说可以用 BackgroundTasks 或 celery redis
5
快速开发还得 django,现在 fastapi 缺乏一个 pydantic model 和数据库 model 转换的杀手级工具,如果你数据库表有几十个字段,你要同时写一遍数据库 model 和 pydantic model 。建议还是 django 写完,需要改进性能的地方渐进的改为异步或其它语言。没有 deadline 的话才可以随便尝试。
laminux29
2021-01-30 07:25:03 +08:00
@LeeReamond 我回帖前,一般会翻翻发帖人的发帖、回帖记录,来综合分析发帖人到底遇到过什么问题,以及他们的水平、偏好、思路、习惯等等,这对于猜测发帖人的真实需求很有帮助。

你的问题我基本上已经回答了,但对你没太大帮助,因为关键决策性的东西,你已经做了一两个月了,现在来更换很不现实。而且在本帖中,你需要的回答,是需要长期进行积累与试错的经验性的东西,这些东西别人没办法一步到位帮到你。不过这事的本质还是你使用了高风险高回报的激进策略。选择什么策略没有好坏,只是你要考虑能否接受策略失败后的风险。
LeeReamond
2021-01-30 07:33:05 +08:00
@neoblackcap 我理解上 io 复用高效的原因在于避免了线程切换产生的时间切片问题,这是其一。
cython 相关的问题我不是很理解你为什么会指出处理请求时不能进行 cpu 密集操作,因为在线程与协程模型中 cython 反倒是不变的那个,因为是纯计算且不涉及 gil,两者使用上并无差异。

这里用这套工具当然不是为了压榨机器性能,只是因为各方面恰好合适而已,数据操作部分,pandas 可以帮我完成大部分处理,而少数没有覆盖的地方,算法用 cy 实现一下也很快。

另外我觉得用完全实际的项目流量选型也未必合适,毕竟即使是最沉重的 django 的单节点部署后响应能力也不是轻易就能达到上限的,毕竟实际业务当中能做到 1000qps 已经是大站了,而这个并发下显然瓶颈也不在 django 本身,它仍是堪用的。我只是觉得还是要以发展的眼光看问题,毕竟项目日活会逐渐增高,只是希望找一种转发效率和开发效率平衡的方式,不要框架消耗太高,也不要框架太难写导致开发缓慢
charmToby
2021-01-30 08:36:53 +08:00
额,我自己写了一个小 demo,https://github.com/CoderCharm/fastapi-mysql-generator 算是集成了权限什么之类的,orm 我没有使用异步的。没有上生产,但是学习参考应该足够了。
LeeReamond
2021-01-30 09:55:27 +08:00
@charmToby 感谢!
neoblackcap
2021-01-30 12:20:06 +08:00
@LeeReamond 因为是协程,如果并发 10 个请求过来,你每个请求都需要进行计算密集型操作 10s,那么这批请求的总耗时将达到 100s (单进程)。协程不会自动进行调度。
abersheeran
2021-01-30 12:40:09 +08:00
@LeeReamond 非线性的增长主要是受限于操作系统、以及该程序使用的诸如缓存、数据库等第三方服务。跟框架本身其实无甚关系。
yangyaofei
2021-01-30 21:36:52 +08:00
fastapi 就是一个 api server 吧,按照前后端分离的话,应该页面直接静态的就可以吧.

fastapi 是有 jwt 的,个人觉得还行

OpenAPI 个人觉得就是一个标准,其实怎么写随开发者吧....

数据库什么的是用 dependency 的方式直接引用的,貌似框架帮你处理了
LeeReamond
2021-01-30 22:12:37 +08:00
@abersheeran 我测试的是单纯的 echo server,没有接入其他任何服务,应该是受限于操作系统。但是具体原因也未必很重要,我只是压力能看到很实在的压力和延迟测试数据,只能说实机跑就是这种感觉,我只能根据结果选一个合适的
johnsona
2021-01-31 10:49:53 +08:00
django,这是唯一的建议
如果还有什么的话
django restframework
性能就别闹了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/749706

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX