Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/django/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Django-如何实时跟踪用户是否在线/离线?_Django_Django Channels_Django Notification - Fatal编程技术网

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

Django-如何实时跟踪用户是否在线/离线?,django,django-channels,django-notification,Django,Django Channels,Django Notification,我正在考虑使用和Web套接字向iOS/Android和Web应用程序发送实时通知。所以我可能会使用Django频道 我可以使用Django频道实时跟踪用户的在线状态吗?如果是,那么如何在不不断轮询服务器的情况下实现这一点 我正在寻找最佳实践,因为我无法找到任何合适的解决方案 更新: 到目前为止,我尝试了以下方法: 使用Django通道,我实现了一个WebSocket使用者,它在connect上将用户状态设置为“在线”,而当套接字断开连接时,用户状态将设置为“离线”。 最初我想包括'away'状态

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

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

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

更新

到目前为止,我尝试了以下方法: 使用Django通道,我实现了一个WebSocket使用者,它在connect上将用户状态设置为
“在线”
,而当套接字断开连接时,用户状态将设置为
“离线”
。 最初我想包括
'away'
状态,但我的方法无法提供此类信息。 此外,当用户从多个设备使用应用程序时,我的实现也无法正常工作,因为一个设备上的连接可以关闭,但另一个设备上的连接仍然打开;即使用户有另一个打开的连接,状态也将设置为“脱机”

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)
注意

我目前的工作解决方案是使用带有API端点的Django REST框架,让客户端应用程序发送当前状态的HTTP POST请求。 例如,web应用程序跟踪鼠标事件并每隔X秒持续发布
在线
状态,当不再有鼠标事件发布
远离
状态时,当选项卡/窗口即将关闭时,应用程序发送状态为
离线
的POST请求。 这是一个有效的解决方案,取决于我在发送
脱机状态时遇到的浏览器问题,但它可以工作


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

使用WebSocket肯定是更好的方法

与二进制的“在线”/“离线”状态不同,您可以计算连接数:当新的WebSocket连接时,将“在线”计数器增加1,当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)

最好的方法是使用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地址、地理位置等。然后,您可以执行以下操作(基于代码):

如何获取设备标识 有很多图书馆喜欢这样做。它们只是使用一组浏览器参数来生成散列键。这比单独使用会话id要好,因为它的变化不那么频繁

跟踪状态 每次操作后,只需更新
last\u echo
。通过这种方式,您可以确定谁连接了或远离了什么设备



优点:在崩溃、重启等情况下,可以随时重新建立跟踪状态。

是的,您的解决方案有效。如何跟踪“离开”状态?如果将“离开”定义为“用户在过去5分钟内未在任何连接的设备上执行任何操作”,则需要记住上次使用的设备上的最后一次操作的时间。因此,只需使用第二个UserProfile属性,在其中放置最后一个“操作”的时间戳。当然,这也取决于你所说的“用户操作”。发布文本?或者移动鼠标(可以使用JavaScript通过打开的WebSocket将操作传递到后端)?我将等待奖金到期,然后再为您分配
+50
,让其他用户共享其他答案您可以在Django应用程序启动时重置所有用户的计数器。是否有理由将连接状态计数存储在数据库中?我认为没有必要坚持下去。计数不能存储在会话变量或memcache中吗?如何确定设备id?我的意思是,从
scope
变量中,我们可以得到
session
cookie,但不能得到设备id。有一些库可以实现这一点(客户端)。他们通常使用一组浏览器参数(用户\代理等)来构建散列。但正如我所说,您也可以使用
session\u id
,而不是
device\u id
,甚至两者都可以。每个设备的会话id已经是唯一的,但与设备不同的是,每次重新登录后,会话id都会更改,因此如果使用会话id而不是设备id,则您的连接历史可能会变大,并且可能需要在本地删除旧记录。
@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)