Каналы Django отправляют сообщение другому канальному уровню
позвольте мне упростить мой вопрос в одном предложении: в одном потребителе, как я могу получить доступ к другому потребителю для отправки сообщения?
В принципе, у меня есть два потребителя в отдельных приложениях:
- потребитель note в приложении note для управления системными и грубыми уведомлениями.
- потребитель chat в приложении chat для управления социальными сообщениями .
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)
Я оставлю это сообщение здесь на случай, если кто-то столкнется с этим и ему понадобится помощь, если у вас есть лучший вариант, я готов принять его в качестве ответа.