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

Django Channels v1升级v3后数据库访问阻塞问题求助

问题背景

我正在将现有项目从Channels v1升级到v3,目前遇到了流程阻塞的问题,相关代码如下:

相关代码

Consumer代码

class MultiplexConsumer(WebsocketConsumer):

    def connect(self):
        print(11111111, self.scope)
        logger.debug("Received new connection request from {}", self.channel_name)
        WebSocketsConnection.objects.create(channel_id=self.channel_name, last_pong=datetime.datetime.now())
        print(444444444)
        self.accept()
        print(555555555)

   ...

模型代码

class WebSocketsConnection(models.Model):
    """
        This model exists because Django channels does not provide a default way to handle user presence.
    """
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    channel_id = models.CharField(max_length=50)
    user = models.ForeignKey(User, null=True, blank=True, on_delete=models.deletion.SET_NULL)
    last_pong = models.DateTimeField(auto_now_add=True)
    type = models.CharField(max_length=30, null=True, blank=True)
    data = JSONField(default={})

    def ping(self):
        channel_layer = get_channel_layer()
        async_to_sync(channel_layer.send)(self.channel_id, create_action('API', APIConstants.PING), immediately=True)

    def pong(self):
        self.last_pong = timezone.now()
        self.save()

    def get_groups(self):
        groups = []
        for sub in WebSocketsConnectionSubscription.objects.filter(ws_connection=self):
            groups.append(sub.get_group_id())
        return groups

    def _add_subscription(self, data, sub_type):
        try:
            sub = WebSocketsConnectionSubscription.objects.get(ws_connection=self, type=sub_type)
            sub.data = data
            sub.save()
        except WebSocketsConnectionSubscription.DoesNotExist:
            WebSocketsConnectionSubscription.objects.create(ws_connection=self, data=data, type=sub_type)

问题现象

代码执行到WebSocketsConnection.objects.create语句时会永久阻塞,永远不会打印后续的444444444,删掉该模型创建语句后流程就可以正常执行。已经检查过WebSocketsConnection模型没有绑定任何模型信号,以下是可能的原因及验证方案:

可能的原因
  • 同步Consumer线程池耗尽:Channels v3默认给同步WebsocketConsumer分配的工作线程池数量极小,默认值为4。如果同一时间有多个连接同时触发数据库操作,会直接占满线程池导致后续数据库操作永久挂起。可以调整配置项CHANNELS_THREADS调大线程池数量验证。
  • 同步数据库操作未适配ASGI上下文:ASGI模式下同步数据库操作如果未走Channels提供的数据库同步适配逻辑,容易出现IO挂起。可以将create操作改为用database_sync_to_async封装调用:
from channels.db import database_sync_to_async

# 如果改为异步Consumer,调用方式如下
await database_sync_to_async(WebSocketsConnection.objects.create)(
    channel_id=self.channel_name, 
    last_pong=timezone.now()
)
  • 事务配置不兼容:如果项目配置了ATOMIC_REQUESTS = True,该配置仅适配传统HTTP请求,不会自动处理Websocket请求的事务生命周期,会导致Websocket上下文内的数据库写操作一直等待事务提交,表现为永久阻塞。可以尝试在create语句后手动调用from django.db import transaction; transaction.commit()验证。
  • JSONField可变默认值触发锁等待:你当前的data字段设置了default={},Python可变对象作为模型字段默认值会导致所有实例共享同一个字典对象,在多连接并发写入的场景下,很容易触发数据库行锁等待,尤其是PostgreSQL数据库下该问题出现概率极高。将默认值改为default=dict传入可调用对象即可解决。
  • 时间类型不匹配导致驱动挂起:create时传入的是不带时区的datetime.datetime.now(),但模型pong方法里用的是带时区的timezone.now(),如果数据库开启了时区强制校验,部分数据库驱动不会抛出类型错误,而是直接挂起等待输入适配。将create时传入的时间改为timezone.now()验证。

内容的提问来源于stack exchange,提问作者James Lin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 12:18:02