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
相关产品推荐
相关产品推荐

