大家好,我有一个 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 作为开发语言的原因,我想问一下有没有什么网上有的比较好的教学可以帮助我。
谢谢大家。
1
yexiaoxing 2021-01-29 21:47:57 +08:00 via iPhone
静态文件可以用 nginx/apache 来 serve 吧,不需要靠框架。其他的可以看一下 fastapi 自己带的实例,里面实现的东西不少。
|
2
LeeReamond OP @yexiaoxing 我想让开发和部署更简化一些,如果全部服务由业务节点提供,nginx 只进行转发的话,我对项目的掌控力度更强,部署上也简单一些,静态性能方面我觉得异步时代倒是不需要做特别担心。例子方面,文档中有完整的路由+权限的例子吗,我没有找到
|
3
gjquoiai 2021-01-29 21:54:45 +08:00
建议别上 asyncio,可用性和异步没啥关系吧?
|
4
LeeReamond OP @gjquoiai 那应该用什么呢? django 对于我来说有些过于重了,性能也不太达标,虽然实际生产部署以后用户数量未必能达到 django 的转发能力上限,但是还是留有余地的好吧,我觉得这种需要大量通信的业务 asyncio 在延迟和转发效率上都挺有优势的。
|
5
gjquoiai 2021-01-29 22:13:09 +08:00
@LeeReamond #4
生态太差。。第三方库趟不完的坑。。甚至标准库也。。_(:з)∠)_ |
6
so1n 2021-01-29 22:18:32 +08:00 via Android
你说了这些,既然不需要文档之类的,starlette 就够用了,排查问题方便,没 fastapi 恶心。
权限系统在 github 搜索有很多开源的,哪个顺手用哪个。 mysql 非 orm 的只有 aiomysql 可以用,在 start_event 启动和 stop_event 关闭,具体 fastapi 文档有给出 日志的话用 linux 的 logroate 就够用了,而且没坑,安全。 |
7
wdhwg001 2021-01-29 22:27:11 +08:00 via iPhone
aiohttp 到 fastapi 的重构没啥意义的,除非你原本代码真的稀烂到一点功能都加不进去了。
不然你还是去用 gin 一类的 go 框架重构吧。 |
8
abersheeran 2021-01-29 22:37:59 +08:00 via Android
首先~aiohttp 性能比 fastapi 好,别听它那个宣传词,fastapi 别说跟 go 比了,比 bottle 这个古老的 WSGI 都慢。aiohttp 迁移到 fastapi 意义不大。
其次,如果你不需要文档功能,就像楼上说的,直接用 starlette 更好,因为 fastapi 就是把 starlette 和 pydantic 拼起来了,pydantic 提供了文档功能。日后如果你要从 starlette 升级到 fastapi 也很容易。 另外,看你说的技术栈似乎在同步里积累的更多?那么不如试试 bottle,这个比 flask 还古老的框架。速度比任何异步框架都快(也比 django/flask 快),而且一切都是你熟悉的那一套。只是出于性能考虑而换框架的话,可以考虑试试这个。 最后,说一句题外话:“无脑劝人上 Python asyncio,应该天打雷劈” |
9
abersheeran 2021-01-29 22:39:18 +08:00 via Android
Python 异步不仅仅只有不成熟的 asyncio,还有很成熟的 gevent 。
|
10
LeeReamond OP |
11
LeeReamond OP @abersheeran 前几天专门发了一个帖子测试,gunicorn+uvloop+aiohttp 的性能确实是没有 gunicorn+uvicorn+fastapi 的好,不过这个倒不重要,qps 也就是四万比五万这种感觉,需求是一种普遍性的快,而不是要快到极致,高出的性能完全冗余了。
|
12
gitopen 2021-01-29 22:43:28 +08:00
看来楼主已经想好了。🐶
|
13
LeeReamond OP |
14
abersheeran 2021-01-29 22:47:27 +08:00
@LeeReamond 严重怀疑你的测试有问题。https://github.com/the-benchmarker/web-frameworks
另外,你怎么测到四万的? Index.py 都只能跑三万,这两就更别提了。 |
15
zhuangzhuang1988 2021-01-29 22:48:39 +08:00
直接用 go 重写呗..
|
16
LeeReamond OP @abersheeran prefork 模型,不是单进程,不 fork 的话在生产中没有太大意义。详细可以参考,https://github.com/TechEmpower/FrameworkBenchmarks
|
17
LeeReamond OP @zhuangzhuang1988 有很多 py 依赖不能很容易的迁移,比如项目中一些科学计算的部分依赖 numpy 和 pandas,可视化方面 html 前端可以用 echarts 解决,图片就只能依赖 matplotlib 了,另外还有一些中文自然语言处理的内容,这些都迁移到 go 我头都要大了。还有有时要进行一些信息爬取,这个 go 应该也能做,但是还是 py 更熟悉一些,写得快
|
18
so1n 2021-01-29 23:10:32 +08:00 via Android
@LeeReamond starlette 需要自己实现参数注入 简单来说,fastapi 基于 starlette+pydantic 框架
|
19
abersheeran 2021-01-29 23:30:26 +08:00
@LeeReamond 啊这……prefork 只是分叉出多个进程,对于大部分 Python Web 框架来说,可处理量的增长是线性的。
|
20
LeeReamond OP @abersheeran 显然不是线性的,实际测试中我用 gunicorn 启动的几乎所有框架都存在软上限,比如单进程 qps 可以达到 15k,四进程并不能达到 15k * 4,实际大概会达到 15k*4*0.5 这样,广义的说比如你有一台 64 核心的机器显然用 fastapi\aiohttp\index.py 这种也不能做到单机百万并发
|
21
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 做数据中转与传输等等。 |
22
neoblackcap 2021-01-30 00:55:22 +08:00 1
鉴于你的需求,我提几个问题
1. 高可用跟异步特性有什么关系? 2. 高可用跟必须使用异步特性有什么关系? 3. 你对 fastapi 有多了解? 4. 你对 Python asyncio 有多了解? 5. SPA 程序为什么还需要后端提供服务? single page application 一般特指使用前端框架或者类似技术实现的程序,此类应用恰恰需要前后端分离。后端单纯提供接口。如果需要提供页面访问能力的,恰恰是传统的服务器渲染架构。 6. 为什么你认为用一个你自己都不是很熟悉的框架可以快速保质不超工期完成你的需求? 不管你最后使用什么框架,但是我觉得你如果思考清楚了以上几点之后。你可以得到更好技术评估结果 |
23
LeeReamond OP @so1n 大佬能不能讲一下 logroate,我搜了搜 logroate+python 没搜到什么有效的信息。大概看下来这似乎是一个 linux 的计划任务工具,属于第三方进程监控文件,然后当文件过大时触发清理?按我目前的理解,如果想用这个记录日志的话,是正常使用 web 框架写程序,正常记录日志,然后再附加一个第三方的清理进程这样吗?
计算机系统的功底不扎实,当清理触发的时候,程序内的文件描述符会怎么样,会不会出现原进程都无法继续写入日志的问题。我看网上一些中文资料里写的范例,触发之后都是要重启 nginx 的 |
24
LeeReamond OP @neoblackcap 大佬勿喷,当然是因为有不清楚的地方所以才来问的,我当然不是什么都懂,我只是说了我的逻辑,如果你觉得我逻辑哪里有问题可以指出,而不是像这样问我都了解什么。
关于第五点,我在楼上的回复里有过说明,我认为文件服务交给业务而不是 nginx 可以让部署更加方便。我再举一个例子是,实际服务里面往往也不是一个纯粹的 SPA,大部分内容交给 SPA 操作,但实际仍有可能嵌入一些单个页面,比如从外部(同事,或者第三方)引入一个独立的 html+js+css 结构的页面想要嵌入 SPA 的某个路由当中。这种问题当然有很多种解决方法,前端可以解决,用 nginx 的方法也可以解决,我只是觉得这种方式更直接,而且在我看来无论是文件服务交给 nginx 或者交给业务节点,并没有什么明显的 drawback 。 关于第六点,人非生而知之者,不能说不懂的就全部否掉不用了,总要慢慢学 |
25
LeeReamond OP @laminux29 大佬为什么提到大内存服务器的问题,我这帖子里没提到啥服务器的内容啊。。
|
26
neoblackcap 2021-01-30 01:44:49 +08:00 1
@LeeReamond 我觉得你说的都没有问题,我只是作为一个旁观者问一下你的想法。
打个比方,你认为一个技术一定能解决你的问题。你总能说出具体的原因吧。 比如 go 写的程序效率很有可能比 Python 写的性能高。因为 go 不是解释语言。对不对? 我看你写的话感觉你是被潮流所迷惑了。自我写 Python 以来,Django 日常被喷。然而它却一直在更新。还引入了新的技术,制定了了新的协议。潮流并非一定是潮流。好比 Django 有 instagram 这样的成功案例,其他的都没有。你的业务还比 instagram 的并发高么? 至于你回答的 SPA 就很好了。你显然可以按这样的方式回去思考相关的点。不过我要提一句,真的是没有区别吗?其实我认为无论是性能还是维护都有很大的区别。这就看你的需求。我不知道,所以只能由你自己回答。 第六点恰恰是我想提个醒,程序员总是高估自己,低估问题的复杂度 我提问题并不是抬杠,毕竟我直接跟你讲“小黄鸭调试法”你也不知道我想表达什么吧?如果你作为一个决策人,你自己都搞不懂为什么要选这个技术栈,这个技术栈有什么优势劣势。那么如何说服其他人呢?当然如果你是全干的那就随你吧。 其实网上的很多回答并不能直接采用,你不懂,我跟你讲 fastapi 坑很多,无脑上 Django/Flask/Tornado/Bottle,你怎么知道对还是不对? |
27
LeeReamond OP @neoblackcap 感谢回复,这个帖子基本上还是调研范围,毕竟已经有一个验证比较成熟的方案,想换框架是因为一些肉眼可见的好处(参数注入等),因为确实目前用的不是说不能用,但是用起来并不开心。路由表打开一看几十行,参数处理要写上千行,比如现在要重构了,让我再重新写一遍,肯定是不开心的。
异步的问题我觉得是这样,因为要频繁和数据库交互,IO 复用肯定比线程模型优势大,比如每次一个请求被访问,最基础的会产生一次权限表请求,然后业务方面或许会产生到不同数据表的若干次请求,比如这样的 10 个小请求,然后再加一次复杂连表计算,这个连表需要计算较长时间。那么理论上我当然希望这若干个请求同时发送,回调业务处理,让通信消耗的时间少一些,我觉得在这方面 asyncio 相比于线程模型要好。比如 django 用 wsgi 部署,echo 的通信延迟可能在几十毫秒的数量级,加入后端请求后在一百到两百毫秒,而异步可以缩减到个位数,django 的 asgi 我不是很了解 |
28
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 想都不用想就排除了。单单线程残废就是一个死结。寄希望于框架帮你实现大并发,我只能说一句“没有银弹”。任何的高并发都建立于你对系统、程序等等多方面的了解。 |
29
so1n 2021-01-30 03:12:19 +08:00
@LeeReamond 一般 web 进程会通过 supervisor 等来托管的,logrotate 在切换日志时会发送一个信号给守护进程, 守护进程根据信号重新打开日志文件,这时候日志流就会写入到新的文件里面.如果不用守护进程,或者守护进程不支持该功能,logrotate 支持另一种方法:迁移文件再清空当前文件, 程序日志流写的目标不变,文件内容变,这种方式只有瞬间有极多的日志流才会出现漏几行日志的情况,一般的业务量不会导致漏日志
|
30
popil1987 2021-01-30 06:11:00 +08:00 1
用 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 的话才可以随便尝试。 |
31
laminux29 2021-01-30 07:25:03 +08:00 1
@LeeReamond 我回帖前,一般会翻翻发帖人的发帖、回帖记录,来综合分析发帖人到底遇到过什么问题,以及他们的水平、偏好、思路、习惯等等,这对于猜测发帖人的真实需求很有帮助。
你的问题我基本上已经回答了,但对你没太大帮助,因为关键决策性的东西,你已经做了一两个月了,现在来更换很不现实。而且在本帖中,你需要的回答,是需要长期进行积累与试错的经验性的东西,这些东西别人没办法一步到位帮到你。不过这事的本质还是你使用了高风险高回报的激进策略。选择什么策略没有好坏,只是你要考虑能否接受策略失败后的风险。 |
32
LeeReamond OP @neoblackcap 我理解上 io 复用高效的原因在于避免了线程切换产生的时间切片问题,这是其一。
cython 相关的问题我不是很理解你为什么会指出处理请求时不能进行 cpu 密集操作,因为在线程与协程模型中 cython 反倒是不变的那个,因为是纯计算且不涉及 gil,两者使用上并无差异。 这里用这套工具当然不是为了压榨机器性能,只是因为各方面恰好合适而已,数据操作部分,pandas 可以帮我完成大部分处理,而少数没有覆盖的地方,算法用 cy 实现一下也很快。 另外我觉得用完全实际的项目流量选型也未必合适,毕竟即使是最沉重的 django 的单节点部署后响应能力也不是轻易就能达到上限的,毕竟实际业务当中能做到 1000qps 已经是大站了,而这个并发下显然瓶颈也不在 django 本身,它仍是堪用的。我只是觉得还是要以发展的眼光看问题,毕竟项目日活会逐渐增高,只是希望找一种转发效率和开发效率平衡的方式,不要框架消耗太高,也不要框架太难写导致开发缓慢 |
33
charmToby 2021-01-30 08:36:53 +08:00
额,我自己写了一个小 demo,https://github.com/CoderCharm/fastapi-mysql-generator 算是集成了权限什么之类的,orm 我没有使用异步的。没有上生产,但是学习参考应该足够了。
|
34
LeeReamond OP @charmToby 感谢!
|
35
neoblackcap 2021-01-30 12:20:06 +08:00 1
@LeeReamond 因为是协程,如果并发 10 个请求过来,你每个请求都需要进行计算密集型操作 10s,那么这批请求的总耗时将达到 100s (单进程)。协程不会自动进行调度。
|
36
abersheeran 2021-01-30 12:40:09 +08:00
@LeeReamond 非线性的增长主要是受限于操作系统、以及该程序使用的诸如缓存、数据库等第三方服务。跟框架本身其实无甚关系。
|
37
yangyaofei 2021-01-30 21:36:52 +08:00
fastapi 就是一个 api server 吧,按照前后端分离的话,应该页面直接静态的就可以吧.
fastapi 是有 jwt 的,个人觉得还行 OpenAPI 个人觉得就是一个标准,其实怎么写随开发者吧.... 数据库什么的是用 dependency 的方式直接引用的,貌似框架帮你处理了 |
38
LeeReamond OP @abersheeran 我测试的是单纯的 echo server,没有接入其他任何服务,应该是受限于操作系统。但是具体原因也未必很重要,我只是压力能看到很实在的压力和延迟测试数据,只能说实机跑就是这种感觉,我只能根据结果选一个合适的
|
39
johnsona 2021-01-31 10:49:53 +08:00 via iPhone
django,这是唯一的建议
如果还有什么的话 django restframework 性能就别闹了 |