Quart框架教程:构建基础WebSocket聊天服务器
前言
在现代Web应用中,实时通信功能变得越来越重要。本教程将指导你如何使用Python的Quart框架构建一个基础的WebSocket聊天服务器。Quart是一个兼容ASGI的Python Web框架,它提供了对WebSocket协议的原生支持,非常适合构建实时应用。
环境准备
1. 创建项目结构
我们首先需要创建一个Python项目。推荐使用Poetry作为项目依赖管理工具:
pip install poetry
poetry new --src chat
这个命令会创建一个名为chat
的项目,采用src
布局(源代码放在src
目录下)。所有后续命令都应该在chat
目录下执行。
2. 添加项目依赖
聊天服务器只需要Quart作为主要依赖:
poetry add quart
安装完成后,运行以下命令确保依赖正确安装:
poetry install
核心实现
3. 创建Quart应用
在src/chat/__init__.py
中创建基础的Quart应用:
from quart import Quart
app = Quart(__name__)
def run() -> None:
app.run()
为了方便启动应用,我们在pyproject.toml
中添加一个脚本:
[tool.poetry.scripts]
start = "chat:run"
现在可以通过以下命令启动应用:
poetry run start
4. 设计前端界面
我们需要一个简单的HTML页面作为聊天界面。在src/chat/templates/index.html
中创建:
<script type="text/javascript">
const ws = new WebSocket(`ws://${location.host}/ws`);
ws.addEventListener('message', function (event) {
const li = document.createElement("li");
li.appendChild(document.createTextNode(event.data));
document.getElementById("messages").appendChild(li);
});
function send(event) {
const message = (new FormData(event.target)).get("message");
if (message) {
ws.send(message);
}
event.target.reset();
return false;
}
</script>
<div style="display: flex; height: 100%; flex-direction: column">
<ul id="messages" style="flex-grow: 1; list-style-type: none"></ul>
<form onsubmit="return send(event)">
<input type="text" name="message" minlength="1" />
<button type="submit">Send</button>
</form>
</div>
然后添加路由处理函数:
from quart import render_template
@app.get("/")
async def index():
return await render_template("index.html")
5. 实现消息代理
为了实现消息的广播功能,我们需要创建一个消息代理。在src/chat/broker.py
中:
import asyncio
from typing import AsyncGenerator
class Broker:
def __init__(self) -> None:
self.connections = set()
async def publish(self, message: str) -> None:
for connection in self.connections:
await connection.put(message)
async def subscribe(self) -> AsyncGenerator[str, None]:
connection = asyncio.Queue()
self.connections.add(connection)
try:
while True:
yield await connection.get()
finally:
self.connections.remove(connection)
这个代理实现了基本的发布-订阅模式,使用Python的asyncio.Queue
来管理消息。
6. WebSocket路由实现
现在我们可以实现WebSocket路由了:
import asyncio
from quart import websocket
from chat.broker import Broker
broker = Broker()
async def _receive() -> None:
while True:
message = await websocket.receive()
await broker.publish(message)
@app.websocket("/ws")
async def ws() -> None:
try:
task = asyncio.ensure_future(_receive())
async for message in broker.subscribe():
await websocket.send(message)
finally:
task.cancel()
await task
这里需要注意:
- 接收消息的任务需要独立运行,与发送消息并发执行
- 必须正确处理任务取消和资源清理
- 当用户断开连接时,会触发CancelledError,确保资源被正确释放
测试与验证
7. 编写测试用例
我们需要验证WebSocket功能是否正常工作:
import asyncio
from quart.testing.connections import TestWebsocketConnection as _TestWebsocketConnection
from chat import app
async def _receive(test_websocket: _TestWebsocketConnection) -> str:
return await test_websocket.receive()
async def test_websocket() -> None:
test_client = app.test_client()
async with test_client.websocket("/ws") as test_websocket:
task = asyncio.ensure_future(_receive(test_websocket))
await test_websocket.send("message")
result = await task
assert result == "message"
安装测试依赖:
poetry add --dev pytest-asyncio
配置pyproject.toml
:
[tool.pytest.ini_options]
asyncio_mode = "auto"
运行测试:
poetry run pytest tests/
进阶思考
目前实现的聊天服务器有以下限制:
- 消息只在单个服务器实例的内存中共享
- 没有用户认证机制
- 前端界面非常基础
对于生产环境,你可以考虑:
- 使用Redis等外部消息代理替代内存实现
- 添加用户认证和房间功能
- 改进前端界面和错误处理
总结
通过本教程,我们实现了一个基础的WebSocket聊天服务器,涵盖了:
- Quart应用的基本结构
- WebSocket路由的实现
- 异步消息代理模式
- 测试WebSocket应用的方法
这为构建更复杂的实时应用打下了良好基础。Quart的ASGI特性使得它非常适合这类实时应用场景。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考