Django - как отслеживать, если пользователь онлайн / оффлайн в режиме реального времени?

Я собираюсь использовать django-уведомления и веб-сокеты для отправки уведомлений в режиме реального времени в iOS/Android и веб-приложения. Поэтому я, вероятно, буду использовать Каналы Django.

могу ли я использовать Каналы Django отслеживать онлайн-статус пользователя в реальном времени? Если да, то как я могу добиться этого без постоянного опроса сервера?

Я ищу лучшую практику, так как я не смог найти ни одного подходящего решение.

обновление:

то, что я пробовал до сих пор, - это следующий подход: Используя каналы Django, я реализовал потребитель 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 с конечной точкой API, чтобы клиентские приложения отправляли запрос HTTP POST с текущим статусом. Например, веб-приложение отслеживает события мыши и постоянно публикует online статус каждые X секунд, когда нет больше событий мыши опубликовать away статус, когда вкладка / окно будет закрыта, приложение отправляет запрос POST со статусом offline. Это рабочее решение, в зависимости от браузера у меня есть проблемы при отправке offline статус, но это работает.

то, что я ищу, - это лучшее решение, которое не нуждается в постоянном опросе сервера.

2 ответов


использование WebSockets, безусловно, лучший подход.

вместо того, чтобы иметь двоичный статус"online"/" offline", вы можете подсчитать соединения: когда новый WebSocket подключается, увеличьте счетчик" online " на один, когда 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)

лучший подход-использование Websockets.

но я думаю, что вы должны хранить не только статус, но и сеансовый ключ или идентификация устройства. Если вы используете только счетчик, вы теряете ценную информацию, например, с какого устройства пользователь подключен в данный момент. Это ключевой момент в некоторых проектах. Кроме того, если что-то не так (отключение, сбои сервера и т. д.), Вы не сможете отслеживать какой счетчик связан с каждым устройством и, вероятно, вам нужно будет сбросить счетчик в конце.

Я рекомендую вам сохранить эту информацию в другой связанной таблице:

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. Они просто используют набор параметров браузера для создания хэш-ключа. Это лучше, чем использовать только идентификатор сессии, потому что он меняется меньше frencuently.

отслеживание до статус

после каждого действие, вы можете просто обновить last_echo. Таким образом, вы можете выяснить, кто связан или далеко и от чего устройство.


преимущество: в случае аварии, перезагрузки и т. д. Статус отслеживания может быть восстановлен в любое время.