100行Python代码实现FastAPI Websocket 聊天室(纯协程方案)

news2024/11/15 6:50:50

本文发表于入职啦(公众号: ruzhila) 大家可以访问入职啦学习更多的编程实战。

项目地址

代码已经开源, fastapi_chatroom 👏 欢迎Star

代码运行效果:

在这里插入图片描述

所有的项目都在github上开源:100-line-code 欢迎Star 👏

用100行代码的不同语言(Java、Python、Go、Javascript、Rust)实现项目,通过讲解项目的实现,帮助大家学习编程

我们会定期在群里分享最新的项目实战代码,包括不同语言的实现

如何做一个聊天室

网页的聊天室最基本的功能就是能输入消息,并且广播给聊天室里面的所有成员,要支持消息的发生和接送,我们可以使用Websocket协议来实现这个功能

如果没有Websocket, 网页如果要获取服务器上的消息,就只能通过Pull的方式去获取最新的消息,也就是我们常说的轮询,这样会导致服务器的压力增大,而且消息的实时性也不高

这是一个简单的聊天室的工作流程:

在这里插入图片描述

  1. Bob 连接到聊天室,服务器启动了2个协程,一个用于接收消息,一个用于发送消息
  2. Bob 发送消息到服务端, 然后通过 dispatch_messsage 协程广播给所有的用户
  3. 聊天室所有的用户 收到消息之后,通过websocket发送到网页上,显示消息

这是最基本的聊天室的工作流程,通过这样的流程,我们可以大概知晓我们要怎么设计程序结构

下一步就是我们要用FastAPI实现简单的Websocket代码

FastAPI的Websocket程序结构

FastAPI是完全基于asyncio的HTTP Server框架,不同于其他的多线程框架,FastAPI是基于协程的框架,可以支持异步编程,也就是要求所有的函数都是异步函数

可以看一下如何用FastAPI实现一个简单的Websocket程序

from fastapi import FastAPI, WebSocket
app = FastAPI()

@app.websocket("/chat")
async def chatroom(websocket: WebSocket):
    await websocket.accept()
    while True:
        data = await websocket.receive_text()
        await websocket.send_text(f"Message text was: {data}")

通过这个代码,我们可以很简单就可以启用/chat的Websocket服务,通过receive_text接收消息,通过send_text发送消息,这个相当于做了一个简单的echo功能。

先上代码:
完整代码查看

协程模型

每个用户加入之后,都需要启动2个协程,协程之间通过asyncio.Queue来通信,这种消息通讯的方式是现代主流语言,比如go和rust都提倡的通信方式

  1. 系统启动时候,会启动一个全局的room_queue,当用户所有发送的消息都会放到这个队列里面,然后启动一个dispatch_message协程,用于广播消息
  2. 每个用户加入聊天室之后,都会启动2个协程,一个用于接收消息(task_recv_from_client),一个用于发送消息(task_send_to_client)

也就是说,用户发送的消息,收到后放入到room_queue,然后dispatch_message协程广播给所有的用户,将消息放入到用户的txbuf里面
然后用户的task_send_to_client 这个协程会从各自的txbuf中获取消息,然后发送到websocket上

可能会有人问为什么不能收到消息之后,遍历所有的用户的websocket直接发送消息?

其实这样做也不是不可以,如果用户数量比较多,那么每次发送消息都需要做一次O(n)的遍历,从算法上就不是一个好的模型

通过这种Pub/Sub的模式,可以将消息广播的复杂度降低到O(1),这样可以支持更多的用户,并且充分利用好协程的并发优势

代码解析

先启动一个全局的dispatch_message协程,用于广播消息

完整代码解析
我们看到代码其实很简单,就是

  • 98行等待消息进来
  • 102-105行 广播给所有的用户

用户加入聊天室

在这里插入图片描述

用户加入一个聊天室之后,会做一些基础的初始化工作,比如检查用户是否已经加入,名字是否规范

  • 71和79行 分别处理用户的加入和退出,并且广播系统消息给所有的用户,这样可以确保用户的加入和退出都是准确的
  • 74行 去执行用户的消息处理, 这时候是一个阻塞的操作,如果用户退出,那么这个协程也会退出

用户处理消息

我们定义了一个Client的类,用来处理用户的封装,这样可以更好的管理用户的状态
在这里插入图片描述

  • 51行 通过asyncio.gather启动多个协程,这个功能是同步协程最好的方案,相当于并行启动多个协程,任意一个协程退出,其他的协程也会退出,这样我们就可以保证,只要有一个任务退出,不管是接送还是发送,都会终止,不会出现僵尸任务
  • 35-40行 收到用户输入的消息之后,放入到room_queue里面,其他就不用管了,前面的dispatch_message协程会广播给所有的用户
  • 42-47行tx里面获取消息,这个队列的消息是来自于dispatch_message协程广播的消息

通过这样的设计,可以提升效率,降低系统的复杂度

如何让协程优雅的退出

要知道,我们大部分的协程都是一个死循环,如果没有处理好退出的逻辑,就会导致程序的内存泄漏,所以我们要确保协程的退出是优雅的
协程都是通过asyncio.Queue来通信,我们可以通过None来通知协程退出,只要收到None就退出即可

这种做法就是完全基于消息的协同,通过消息来控制协程的生命周期,这样可以确保协程的退出是优雅的

  • 57行 当一个任意一个协程退出的时候,给tx发送一个None
  • 45行 就可以让task_send_to_client结束

总结

通过这个项目,我们学会了如何使用Python的asyncio + FastAPI+ Websocket实现一个简单的聊天室,学会处理Websocket请求,了解协程的使用

  • FastAPI是最常用的Python Web框架之一,它的性能非常好,支持异步编程,可以用于高并发的场景
  • asyncio 是python最核心的异步协程库,可以用于处理IO密集型的任务, 通过async/await关键字,可以实现异步编程, 提高程序的性能 也简化了异步任务之间的同步操作
  • Websocket是一种基于TCP协议的全双工通信协议,可以实现客户端和服务端之间的实时通信,默认所有的浏览器都支持Websocket协议

交流

我们构建了一个100行代码项目的实战群,大家可以点击这里查看,加入一起学习编程

也可以访问入职啦学习更多的编程实战

所有的代码都在github上开源:100-line-code 欢迎Star 👏

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2144659.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

open sora1.1容器构建教程指南

一、介绍 Open-Sora 1.1 项目是 Colossal AI 团队开发的一个完全开源的视频生成项目,该项目致力于高效制作高质量视频,并通过开源原则实现先进视频生成技术的低成本普及。 1. 项目背景与目标 Open-Sora 项目旨在通过提供开源的模型、工具和内容&#…

idea使用阿里云服务器运行jar包

说明&#xff1a;因为我用的阿里云服务器不是自己的&#xff0c;所以一些具体的操作可能不太全面。看到一个很完整的教程&#xff0c;供参考。 0. 打包项目 这里使用的是maven打包。 在pom.xml中添加以下模块。 <build><plugins><plugin><groupId>org…

JDBC导图

思维歹徒 一、使用步骤 二、SQL注入 三、数据库查询&#xff08;查询&#xff09; 四、数据库写入&#xff08;增删改&#xff09; 五、Date日期对象处理 六、连接池使用 创建连接是从连接池拿&#xff0c;释放连接是放回连接池 七、事务和批次插入 八、Apache Commons DBUtil…

进程监控与管理详解

一、进程的定义: 进程process是正在运行的程序,包括: 分配的内存地址空间 安全属性、包括所有权和特权 一个或多个线程 进程状态 进程的环境包括: 本地和全局变量 当前调度上下文…

多目标优化算法(Multi-Objective Optimization Algorithms, MOOA)介绍

在现实世界中&#xff0c;许多问题都涉及到多个目标的权衡和优化。例如&#xff0c;在工程设计中&#xff0c;可能需要同时考虑成本、效率和可靠性&#xff1b;在资源管理中&#xff0c;可能需要平衡环境保护和经济效益。多目标优化算法&#xff08;Multi-Objective Optimizati…

bmp格式图片怎么转换jpg?这几种转换方法超级好用!

bmp格式图片怎么转换jpg&#xff1f;BMP格式&#xff0c;这一历史悠久的图像编码方式&#xff0c;正逐渐在数字时代的浪潮中显得力不从心&#xff0c;其边缘化的趋势愈发明显&#xff0c;这一现象的根源&#xff0c;在于BMP格式固有的局限性难以匹配现代用户对于图像处理的多元…

【Python】探索Magenta:音乐与艺术的机器智能创作

下班了&#xff0c;今天的苦就先吃到这里。 在人工智能的浪潮中&#xff0c;机器学习技术正逐渐渗透到艺术创作的各个领域。今天&#xff0c;我们来探索一个特别的项目——Magenta&#xff0c;它是由Google Brain团队发起的&#xff0c;旨在使用机器智能生成音乐和艺术。这个项…

Lucene详解介绍以及底层原理说明

文章目录 什么是Lucene?示意图1. 倒排索引2. 索引创建过程3. 数据存储4. 搜索过程5. 相关性评分 Lucene底层原理1. 倒排索引2. 索引创建过程3. 数据存储4. 搜索过程5. 相关性评分 什么是Lucene? Lucene是一个高性能的全文搜索引擎库&#xff0c;它基于倒排索引技术实现快速、…

Threejs之看房案例(下)

本文目录 前言最终效果1、点精灵1.1 添加点精灵1.2 点精灵效果2、添加事件2.1 鼠标移动事件2.1.1 效果2.2 鼠标点击事件2.2.1 效果2.3 切换互通3. 完整代码前言 在Threejs之看房案例(上)这篇博客中我们已经完成了大厅的3d观看效果,但是我们会发现如果想去其他房间观看,没有…

vue3+ant design vue 中弹窗自定义按钮设置及以冒号为基准布局

1、自定义弹窗按钮&#xff0c;去除取消和确定按钮。&#xff08;网上很多方法都是说通过插槽来实现&#xff0c;但是试了下不生效&#xff0c;那既然插槽不生效的话&#xff0c;干脆直接写按钮就好了&#xff09; <a-modalv-model:open"open"title"人员信息…

为什么现在都流行开放式耳机?四款性能出色的蓝牙耳机推荐

在当下&#xff0c;开放式耳机逐渐成为众多消费者的新宠。与传统入耳式耳机相比&#xff0c;开放式耳机展现出诸多独特之处。它可以呈现出更清晰的音质效果&#xff0c;让用户有更美妙的听觉体验。在佩戴感上&#xff0c;开放式耳机更为舒适&#xff0c;不会给耳朵带来压迫感。…

MYSQL登录失败,确保密码正确,常见问题

今天登录MYSQL时&#xff0c;发现登录不进去,我能确保密码没有错误&#xff0c;并且我昨天以这样的方式登录成功&#xff0c;我已经重启过mysql服务&#xff0c;但是依旧登录不进去。 C:\Users\user>mysql -u root -p Enter password: ****** ERROR 1045 (28000): Access …

tidb 集群搭建

官网的搭建文档&#xff1a;使用 TiUP 部署 TiDB 集群 | TiDB 文档中心 我本地使用三台 centos7.9 服务器搭建&#xff0c;要保证三台服务器之间是可以互相通信的&#xff1b; 搭建集群的命令在其中一台服务器上执行即可&#xff1b; 1、安装tiup&#xff1a; curl --proto …

[附源码]超简洁个人博客网站搭建+SpringBoot+Vue前后端分离

今天带来一款优秀的项目&#xff1a;个人博客系统源码 。 系统采用的流行的前后端分离结构&#xff0c;内含功能包括 "写博客文章"&#xff0c;“修改博客文章”&#xff0c;“富文本编辑器”&#xff0c;“评论管理”“管理员角色”&#xff0c;“游客角色”&#x…

9.18每日作业

使用cout实现输出斐波那契前20项的值 #include <iostream>using namespace std;int main() {int a 1,b 1;int sum;int i;for(i 0;i<20;i){cout << b << endl;sum ab;b a;a sum;}return 0; }使用cin和cout完成&#xff0c;提示并输入一个字符&#…

canvas画笑脸

用到 stroke()控制线条fill()填充区域&#xff1b;fillStyle填充样式beginPath()和closePath() &#xff1a;两个不相关路径间需要配合使用线性渐变createLinearGradient(x0, y0, x1, y1)&#xff0c; x0:起点的 x 轴坐标。y0&#xff1a;起点的 y 轴坐标。x1&#xff1a;终点…

Mercari煤炉上架产品需要注意什么?Mercari煤炉批量上传工具

Mercari煤炉是日本最大的二手交易平台之一&#xff0c;不仅拥有稳定的日本市场&#xff0c;还扩展到了美国&#xff0c;如今Mercari美国市场用户数量众多&#xff0c;商品从上架到出单的速度很快&#xff0c;通常不会超过三天&#xff0c;上架当然出单的卖家都很多&#xff01;…

硬件基础知识

驱动开发分为&#xff1a;裸机驱动、linux驱动 嵌入式&#xff1a;以计算机技术为基础&#xff0c;软硬结合的、可移植、可剪裁的专用计算机 单片机最小单元&#xff1a;vcc gnd reset 晶振 cpu --- soc :system on chip 片上外设 所有的程序都是在soc&#xff08;cpu&…

[C++进阶[六]]list的相关接口模拟实现

1.前言 本章重点 在list模拟实现的过程中&#xff0c;主要是感受list的迭代器的相关实现&#xff0c;这是本节的重点和难点。 2.list接口的大致框架 list是一个双向循环链表&#xff0c;所以在实现list之前&#xff0c;要先构建一个节点类 template <class T> struct L…

Packet Tracer - 配置编号的标准 IPv4 ACL(两篇)

Packet Tracer - 配置编号的标准 IPv4 ACL(第一篇) 目标 第 1 部分&#xff1a;计划 ACL 实施 第 2 部分&#xff1a;配置、应用和验证标准 ACL 背景/场景 标准访问控制列表 (ACL) 为路由器 配置脚本&#xff0c;基于源地址控制路由器 是允许还是拒绝数据包。本练习的主要内…