Можно ли перечислить каналы, хранящиеся в группе?

Можно ли получить доступ к списку каналов, добавленных в группу, с помощью django-channel?

def consumer(message):
    Group("group_name").add(message.reply_channel)
    channel_list = Group("group_name").???

РЕДАКТИРОВАТЬ:

Я пытаюсь добиться доступа к сеансам всех каналов, например, к списку подключенных пользователей.

До сих пор я использую систему на основе базы данных, которая перечисляет соединения. Но если сервер отключается без выполнения моих потребителей ws_disconnect, объект этих соединений останется в базе данных, и я этого не хочу.

РЕДАКТИРОВАТЬ 2:

Чтобы просмотреть список подключенных пользователей, я нашел django-channels-presence, Я проверю это.

6 ответов

Решение

Я проверял django-channels-presence и мне легко удалось перечислить подключенных пользователей для Group создавая Room (эта ручка Group Управление / создание, channel добавление / удаление...) и это обеспечивает get_users() метод, который позволяет мне достичь того, что я искал.

Он также предоставляет способ очистки добавленных каналов, которые не удаляются в случае сбоя сервера (это не вызовет потребителя ws_disconnect, который используется для удаления этих каналов из группы). Это обеспечивает prune_presence а также prune_room задачи, которые очищают просроченные каналы.

Да, это возможно. И легкий взлом...

# Get channel_layer function
from channels.asgi import get_channel_layer

# passing group_channel takes channel name
channel_layer = get_channel_layer()
ch_group_list = channel_layer.group_channels('<your group name>')

Я нашел это channel_layer.group_channels('<your group name>'), упомянутое выше, не работало на каналах 2. Итак, я решил сохранить необходимую мне информацию в channel_layer, и она заработала.

информация о версии

  • каналы == 2.1.5
  • aioredis == 1.2.0
  • каналы-redis==2.3.2

В моем случае мне нужно знать номер каналов в группе, потому что я запускаю ритм сельдерея в фоновом режиме для продолжения передачи данных в каналы, но когда последний канал в группе отключается, я хочу остановить ритм сельдерея.

Я считаю каналы в группе, сохраняя их в channel_layer.

при подключении

count = getattr(self.channel_layer, self.group_name, 0)
if not count:
    setattr(self.channel_layer, self.group_name, 1)
else:
    setattr(self.channel_layer, self.group_name, count + 1)

при отключении

count = getattr(self.channel_layer, self.group_name, 0)
setattr(self.channel_layer, self.group_name, count - 1)
if count == 1:
    delattr(self.channel_layer, self.group_name)

    # stop my celery beat

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

      from django.db import models
from django.contrib.auth import get_user_model
from asgiref.sync import sync_to_async


# ./models
class Room(models.Model):
    room_name = models.CharField(max_length=150, unique=True)
    users = models.ManyToManyField(get_user_model(), related_name='rooms')

    @classmethod
    @sync_to_async
    def add(cls, room_name, user):
        room, created = cls.objects.get_or_create(room_name=room_name)
        room.users.add(user)
        return created # sockets => join or create



    @classmethod
    @sync_to_async
    def users_count(cls, room_name):
        rooms = cls.objects.filter(room_name=room_name)
        if rooms.exists():
            return rooms.first().users.count()
        return 0

    @classmethod
    @sync_to_async
    def remove_user(cls, user, room_name):
        room = cls.objects.filter(room_name=room_name)
        if room.exists():
            room.users.remove(user)


# ./consumer.py
class YourConsumer(AsyncWebsocketConsumer):

    async def connect(self):
        self.room_name = self.scope['url_route']['kwargs']['room_name']
        self.room_group_name = 'video_%s' % self.room_name
        self.group_users = {self.scope.get('user').id: 1}

        if self.scope.get('user').is_authenticated:
            room = await Room.add(self.room_name, self.scope.get('user'))

        await self.channel_layer.group_add(
            self.room_name,
            self.channel_name
        )

        await self.accept()

    async def disconnect(self, code):
        # Leave room group
        await self.channel_layer.group_discard(
            self.room_group_name,
            self.channel_name
        )
        if self.scope.get('user').is_authenticated:
            await Room.remove_user(self.room_name, self.scope.get('user'))

    async def websocket_receive(self, message):
        count = await Room.users_count(room_name=self.room_name)
        await self.send(json.dumps({
            'type': 'websocket.send',
            'text': 'pong',
            'group': self.room_group_name,
            'room': self.room_name,
            'connections': count
        }))

я создаю объект вAuthMiddlewareStack

      
class TokenAuthMiddleware:
    handler: MessengerHandler = None

    def __init__(self, inner):
        self.inner = inner

    async def generate_object(self):
        if self.handler is None: # just for first time
            self.handler= MessengerHandler()
            await self.handler.import_key()
        return self.handler

    async def __call__(self, scope, send, rec):
        model = await self.generate_object()
        scope["handler"] = model
        return await self.inner(scope, send, rec)

вAsyncWebsocketConsumerсорт

      @property
    def _get_handler(self) -> MessengerHandler:
        return self.scope["handler"]

соединять

       self._get_handler.add_new_channel(self.channel_name, self.user_api.user_id)

Отключить

          async def disconnect(self, close_code):
        if self.user_api.is_active:
            self._get_handler.remove_channel(self.channel_name, self.user_api.user_id)
        pass

MessengerHandler

      class MessengerHandler:

    channels: dict[int, ClientChannels] = {}


    def add_new_channel(self, channel: str, user_id: int):
        ....
    def remove_channel(self, channel: str, user_id: int):
       ....

    def get_user_channels(self, user_id: int, without_channel: str = None) -> list:
...


в противном случае безопасный поток с использованием пакета Django-Redis

обрабатывать каналы

          @staticmethod
    def add_new_channel(channel: str, user_id: int, client_type: str, client_id: int):
        channel_name = f"u_{user_id}_{client_type}_{client_id}"
        cache.set(channel_name, channel, timeout=None)

    @staticmethod
    def remove_channel(channel_type: str, user_id: int, client_id: int):
        channel_name = f"u_{user_id}_{channel_type}_{client_id}"
        cache.delete(channel_name)

    @staticmethod
    def get_user_channels(user_id: int) -> list:
        channel_name = f"u_{user_id}_*"
        values = cache.get_many(cache.keys(channel_name))
        return [v for k, v in values.items()]

Это, вероятно, не очень хорошее решение в целом, и в моем случае оно точно не сократит его в 100% случаев, действительно было предназначено только для конкретного использования, но в любом случае я остановился на этом:

  1. Обратите внимание на метод в файле consumer.py (как его обычно называют в учебниках), прикрепленный к вашему классу потребителей - в моем случае player_manager.
      async_to_sync(self.channel_layer.group_send)
(self.game_id_name,
{
  'type': 'player_manager', #notice this name "player_manager"
  'team': team,
  'name': name,
  'p_id': p_id
})
  1. Перейдите к этому методу, и вы можете добавить переменную в моем случае на канал так:
      if getattr(self.channel_layer, "data", None):
  self.channel_layer.data = {} #this just a dictionary that you can now modify to add data accessible to any one connected to a specific channel group
  1. Затем я отправляю все эти данные и на стороне клиента (веб-страница), после получения, анализирую ее и соответствующим образом обновляю страницу.
Другие вопросы по тегам