web-dev-qa-db-ja.com

Django-ユーザーがオンラインかオフラインかをリアルタイムで追跡する方法は?

Django-notifications とWebソケットを使用して、リアルタイム通知をiOS/AndroidアプリとWebアプリに送信することを検討しています。したがって、おそらくDjango Channelsを使用します。

Django Channelsを使用して、ユーザーのオンラインステータスをリアルタイムで追跡できますか?はいの場合、サーバーを定期的にポーリングせずにこれをどのように達成できますか?

適切な解決策を見つけることができなかったので、ベストプラクティスを探しています。

[〜#〜]更新[〜#〜]

これまでに試みたのは次のアプローチです:Djangoチャネルを使用して、接続時にユーザーステータスを'online'に設定するWebSocketコンシューマーを実装し、ソケットがユーザーステータスから切断されると'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)

[〜#〜]ノート[〜#〜]

私の現在の作業ソリューションは、APIエンドポイントでDjango RESTフレームワークを使用して、クライアントアプリが現在のステータスでHTTP POSTリクエストを送信できるようにします。たとえば、Webアプリはマウスイベントを追跡し、マウスイベントがない場合は常にX秒ごとにPOST onlineステータスPOST awayステータス。タブ/ウィンドウが閉じようとしているときに、アプリはPOSTリクエストをステータスofflineで送信します。これはIS機能するソリューションであり、ブラウザによってはofflineステータスの送信時に問題が発生しますが、機能します。

私が探しているのは、常にサーバーをポーリングする必要がないより良いソリューションです。

15
Fabio

WebSocketsを使用する方が間違いなく優れたアプローチです。

バイナリの「オンライン」/「オフライン」ステータスを使用する代わりに、接続をカウントすることができます。新しい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)
12
C14L

最善のアプローチはWebsocketを使用することです。

ただし、ステータスだけでなく、セッションキーまたはデバイスID。カウンターのみを使用すると、たとえば、特定の瞬間にユーザーが接続しているデバイスなどの貴重な情報が失われます。これは、一部のプロジェクトでは重要です。さらに、何か問題が発生した場合(切断、サーバークラッシュなど)、各デバイスに関連するカウンターを追跡できなくなり、おそらく最後にカウンターをリセットする必要があります。

この情報を別の関連テーブルに保存することをお勧めします。

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)

デバイスIDを取得する方法

https://www.npmjs.com/package/device-uuid のように行うライブラリはたくさんあります。ブラウザーのパラメーターのバンドルを使用してハッシュキーを生成するだけです。変更の頻度が少ないため、セッションIDのみを使用するよりも優れています。

追跡awayステータス

アクションの後に、last_echoを更新するだけです。このようにして、誰がどのデバイスに接続しているか、または離れているかを把握できます。


利点:クラッシュや再起動などの場合、追跡のステータスはいつでも再確立できます。

6
ddiazp