Django-如何实时跟踪用户在线/离线?

时间:2018-08-20 12:43:58

标签: django django-channels django-notification

我正在考虑使用django-notifications和Web套接字向iOS / Android和Web应用程序发送实时通知。所以我可能会使用 Django Channels

我可以使用 Django频道实时跟踪用户的在线状态吗?如果是,那我如何在不不断轮询服务器的情况下实现这一目标?

我正在寻找最佳实践,因为我找不到任何合适的解决方案。

更新

到目前为止,我尝试了以下方法: 我使用Django Channels实现了一个WebSocket使用者,该使用者在连接时会将用户状态设置为'online',而当套接字断开连接时,用户状态将设置为'offline'。 最初,我想包含'away'状态,但是我的方法无法提供这种信息。 另外,当用户从多个设备上使用该应用程序时,我的实现将无法正常工作,因为连接可以在一个设备上关闭,但仍然可以在另一个设备上打开;即使用户具有另一个打开的连接,状态也将设置为'offline'

class MyConsumer(AsyncConsumer):

    async def websocket_connect(self, event):
        # Called when a new websocket connection is established
        print("connected", event)
        user = self.scope['user']
        self.update_user_status(user, 'online')

    async def websocket_receive(self, event):
        # Called when a message is received from the websocket
        # Method NOT used
        print("received", event)

    async def websocket_disconnect(self, event):
        # Called when a websocket is disconnected
        print("disconnected", event)
        user = self.scope['user']
        self.update_user_status(user, 'offline')

    @database_sync_to_async
    def update_user_status(self, user, status):
        """
        Updates the user `status.
        `status` can be one of the following status: 'online', 'offline' or 'away'
        """
        return UserProfile.objects.filter(pk=user.pk).update(status=status)

注意

我当前的工作解决方案是将Django REST Framework与API端点一起使用,以允许客户端应用发送具有当前状态的HTTP POST请求。 举例来说,网路应用程式会追踪滑鼠事件,并每隔X秒不断地追踪online状态;当没有其他滑鼠事件即将关闭标签页/视窗时,则追踪away状态,该应用会发送状态为offline的POST请求。 这是一个可行的解决方案,具体取决于浏览器,在发送offline状态时我遇到问题,但是它可以工作。

我正在寻找的是一种更好的解决方案,不需要不断轮询服务器。

2 个答案:

答案 0 :(得分:10)

使用WebSockets绝对是更好的方法。

您可以计算连接数,而不必具有二进制的“在线” /“离线”状态:当连接新的WebSocket时,将“在线”计数器增加一个,当WebSocket断开连接时,将其减少。这样,当它为0时,用户在所有设备上都处于脱机状态。

类似这样的东西

@database_sync_to_async
def update_user_incr(self, user):
    UserProfile.objects.filter(pk=user.pk).update(online=F('online') + 1)

@database_sync_to_async
def update_user_decr(self, user):
    UserProfile.objects.filter(pk=user.pk).update(online=F('online') - 1)

答案 1 :(得分:5)

最好的方法是使用Websocket。

但是我认为您不仅应该存储状态,还应该存储会话密钥设备标识。如果仅使用计数器,则会丢失有价值的信息,例如,用户在特定时刻连接了什么设备。这在某些项目中很关键。此外,如果发生错误(断开连接,服务器崩溃等),您将无法跟踪与每个设备相关的计数器,并且可能最终需要重置计数器。

我建议您将此信息存储在另一个相关表中:

from django.db import models
from django.conf import settings


class ConnectionHistory(models.Model):
    ONLINE = 'online'
    OFFLINE = 'offline'
    STATUS = (
        (ONLINE, 'On-line'),
        (OFFLINE, 'Off-line'),
    )
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE
    )
    device_id = models.CharField(max_lenght=100)
    status = models.CharField(
        max_lenght=10, choices=STATUS,
        default=ONLINE
    )
    first_login = models.DatetimeField(auto_now_add=True)
    last_echo = models.DatetimeField(auto_now=True)

    class Meta:
        unique_together = (("user", "device_id"),)

这样,您就可以通过每台设备的记录来跟踪其状态以及其他一些信息,例如ip地址,地理位置等。然后您可以执行以下操作(根据您的代码):

@database_sync_to_async
def update_user_status(self, user, device_id, status):
    return ConnectionHistory.objects.get_or_create(
        user=user, device_id=device_id,
    ).update(status=status)

如何获取设备标识

有很多类似https://www.npmjs.com/package/device-uuid的库都在这样做。他们只是使用一堆浏览器参数来生成哈希键。比单独使用会话ID更好,因为它的更改频率较低。

跟踪离开状态

每个操作之后,您只需更新last_echo。通过这种方式,您可以弄清楚是谁连接了或与谁断开了连接以及来自什么设备。


优点:万一发生崩溃,重新启动等情况,可以随时重新建立跟踪状态。

相关问题