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

如何用Django Channels实现全站用户在线/离线状态追踪?

Django Channels 全站用户在线/离线状态追踪方案

1. 能否实现全站追踪?

完全可以。Django Channels 支持通过 WebSocket/HTTP 长连接结合通道层(Channel Layer)实现全局用户状态追踪,核心是统一管理用户连接,并配合后端存储(如 Redis)维护在线用户列表。

2. 是否需要为每个页面建立 WebSocket 连接?

不需要。推荐单全局 WebSocket 连接方案:

  • 用户打开任意页面时,前端尝试建立持久化连接,若已存在则复用(可通过 localStorage 标记连接状态)。
  • 多标签页场景下,用浏览器 Broadcast Channel API 共享连接状态,避免重复建连。
  • 连接意外断开时,前端自动触发重连,保证状态追踪连续性。

3. 实时展示用户在线/离线状态的实现步骤

后端实现(基于 Django Channels)

  • 定义全局 WebSocket 消费者:
    处理用户连接、断开、心跳事件,同步状态并广播更新:
    from channels.generic.websocket import AsyncWebsocketConsumer
    from channels.db import database_sync_to_async
    from django.contrib.auth.models import User
    import json
    from asgiref.sync import sync_to_async
    
    class OnlineStatusConsumer(AsyncWebsocketConsumer):
        async def connect(self):
            self.user = self.scope['user']
            if self.user.is_authenticated:
                # 将用户加入全局在线组
                await self.channel_layer.group_add('online_users', self.channel_name)
                # 更新用户在线状态(假设用户有 Profile 模型,含 is_online 字段)
                await sync_to_async(self.user.profile.update)(is_online=True)
                # 广播上线事件
                await self.channel_layer.group_send(
                    'online_users',
                    {'type': 'user_status_update', 'user_id': self.user.id, 'status': 'online'}
                )
            await self.accept()
    
        async def disconnect(self, close_code):
            if self.user.is_authenticated:
                await self.channel_layer.group_discard('online_users', self.channel_name)
                await sync_to_async(self.user.profile.update)(is_online=False)
                # 广播下线事件
                await self.channel_layer.group_send(
                    'online_users',
                    {'type': 'user_status_update', 'user_id': self.user.id, 'status': 'offline'}
                )
    
        async def user_status_update(self, event):
            # 向所有在线客户端推送状态更新
            await self.send(text_data=json.dumps({
                'user_id': event['user_id'],
                'status': event['status']
            }))
    
        async def receive(self, text_data):
            # 处理心跳包,维持连接
            text_data_json = json.loads(text_data)
            if text_data_json.get('type') == 'heartbeat':
                await self.send(text_data=json.dumps({'type': 'heartbeat_ack'}))
    
  • 配置路由:
    在 routing.py 中添加全局 WebSocket 路由:
    from django.urls import re_path
    from . import consumers
    
    websocket_urlpatterns = [
        re_path(r'ws/online-status/$', consumers.OnlineStatusConsumer.as_asgi()),
    ]
    
  • 状态存储优化:
    优先用 Redis 存储在线用户列表(读写效率远高于数据库),同时配合定时任务:扫描用户最后活跃时间,标记超过10分钟未发送心跳的用户为离线(避免异常断开导致状态未更新)。

前端实现

  • 全局连接管理:
    在网站公共 JS 文件中实现连接逻辑,确保任意页面都能复用连接:
    let socket;
    let heartbeatInterval;
    
    function initOnlineStatusSocket() {
        // 避免重复创建连接
        if (socket && socket.readyState === WebSocket.OPEN) return;
    
        socket = new WebSocket(`ws://${window.location.host}/ws/online-status/`);
    
        socket.onopen = () => {
            console.log('在线状态连接建立');
            // 每30秒发送心跳包
            heartbeatInterval = setInterval(() => {
                socket.send(JSON.stringify({type: 'heartbeat'}));
            }, 30000);
        };
    
        socket.onmessage = (e) => {
            const data = JSON.parse(e.data);
            if (data.type === 'heartbeat_ack') return;
            // 更新页面中对应用户的状态UI
            updateUserStatus(data.user_id, data.status);
        };
    
        socket.onclose = () => {
            console.log('连接断开,尝试重连');
            clearInterval(heartbeatInterval);
            setTimeout(initOnlineStatusSocket, 3000);
        };
    }
    
    function updateUserStatus(userId, status) {
        // 匹配页面中带 data-user-id 属性的状态元素
        const statusElement = document.querySelector(`.user-status[data-user-id="${userId}"]`);
        if (statusElement) {
            statusElement.className = `user-status ${status}`;
            statusElement.textContent = status === 'online' ? '在线' : '离线';
        }
    }
    
    // 页面加载时初始化连接
    document.addEventListener('DOMContentLoaded', initOnlineStatusSocket);
    
  • UI 实时更新:
    在需要展示用户状态的页面,为状态元素添加 data-user-id 属性,前端收到广播后自动匹配更新样式/文本。

异常情况处理

  • 浏览器崩溃、网络异常断开:若 disconnect 未触发,依赖心跳超时机制标记离线。
  • 多标签页:用 Broadcast Channel API 同步连接状态,同一用户仅保留一个有效连接。

内容的提问来源于stack exchange,提问作者Bahrom Najmiddinov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 11:50:12