Каналы Django отправляют сообщение другому канальному уровню

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


В принципе, у меня есть два потребителя в отдельных приложениях:

  1. потребитель note в приложении note для управления системными и грубыми уведомлениями.
  2. потребитель chat в приложении chat для управления социальными сообщениями
  3. .
application = ProtocolTypeRouter({
  "http": django_asgi_app,
  "websocket": AllowedHostsOriginValidator(
        SessionMiddlewareStack(
            AuthMiddlewareStack(
                URLRouter(
                    [
                        path('ws/chat/<int:pk>/', ChatConsumer.as_asgi()),
                        path('ws/note/<int:pk>/', NoteConsumer.as_asgi()),
                    ]
                )
            )
        )
    ),
})

Я реализую функцию online status. она работает следующим образом:

нот ws соединение устанавливает на каждой странице, пока вы находитесь на нашем сайте, у вас есть уведомление ws для получения уведомления. это соответствует идее online, поэтому мы можем сохранить онлайн запись в True в db на нот ws соединение, и False при отключении, чтобы указать онлайн статус.

class NoteConsumer(AsyncJsonWebsocketConsumer):
    @database_sync_to_async
    def noteOnline(self):
        user = User.objects.get(pk=int(self.scope['user'].pk))
        user.chatProfile.online = True
        user.save()
    
    @database_sync_to_async
    def noteOffline(self):
        user = User.objects.get(pk=int(self.scope['user'].pk))
        user.chatProfile.online = False
        user.save()

    async def connect(self):
        # ...
        await self.noteOnline()

    async def disconnect(self):
        # ...
        await self.noteOffline()

В представлении друзей чата, пользователь может захотеть узнать, вышел ли собеседник из системы или нет, без реализации ws, информация не будет обновляться немедленно, нужно будет обновлять, мне это не нравится. поэтому я хочу добавить метод обмена сообщениями в потребителе чата:

class ChatConsumer(AsyncJsonWebsocketConsumer):
    async def online_status(self, event):
        # print(f"{event['user']} online to {self.room_name}")
        await self.send(text_data=json.dumps({
            'type': 'online', 
            'message': event['message'], 
            'user': event['user'], 
        }))

что позволяет нам отправлять уведомления о состоянии чата в чат-сокет, в chat.consumers

class NoteConsumer(AsyncJsonWebsocketConsumer):
    # ...

    # try to get layer from ChatConsumer, in my mind,
    # get_channel_layer takes first arg as unique 
    # identifier of the socket layer and will find 
    # corresponding one for me, but seems not working 
    @database_sync_to_async
    def noteOnline(self):
        user = User.objects.get(pk=int(self.scope['user'].pk))
        user.chatProfile.online = True
        user.save()
        for cp in user.chatProfile.getRecentContact():
            # print(f'{user} connect to {self.room_name}')
            # async_to_sync(get_channel_layer)().group_send(
            get_channel_layer().group_send(
                'chat_{}'.format(cp.pk), 
                {
                    'type': 'online_status', 
                    'message': 'online', 
                    'user': user.pk, 
                }
            )

    @database_sync_to_async
    def noteOffline(self):
        user = User.objects.get(pk=int(self.scope['user'].pk))
        user.chatProfile.online = False
        user.save()
        for cp in user.chatProfile.getRecentContact():
            get_channel_layer().group_send(
                'chat_{}'.format(cp.pk), 
                {
                    'type': 'online_status', 
                    'message': 'offline', 
                    'user': user.pk, 
                }
            )

спасибо вам большое за то, что читаете так долго, код выше требует немного больше объяснений:

Архитектура сокетов

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

chat profile iteration получает пк от who I talks to recently, и отправляет сообщение на их receiving socket


Я сомневаюсь, что что-то пошло не так, но кажется, что online_status никогда не вызывается. Мне интересно, почему так происходит, и я буду очень благодарен за вашу помощь.

Я ходил вокруг с channel_layer_alias, но это не сработало, похоже, что мое предыдущее мышление было неправильным: as long as you have room name, you can access to the layer anywhere with get_channel_layer

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

class ChatConsumer(AsyncJsonWebsocketConsumer):
    @staticmethod
    def send_online(sender_pk, receiver_pk):
        channel_layer = get_channel_layer()
        async_to_sync(channel_layer.group_send)(
            'chat_{}'.format(receiver_pk), 
            {
                'type': 'online_status', 
                'message': 'online', 
                'user': sender_pk, 
            }
        )

    @staticmethod
    def send_offline(sender_pk, receiver_pk):
        channel_layer = get_channel_layer()
        async_to_sync(channel_layer.group_send)(
            'chat_{}'.format(receiver_pk), 
            {
                'type': 'online_status', 
                'message': 'offline', 
                'user': sender_pk, 
            }
        )
class NoteConsumer(AsyncJsonWebsocketConsumer):
    @database_sync_to_async
    def noteOnline(self):
        from chat.consumers import ChatConsumer

        user = User.objects.get(pk=int(self.scope['user'].pk))
        user.chatProfile.online = True
        user.save()
        for cp in user.chatProfile.getRecentContact():
            ChatConsumer.send_online(user.pk, cp.pk)
            

    @database_sync_to_async
    def noteOffline(self):
        from chat.consumers import ChatConsumer

        user = User.objects.get(pk=int(self.scope['user'].pk))
        user.chatProfile.online = False
        user.save()
        for cp in user.chatProfile.getRecentContact():
            ChatConsumer.send_offline(user.pk, cp.pk)

Я оставлю это сообщение здесь на случай, если кто-то столкнется с этим и ему понадобится помощь, если у вас есть лучший вариант, я готов принять его в качестве ответа.

Вернуться на верх