用Python和Websockets库构建一个高性能、低延迟的实时消息推送服务

基础的服务端代码:

python 复制代码
import asyncio
import websockets
import json
from datetime import datetime

class MessageServer:
    def __init__(self):
        self.clients = set()  # 存储所有连接的客户端
        
    async def register(self, websocket):
        """注册新客户端"""
        self.clients.add(websocket)
        print(f"新客户端连接 当前连接数: {len(self.clients)}")
        
    async def unregister(self, websocket):
        """注销客户端"""
        self.clients.remove(websocket)
        print(f"客户端断开 当前连接数: {len(self.clients)}")
        
    async def broadcast(self, message, sender_ws=None):
        """广播消息给所有客户端"""
        if self.clients:
            # 过滤掉发送者自己 避免回显
            targets = {client for client in self.clients if client != sender_ws}
            if targets:
                await asyncio.gather(
                    *[client.send(json.dumps(message)) for client in targets],
                    return_exceptions=True
                )
                
    async def handle_client(self, websocket, path):
        """处理客户端连接"""
        await self.register(websocket)
        try:
            async for message in websocket:
                data = json.loads(message)
                # 添加时间戳
                data['timestamp'] = datetime.now().isoformat()
                print(f"收到消息: {data}")
                # 广播给其他客户端
                await self.broadcast(data, websocket)
        except websockets.exceptions.ConnectionClosed:
            pass
        finally:
            await self.unregister(websocket)

# 启动服务器
server = MessageServer()
start_server = websockets.serve(server.handle_client, "localhost", 8765)

print("WebSocket服务器启动在 ws://localhost:8765")
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

客户端代码

python 复制代码
import asyncio
import websockets
import json
import threading

class MessageClient:
    def __init__(self, uri):
        self.uri = uri
        self.websocket = None
        
    async def connect(self):
        """连接服务器"""
        self.websocket = await websockets.connect(self.uri)
        print("连接成功!")
        
    async def send_message(self, message):
        """发送消息"""
        if self.websocket:
            data = {
                'type': 'message',
                'content': message,
                'user': 'user_123'  # 实际项目中应该是真实用户ID
            }
            await self.websocket.send(json.dumps(data))
            
    async def listen(self):
        """监听服务器消息"""
        try:
            async for message in self.websocket:
                data = json.loads(message)
                print(f"[{data.get('timestamp', '')}] {data.get('user', '未知')}: {data.get('content', '')}")
        except websockets.exceptions.ConnectionClosed:
            print("连接已断开")
            
    def start_input_loop(self):
        """处理用户输入"""
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        
        async def input_handler():
            while True:
                message = input()
                if message.lower() == 'quit':
                    break
                await self.send_message(message)
                
        loop.run_until_complete(input_handler())

# 使用示例
async def main():
    client = MessageClient("ws://localhost:8765")
    await client.connect()
    
    # 启动输入线程
    input_thread = threading.Thread(target=client.start_input_loop)
    input_thread.daemon = True
    input_thread.start()
    
    # 监听消息
    await client.listen()

if __name__ == "__main__":
    asyncio.run(main())

性能优化首先是连接池管理。当连接数达到几千甚至上万时 内存占用会很大,解决办法是设置最大连接数限制:

python 复制代码
async def handle_client(self, websocket, path):
    if len(self.clients) >= MAX_CONNECTIONS:
        await websocket.close(code=1013, reason="服务器连接数已满")
        return
    # 其他处理逻辑...

高并发时如果直接广播 会造成阻塞。我现在都用Redis做消息队列了。

python 复制代码
import redis
import asyncio

class RedisMessageServer(MessageServer):
    def __init__(self):
        super().__init__()
        self.redis_client = redis.Redis(host='localhost', port=6379, db=0)
        self.pubsub = self.redis_client.pubsub()
        
    async def start_redis_listener(self):
        """监听Redis消息"""
        self.pubsub.subscribe('chat_messages')
        for message in self.pubsub.listen():
            if message['type'] == 'message':
                data = json.loads(message['data'])
                await self.broadcast(data)

部署的话我推荐用nginx做反向代理

python 复制代码
upstream websocket {
    server 127.0.0.1:8765;
}

server {
    listen 80;
    location /ws {
        proxy_pass http://websocket;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_read_timeout 86400;
    }
}

proxy_read_timeout要设置长一点。不然连接会被nginx强制断开。

生产环境一定要加心跳检测。我用的是每30秒发一次ping:

python 复制代码
async def heartbeat(self, websocket):
    try:
        while True:
            await websocket.ping()
            await asyncio.sleep(30)
    except:
        await self.unregister(websocket)
相关推荐
程序员小远8 小时前
软件测试之单元测试详解
自动化测试·软件测试·python·测试工具·职场和发展·单元测试·测试用例
心无旁骛~9 小时前
python多进程和多线程问题
开发语言·python
星云数灵9 小时前
使用Anaconda管理Python环境:安装与验证Pandas、NumPy、Matplotlib
开发语言·python·数据分析·pandas·教程·环境配置·anaconda
计算机毕设匠心工作室9 小时前
【python大数据毕设实战】青少年抑郁症风险数据分析可视化系统、Hadoop、计算机毕业设计、包括数据爬取、数据分析、数据可视化、机器学习
后端·python
计算机毕设小月哥9 小时前
【Hadoop+Spark+python毕设】智能制造生产效能分析与可视化系统、计算机毕业设计、包括数据爬取、Spark、数据分析、数据可视化、Hadoop
后端·python·mysql
计算机毕设小月哥11 小时前
【Hadoop+Spark+python毕设】中风患者数据可视化分析系统、计算机毕业设计、包括数据爬取、Spark、数据分析、数据可视化、Hadoop
后端·python·mysql
Keep_Trying_Go11 小时前
基于Zero-Shot的目标计数算法详解(Open-world Text-specified Object Counting)
人工智能·pytorch·python·算法·多模态·目标统计
计算机毕设匠心工作室12 小时前
【python大数据毕设实战】强迫症特征与影响因素数据分析系统、Hadoop、计算机毕业设计、包括数据爬取、数据分析、数据可视化、机器学习、实战教学
后端·python·mysql
Trouville0113 小时前
Pycharm软件初始化设置,字体和shell路径如何设置到最舒服
ide·python·pycharm