FastAPI 学习之路(五十二)WebSockets(八)接受/发送json格式消息

前面我们发送的大多数都是text类型的消息,对于text消息来说,后端处理出来要麻烦的多,那么我们可以不可以传递json格式的数据,对于前后端来说都比较友好,答案是肯定的,我们需要做下处理。

首先,我们在websocket管理器中增加处理json格式消息的方法:

from typing import List, Dict

from starlette.websockets import WebSocket


class ConnectionManager:

    def __init__(self):
        """存放链接"""
        self.active_connections: List[Dict[str, WebSocket]] = []

    async def connect(self, user: str, ws: WebSocket):
        """链接"""
        await ws.accept()
        self.active_connections.append({"user": user, "ws": ws})

    async def disconnect(self, user: str, ws: WebSocket):
        """断开链接,移除"""
        self.active_connections.remove({"user": user, "ws": ws})

    @staticmethod
    async def send_personal_message(message: str, ws: WebSocket):
        """发送所有人消息"""
        await ws.send_text(message)

    async def send_other_message(self, message: dict, user: str):
        """发送个人消息"""
        for coon in self.active_connections:
            if coon["user"] == user:
                await coon["ws"].send_json(message)

    async def broadcast(self, data: str):
        """广播"""
        for conn in self.active_connections:
            await conn["ws"].send_text(data)

    async def broadcast_json(self, data: dict):
        """广播json数据"""
        for conn in self.active_connections:
            await conn["ws"].send_json(data)

接口中如何接收json数据呢,新增接口如下:

@app.websocket("/ws/json/{user}")
async def websocket_json_data(
        websocket: WebSocket,
        user: str,
        cookie_or_token: str = Depends(get_cookie_or_token)
):
    """发送接收json数据(前面一对一接口其实就是发送的json数据)"""
    await ws_manager.connect(user, websocket)
    try:
        while True:
            data = await websocket.receive_json()
            send_user = data["username"]
            if send_user:
                await ws_manager.send_other_message(data, send_user)
            else:
                await ws_manager.broadcast_json(data)
    except WebSocketDisconnect as e:
        await ws_manager.disconnect(user, websocket)

前端也要相对应的修改:
 

<!DOCTYPE html>
<html>
<head>
    <title>Chat</title>
</head>
<body>
<h1>WebSocket 聊天</h1>
<form action="" onsubmit="sendMessage(event)">
    <input type="text" id="messageText" autocomplete="off"/>
      <input type="text" id="username" autocomplete="off"/>
    <button>Send</button>
</form>
<button onclick="logout()">退出</button>
<ul id='messages'>
</ul>
<script>
    var  token=window.localStorage.getItem("token")
    if (token==null ){
        window.location.href="/login"
    }
    var ws = new WebSocket("ws://localhost:8000/ws/json/"+token+"?token="+token);

    ws.onmessage = function (event) {

        var messages = document.getElementById('messages')

        var message = document.createElement('li')
        let receiveJson = JSON.parse(event.data);
        console.log(receiveJson)
        var content = document.createTextNode(receiveJson.username+"说:"+receiveJson.messageText)

        message.appendChild(content)

        messages.appendChild(message)

    };

    function sendMessage(event) {

        var input = document.getElementById("messageText")
        var username = document.getElementById("username")
        let message = {messageText: input.value, username:username.value};
        let messageJson = JSON.stringify(message);
        ws.send(messageJson);
        // input.value = ''
        event.preventDefault()

    }
    function logout() {
        window.localStorage.removeItem("token")
        window.location.href='/login'
    }
</script>

</body>

</html>

部署看效果,因为发送给后端的数据格式是前端拼的json,所以页面上只需要填文本即可:

 

<think>嗯,用户问的是如何在FastAPI中实现心跳包机制或者保持连接活跃的最佳实践。首先,我需要回忆一下FastAPI的相关知识。心跳包通常用于维持长连接的活跃状态,防止被防火墙或路由器断开。在HTTP层面,这可能涉及到轮询或者WebSocket。不过FastAPI本身是基于ASGI的,支持异步,所以可能有不同的方法。首先,用户可能需要的是HTTP长轮询,或者是WebSocket的心跳机制。对于HTTP来说,保持连接活跃可以通过定期发送请求来实现,比如客户端每隔一段时间向服务器发送一个心跳请求,服务器响应确认。但这样可能会增加服务器的负担,尤其是当客户端数量很大时。然后是WebSocket,FastAPI支持WebSocket路由。在WebSocket连接中,服务器和客户端可以双向通信,这时候维持心跳通常更高效。WebSocket协议本身有心跳机制(ping/pong帧),但可能需要手动处理,尤其是在某些浏览器或客户端不支持自动ping/pong的情况下。 接下来,我需要考虑如何在FastAPI中实现这两种情况。对于HTTP心跳,可以创建一个简单的端点,比如`/heartbeat`,客户端定期访问这个端点。服务器端只需要返回一个简单的响应,比如当前时间戳或者状态信息。这应该不难,用普通的GET路由就可以实现。 对于WebSocket的心跳,情况稍微复杂一些。在WebSocket路由的处理函数中,可能需要设置一个循环,定期向客户端发送ping消息,并等待pong响应。如果在一定时间内没有收到响应,就关闭连接。同时,客户端也需要处理来自服务器的ping消息,并回复pong。这需要异步处理,因为FastAPI的WebSocket是基于异步的。 另外,用户可能关心的是最佳实践,所以需要考虑性能、资源管理以及错误处理。例如,使用后台任务来定期检查连接状态,或者在WebSocket处理中使用超时机制。此外,可能还需要处理异常,比如客户端突然断开连接的情况,避免服务器资源泄漏。 还有,用户可能没有明确说明是HTTP还是WebSocket的心跳,所以需要分别给出两种场景的解决方案。同时,可能还需要提到第三方库或中间件,比如使用websockets库中的ping_interval和ping_timeout参数,但这些可能是在建立WebSocket连接时的配置选项。 另外,是否需要使用数据库或者缓存来跟踪连接状态?比如记录每个连接的最后活跃时间,然后定时清理不活跃的连接。不过这可能超出了心跳机制本身的范围,属于连接管理的部分。还需要考虑的是,FastAPI的中间件是否可以用来实现心跳机制。例如,通过中间件记录每个请求的时间,从而判断连接是否活跃。但这种方法可能不够直接,特别是对于WebSocket来说,中间件可能无法捕获到每个ping/pong帧。 总结一下,可能的步骤是:1.HTTP心跳端点:创建一个简单的GET路由,返回状态。2.WebSocket心跳处理:在WebSocket路由中实现异步循环,发送ping并等待pong。 3.使用异步任务或超时机制来管理连接状态。 4.异常处理和资源释放,确保连接断开时正确清理。5.可能的配置选项,比如设置ping间隔和超时时间。 然后,需要考虑代码示例的正确性。比如在WebSocket路由中使用`await websocket.receive_text()`来处理客户端发送消息,同时使用`asyncio.sleep`来定时发送ping。另外,处理客户端断开时,需要捕获异常,比如`WebSocketDisconnect`。 可能的问题点:在WebSocket循环中,如何同时处理消息接收和定时发送ping?可能需要使用异步任务或者同时处理多个异步操作。例如,可以启动一个后台任务来发送ping,而主循环继续接收消息。但需要注意任务的管理和取消。 此外,是否需要考虑不同的客户端实现?比如某些客户端可能不支持手动处理ping/pong,这时候服务器端的处理方式是否需要调整。最后,最佳实践可能包括合理设置心跳间隔,避免过于频繁增加负载,同时也要避免间隔过长导致连接被断开。另外,日志记录和监控连接状态也是重要的,以便及时发现和处理问题。</think>在FastAPI中实现心跳包机制需要根据协议类型选择不同方案,以下是两种典型场景的实践方法: ### 一、HTTP长连接心跳方案 ```python from fastapi import APIRouter, Response import time router = APIRouter() @router.get("/heartbeat") async def keep_alive(): # 返回当前时间戳作为活跃状态标识 return {"timestamp": time.time(), "status": "active"} ``` 客户端可定时请求`/heartbeat`接口(如间隔30秒),通过响应中的时间戳验证连接有效性[^1] ### 二、WebSocket双工心跳方案 ```python from fastapi import WebSocket import asyncio async def websocket_heartbeat(websocket: WebSocket): await websocket.accept() try: while True: # 发送Ping并等待Pong响应 await asyncio.wait_for(websocket.send_json({"type": "ping"}), timeout=10) pong = await asyncio.wait_for(websocket.receive_json(), timeout=15) if pong.get("type") != "pong": raise ConnectionError("Invalid heartbeat response") await asyncio.sleep(30) # 心跳间隔 except (asyncio.TimeoutError, ConnectionError): await websocket.close(code=1001) ``` ### 三、最佳实践建议 1. **双向验证机制**:同时实现客户端主动上报和服务端主动探测两种模式 2. **异常熔断**:连续3次心跳失败立即断开连接 3. **资源释放**:使用`try/finally`确保关闭连接时释放相关资源 4. **动态间隔调整**:根据网络质量自动调节心跳间隔 $$ \text{理想间隔} = \frac{\text{平均RTT}}{2} + 3\sigma $$ 其中$\sigma$为网络延迟标准差[^2] ### 四、性能优化技巧 1. 使用Redis Stream记录心跳状态 2. 对WebSocket连接实施分片管理 3. 采用二进制协议替代JSON减少传输开销
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值