Как получить идентификаторы моделей, на которые подписаны через определенный канал - PullRequest
0 голосов
/ 23 апреля 2019

Как получить список всех моделей ActiveRecord, на которые в настоящее время подписаны через определенный канал ActionCable?

Ответы [ 2 ]

1 голос
/ 23 апреля 2019

Извините, что даю вам ответ, который вы не хотите, но ...:

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

Почему?

Парадигма pub / sub предназначена для отвода этих деталей, что позволяет осуществлять горизонтальное масштабирование таким образом, чтобы различные узлы управляли своими собственными списками подписок.

Конечно, когда вы запускаете одно приложение в одном процессе, вы можете извлечь эту информацию, но в тот момент, когда вы увеличиваете масштаб, используя больше процессов / машин, эта информация распространяется и становится недоступной. больше.

Пример

Например, при использовании механизма публикации / подсистемы йода (подробности см. на сервере Ruby iodine WebSocket / HTTP ):

  • Каждый процесс управляет собственным списком клиентов.

  • Каждый процесс является «клиентом» в главном / корневом процессе.

  • Каждый основной / корневой процесс является клиентом на сервере Redis (при условии, что Redis используется).

Допустим, вы запускаете два йодных "динамо" на Heroku, каждый с 16 рабочими, затем:

  • Redis видит максимум двух клиентов на канал.

  • Каждый из двух основных процессов видит максимум 16 клиентов на канал.

  • Каждый процесс видит только клиентов, подключенных к этому конкретному процессу.

Как видите, запрашиваемая вами информация нигде не доступна. Реализация pub / sub распространяется на разные машины. Каждый процесс / машина управляет только небольшой частью списка паба / суб-клиента.

РЕДАКТИРОВАТЬ (1) - ответ на обновленный вопрос

Существует три возможных подхода к решению этого вопроса:

  1. клиентское решение;

  2. решение на стороне сервера; и

  3. ленивый (недействительный) подход.

В качестве решения на стороне клиента клиент может зарегистрироваться в глобальном "канале уведомлений сервера". Когда появляется сообщение «re-authenticate», клиент должен повторно пройти аутентификацию, инициируя генерацию уникального токена на своем уникальном соединении.

A решение на стороне сервера требует подключения на стороне сервера для прослушивания глобального "канала-уведомлений сервера". Затем объект подключения пересчитает токен аутентификации и передаст уникальное сообщение клиенту.

Подход lazy-invalidation состоит в том, чтобы просто аннулировать все токены. Подключенные клиенты будут оставаться на связи (пока они не закроют браузер, не закроют свой компьютер или не выйдут из приложения). Клиенты должны будут повторно пройти аутентификацию при установлении нового соединения.

Примечание (добавлено, как описано в комментариях):

Единственное решение, которое решает сценарий "гремящего стада", - это решение для ленивых / аннулирования.

Любое другое решение вызовет скачок сетевого трафика и загрузки ЦП, поскольку все подключенные клиенты будут обрабатывать событие в одно и то же время.

Осуществление

С ActionCable решение на стороне клиента может быть проще в реализации. Его дизайн и документация очень ориентированы на «толчок». Они часто используют подход обработки на стороне клиента.

На йод , подписки на стороне сервера просто требуют, чтобы block был передан методу client.subscribe . Это создает клиентскую подписку с событием, которое выполняется на сервере (вместо сообщения, отправленного клиенту).

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

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

0 голосов
/ 26 апреля 2019

ВНИМАНИЕ: Пожалуйста, смотрите @ Myst ответ и связанные комментарии.Приведенный ниже ответ не рекомендуется при масштабировании за пределы одного экземпляра сервера.

ПАТЧ, НЕОБХОДИМЫЙ ДЛЯ РАЗРАБОТКИ И ИСПЫТАНИЯ ОКРУЖАЮЩЕЙ СРЕДЫ

module ActionCable
  module SubscriptionAdapter
    class SubscriberMap
      def get_subscribers
        @subscribers
      end
    end
   end
end

КОД ДЛЯ ПОЛУЧЕНИЯ ИДЕНТИФИКАЦИЙ МОДЕЛЕЙ, ПОДПИСАННЫХ НА

pubsub = ActionCable.server.pubsub

if Rails.env.production?
  channel_with_prefix = pubsub.send(:channel_with_prefix, ApplicationMetaChannel.channel_name)
  channels = pubsub.send(:redis_connection).pubsub('channels', "#{channel_with_prefix}:*")
  subscriptions = channels.map do |channel|
    Base64.decode64(channel.match(/^#{Regexp.escape(channel_with_prefix)}:(.*)$/)[1])
  end
else #DEVELOPMENT or Test Environment: Requires patching ActionCable::SubscriptionAdapter::SubscriberMap
  subscribers = pubsub.send(:subscriber_map).get_subscribers.keys

  subscriptions = []
  subscribers.each do |sid|
    next unless sid.split(':').size === 2
    channel_name, encoded_gid = sid.split(':')
    if channel_name === ApplicationMetaChannel.channel_name
      subscriptions << Base64.decode64(encoded_gid)
    end
  end

end

# the GID URI looks like that: gid://<app-name>/<ActiveRecordName>/<id>
gid_uri_pattern = /^gid:\/\/.*\/#{Regexp.escape(SomeModel.name)}\/(\d+)$/
some_model_ids = subscriptions.map do |subscription|
  subscription.match(gid_uri_pattern)
  # compacting because 'subscriptions' include all subscriptions made from ApplicationMetaChannel,
  # not just subscriptions to SomeModel records
end.compact.map { |match| match[1] }
...