You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django Websocket随连接用户增多变慢,生产环境异常排查求助

问题分析:Django Channels生产环境Websocket连接慢、推送延迟问题

问题描述

我用Django Channels向前端每秒推送1条消息,本地运行正常,但用Nginx+Gunicorn+Daphne部署到生产环境后,Websocket要尝试2-3次才能连上,而且连接后消息推送速度特别慢。想问这是服务器配置问题还是代码问题?

相关代码

consumer.py

class MyConsumer(AsyncJsonWebsocketConsumer):
    async def connect(self):
        self.room_group_name = 'Live Message'
        self.error_code = 4011
        await self.channel_layer.group_add(self.room_group_name, self.channel_name)
        await self.accept()

    async def disconnect(self, code):
        await self.channel_layer.group_discard(self.room_group_name, self.channel_name)
        await self.close(self.error_code)

    async def send_live_data(self, event):
        data = event.get('value')

        additional_msg = get_additional_msg()
        data['additional_msg'] = json.dumps(additional_msg, default=default)

        try:
            await self.send_json(data)
        except Exception as e:
            await self.disconnect({'code': self.error_code})
            await self.close(self.error_code)

message.py

channel_layer = get_channel_layer()
async def send_data():
    try:
        await (channel_layer.group_send)('Live Message', {
        'type': 'send_live_data',
        'value': {'live_message':json.dumps(message)}})
    except:
        print("Error while sending message ...\n", traceback.format_exc())

class MyWorker(Thread):
    def __init__(self):
        Thread.__init__(self)
    
    def run(self):
        print('Worker is running .....')

        while True:
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
            loop.run_until_complete(send_data())
            loop.close()

            time.sleep(1)

MyWorker().start()

问题根源:代码+配置双重问题

一、代码层面的核心问题

  1. 异步循环滥用
    在MyWorker的run方法中,每次循环都新建并关闭asyncio事件循环,这会产生巨大的性能开销——每次创建循环都要初始化异步资源,关闭时还要清理,直接导致每秒一次的消息发送延迟飙升,这是推送慢的主要原因之一。

  2. 异常处理与参数错误

  • send_data的except未指定具体异常类型,会捕获所有错误,掩盖配置类问题,不利于排查。
  • disconnect方法中调用close时传入字典,而close仅接受整数错误码,这会导致连接关闭逻辑异常,间接引发重连问题。
  1. 序列化冗余
    send_data中已将message用json.dumps序列化,到send_live_data又对additional_msg再次序列化,最后用send_json发送,导致JSON嵌套序列化,既浪费性能,还可能引发前端解析问题。

二、服务器配置层面的可能问题

  1. Nginx Websocket代理配置缺失
    Nginx默认不会维持Websocket长连接,必须添加专属配置:
  • 未转发Upgrade和Connection头,会导致Nginx主动断开连接,引发多次重连。
  • proxy_read_timeout默认60秒过短,容易导致连接超时。
    正确的Nginx配置片段:
location /ws/ {
    proxy_pass http://127.0.0.1:8001;  # Daphne监听地址
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_read_timeout 3600s;
}
  1. Daphne与Gunicorn职责混淆
    Gunicorn仅处理HTTP请求,Websocket请求必须全部转发给Daphne,若让Gunicorn处理Websocket请求,会直接导致连接失败或延迟。

  2. Daphne启动参数不合理
    未设置足够的worker进程或未启用异步优化参数,会导致连接和消息处理能力不足。

修复方案

代码修复

  1. 复用asyncio事件循环
class MyWorker(Thread):
    def __init__(self):
        Thread.__init__(self)
    
    def run(self):
        print('Worker is running .....')
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        try:
            while True:
                loop.run_until_complete(send_data())
                time.sleep(1)
        finally:
            loop.close()
  1. 修正异常处理与序列化
# send_data优化
async def send_data():
    try:
        # 去掉冗余序列化,直接传对象
        await channel_layer.group_send('Live Message', {
            'type': 'send_live_data',
            'value': {'live_message': message}
        })
    except Exception as e:
        print("Error while sending message ...\n", traceback.format_exc())

# send_live_data优化
async def send_live_data(self, event):
    data = event.get('value')
    # 直接传对象,send_json自动序列化
    data['additional_msg'] = get_additional_msg()
    try:
        await self.send_json(data)
    except Exception as e:
        # close仅传整数错误码
        await self.close(self.error_code)

配置检查

  • 确保Nginx的Websocket代理配置正确,重点检查Upgrade/Connection头和超时设置。
  • 确认Daphne独立启动(例如daphne -b 0.0.0.0 -p 8001 myproject.asgi:application),Nginx仅将/ws/路径请求转发给Daphne,其余HTTP请求转发给Gunicorn。
  • 检查服务器防火墙,确保Daphne监听的端口未被拦截。

内容的提问来源于stack exchange,提问作者Django Developer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 03:50:32