Django - Как отслеживать, находится ли пользователь в режиме онлайн/офлайн в режиме реального времени?
Я рассматриваю возможность использования django-уведомлений и веб-сокетов для отправки уведомлений в реальном времени в iOS/Android и веб-приложения. Поэтому я, вероятно, буду использовать 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 с текущим статусом. Например, веб - приложение отслеживает события мыши и постоянно по месту службы online
Статус каждые Х секунд, когда нет больше событий мыши не POST на away
состояние, когда вкладка/окно собирается быть закрыты, приложение отправляет запрос POST со статусом offline
. Это рабочее решение, в зависимости от браузера у меня возникают проблемы при отправке offline
состояния, но он работает.
То, что я ищу, - лучшее решение, которое не требует постоянного опроса сервера.
Ответы
Ответ 1
Использование 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)
Ответ 2
Лучший подход - использование 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. Они просто используют набор параметров браузера для генерации хэш-ключа. Это лучше, чем использовать только идентификатор сеанса, потому что он изменяется менее откровенно.
Отслеживание статуса
После каждого действия вы можете просто обновить last_echo
. Таким образом, вы можете понять, кто подключен или ушел, и с какого устройства.
Преимущество: В случае сбоя, перезагрузки и т.д. Статус отслеживания может быть восстановлен в любое время.