Python-FastAPI异步博客开发 (三)异步特性篇

本文探讨了Python异步编程在FastAPI框架中的应用,分析了异步编程中的IO阻塞问题,并提供了相应的解决方案。文章通过三类IO场景(连接耗时、通信耗时、数据操作耗时)详细解释了如何利用异步特性提高并发性能。同时,介绍了在类设计中使用的一些技巧,如将异步@property属性序列化。文章强调,FastAPI不仅兼容同步写法,还提供了强大的数据模式设计和Swagger-Doc支持,简化了开发工作。
摘要由CSDN通过智能技术生成

异步篇最接近Frodo的初衷了。通信与数据的内容使用传统框架的思路是相同的。而异步思路只改变了若干场景的实现方法。

博客地址

项目地址

异步编程不是新鲜概念,但他并没有指定很明确的技术特点和路线。相关概念也不是很清晰,很少有文章能细致地说明白 阻塞/非阻塞、异步/同步、并行/并发、分布式、IO多路复用、协程 这些概念的区别与联系。这些概念在CS专业的OS、分布式系统课程中可能有设计,但具体实现层面可能鲜有涉及。具体到Python这门语言,我阅读了很多工业界、python届的工作者(或者称为pythonista们)写的文章,下面两篇是最值得阅读的:

小白的 asyncio :原理、源码 到实现(1) - 闲谈后的文章 - 知乎; 当然标题是作者在自谦。该文作者结合CPython中asyncio标准源码、函数栈帧的源码和python函数上下文源码实现讲述了python异步的设计原理,并手写了一个简易版的事件循环和asyncio-future对象。

深入理解 Python 异步编程(上);这篇文章写于2017年,当时asyncio还没成为标准库。这篇文章大篇幅使用python和linux的epoll接口一步步实现了单线程异步IO,最后引出了asyncio的事件循环,证实了其便捷性。作者规划还有中下篇讲述asyncio的原理,可是目前还没等到下文。作者安放文章代码的仓库已经累计了数十条催更的issue。

基本问题

还记得我们再「通信篇」绘制的时序图吗?用它表示一次用户执行的逻辑是没问题的,但实际实现中,我们真的能这样写代码吗?这里有两个基本问题:

  • 并发访问问题,如何实现多人同时访问你的博客web进程?

  • 如何避免io阻塞,从而充分利用cpu的时间片?

第一个问题做过web开发的都很熟悉了,他的解决方案很多,因为这是软件发展中必须面对的问题:

  • os级别,io多路复用机制,成熟的为linux的epoll机制,nginx便是基于此实现访问并发。

  • 编程语言使用多线程解决,以Flask为例,使用本地线程解决线程安全问题。

  • 编程语言使用异步编程解决,以nodejs为例,promise+回调的方式。python就是以asyncio为代表的异步生态圈。

第二个问题其实跟第一个问题是一个意思,把对象换成cpu即可。Frodo解决第一个问题使用的是类似asyncio事件循环的uvloop循环,他包装成了一个机遇ASGI协议的web服务器uvicorn,他可以启动多个ASGI标准写的app,内置一套事件循环实现并发访问。

uvicorn main:app --reload --host 0.0.0.0 --port 8001

重点是Frodo对于第二个问题的解决,这些都是在程序细节中体现出的。

问题分析:哪里存在IO阻塞

我们拿「通信篇」中CRUD的通信逻辑举例,我们先标注出IO阻塞的地方, 然后对应到程序设计中的环节,再来思考在实现中怎么解决。

图中标注出了三类io场景,并有的是串行的需求,有的是并发(可以并发)的需求。我来分别解释下:

  • 第一类: 网络的连接和断开,http是基于tcp的可靠传输协议,建立连接的过程也是耗时的io操作。数据库的连接是网络连接或套接字文件读写类的链接,也是io耗时的。这些代码主要在web中的checkpoin函数,在Frodoviews目录下。

  • 第二类: 通信异步是指客户端发送请求,等待数据准备好到返回的过程,这部分等到的时间其实是后端的数据io操作,cpu不应被这段时间占用。这部分代码在Frodomdoels下。

  • 第三类: 数据异步是指跟数据库操作等待数据返回所需的时间消耗。这部分时间也应该还给cpu。

上述的很多场景必须是串行完成的,比如建立数据库连接–>数据操作–>断开连接。也有一些场景(主要是不涉及数据一致性的场景)可以是并行的,如缓存的更新与删除,因为KV数据库不涉及关系的联立,可以并行地删除。

解决方案

第一类:连接耗时

数据库的连接与退出同步中都会想到使用带with关键字的连接池,异步为了这一连接过程可以「被等待」或者说交出执行权给主程序,需要使用async关键字包装一下,并实现异步上下文的方法__aenter__, __aexit__.

import databases

class AioDataBase():
    async def __aenter__(self):
        db = databases.Database(DB_URL.replace('+pymysql', ''))
        await db.connect()
        self.db = db
        return db

    async def __aexit__(self, exc_type, exc, tb)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值