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

如何通过Django Channels操作Redis数据库并实现聊天室用户功能

Django Channels + Redis 实现聊天室在线人数与在场用户列表

一、用channels_layer.receive_count显示聊天室访问人数

Channels的Redis通道层提供group_receive_count方法,可直接统计指定组内的活跃连接数(即当前聊天室在线人数),核心逻辑是将每个进入聊天室的用户连接加入对应组,再通过该方法获取数量。

实现步骤:

  1. 复用现有Redis配置
    在settings.py中配置Channels的Redis通道层,确保和Django现有Redis配置一致:

    CHANNEL_LAYERS = {
        "default": {
            "BACKEND": "channels_redis.core.RedisChannelLayer",
            "CONFIG": {
                "hosts": [("127.0.0.1", 6379)],  # 与Django Redis配置保持一致
                # 有密码或指定数据库时补充参数
                # "password": "your_redis_pwd",
                # "db": 0,
            },
        },
    }
    
  2. 用户连接时加入聊天室组
    在Consumer的connect方法中,将用户WebSocket连接绑定到对应聊天室的组:

    # consumers.py
    from channels.generic.websocket import AsyncWebsocketConsumer
    
    class ChatRoomConsumer(AsyncWebsocketConsumer):
        async def connect(self):
            self.room_id = self.scope['url_route']['kwargs']['room_id']
            self.room_group_name = f'chat_room_{self.room_id}'
    
            # 加入聊天室组
            await self.channel_layer.group_add(
                self.room_group_name,
                self.channel_name
            )
            await self.accept()
    
        async def disconnect(self, close_code):
            # 离开聊天室组
            await self.channel_layer.group_discard(
                self.room_group_name,
                self.channel_name
            )
    
  3. 获取并展示在线人数
    在视图或API中,通过group_receive_count获取指定聊天室的人数:

    # views.py
    from channels.layers import get_channel_layer
    from asgiref.sync import async_to_sync
    
    def get_room_online_count(room_id):
        channel_layer = get_channel_layer()
        group_name = f'chat_room_{room_id}'
        # 异步方法转同步调用
        count = async_to_sync(channel_layer.group_receive_count)(group_name)
        return count
    
    # 聊天室列表视图中调用
    def chat_room_list(request):
        rooms = ChatRoom.objects.all()
        for room in rooms:
            room.online_count = get_room_online_count(room.id)
        return render(request, 'chat/room_list.html', {'rooms': rooms})
    

二、进入聊天室时列出当前在场用户信息

Channels组仅维护连接通道,不存储用户详情,需借助Redis单独维护每个聊天室的在线用户集合,存储用户ID、用户名等信息。

实现步骤:

  1. 连接时存储用户信息到Redis
    在Consumer的connect方法中,认证用户后将其信息存入Redis哈希集合(按聊天室区分键名):

    # consumers.py
    import json
    from django.contrib.auth.models import User
    from django_redis import get_redis_connection
    from asgiref.sync import sync_to_async
    
    class ChatRoomConsumer(AsyncWebsocketConsumer):
        async def connect(self):
            # 验证用户登录状态
            if self.scope['user'].is_anonymous:
                await self.close()
                return
    
            self.room_id = self.scope['url_route']['kwargs']['room_id']
            self.room_group_name = f'chat_room_{self.room_id}'
            self.user = self.scope['user']
    
            # 加入聊天室组
            await self.channel_layer.group_add(
                self.room_group_name,
                self.channel_name
            )
    
            # 同步存储用户信息到Redis
            await sync_to_async(self.save_online_user)()
    
            # 获取当前在线用户列表并发送给新用户
            online_users = await sync_to_async(self.get_online_users)()
            await self.send(text_data=json.dumps({
                'type': 'online_users',
                'users': online_users
            }))
    
            await self.accept()
    
        def save_online_user(self):
            redis_conn = get_redis_connection()
            redis_key = f'chat_room:{self.room_id}:online_users'
            # 用通道名作为哈希键,存储用户详情
            redis_conn.hset(redis_key, self.channel_name, json.dumps({
                'id': self.user.id,
                'username': self.user.username,
                'avatar': self.user.profile.avatar.url if hasattr(self.user, 'profile') else ''
            }))
            # 设置过期时间,防止异常断开未清理
            redis_conn.expire(redis_key, 86400)
    
        def get_online_users(self):
            redis_conn = get_redis_connection()
            redis_key = f'chat_room:{self.room_id}:online_users'
            # 解析所有在线用户信息
            users_data = redis_conn.hvals(redis_key)
            return [json.loads(data) for data in users_data]
    
  2. 断开连接时移除用户信息
    在disconnect方法中,从Redis删除当前用户记录,并通知其他成员:

    async def disconnect(self, close_code):
        # 离开聊天室组
        await self.channel_layer.group_discard(
            self.room_group_name,
            self.channel_name
        )
        # 同步删除Redis中的用户信息
        await sync_to_async(self.remove_online_user)()
    
        # 通知所有成员更新用户列表
        online_users = await sync_to_async(self.get_online_users)()
        await self.channel_layer.group_send(
            self.room_group_name,
            {
                'type': 'online_users_update',
                'users': online_users
            }
        )
    
    def remove_online_user(self):
        redis_conn = get_redis_connection()
        redis_key = f'chat_room:{self.room_id}:online_users'
        redis_conn.hdel(redis_key, self.channel_name)
    
  3. 处理用户列表更新通知
    添加消息处理方法,将更新后的用户列表推送给所有成员:

    async def online_users_update(self, event):
        users = event['users']
        await self.send(text_data=json.dumps({
            'type': 'online_users',
            'users': users
        }))
    

前端渲染用户列表

前端WebSocket连接成功后,监听online_users类型消息,渲染在场用户:

const socket = new WebSocket('ws://' + window.location.host + '/chat/' + roomId + '/');

socket.onmessage = function(e) {
    const data = JSON.parse(e.data);
    if (data.type === 'online_users') {
        const userList = document.getElementById('online-users');
        userList.innerHTML = '';
        data.users.forEach(user => {
            const li = document.createElement('li');
            li.innerHTML = `<img src="${user.avatar}" alt="avatar"> ${user.username}`;
            userList.appendChild(li);
        });
    }
};

内容的提问来源于stack exchange,提问作者KimGyeongMin-KR

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 18:45:28