Должно ли отправленное сервером событие всегда запускаться независимо от того, на какой странице находится пользователь? - PullRequest
0 голосов
/ 22 февраля 2020

Я довольно новичок в SSE, поэтому не стесняйтесь, дайте мне знать, если я неправильно понял цель, и есть намного лучший способ реализовать то, что я хочу!

У меня есть работающий SSE, который каждую минуту , обновляет панель пользователя. Код выглядит так:

# SitesController
def dashboard
end

def regular_update
  response.headers['Content-Type'] = 'text/event-stream'
  sse = SSE.new(response.stream, event: 'notice')
  begin
    sse.write(NoticeTask.perform) # custom code returning the JSOn 
    sleep 60
  rescue ClientDisconnected
  ensure
    sse.close
  end
end

# routes
get "/dashboard(/:id)" => "sites#dashboard"
get "/site_update" => 'sites#regular_update'

# view - /dashboard
var source = new EventSource('/site_update');
source.addEventListener('notice', function(event) {
  var data = JSON.parse(event.data)
  appendNoticeAndAlert(data)
});

Это работает просто отлично. Когда я нахожусь на /dashboard для пользователя, SSE регулярно обновляет нужную информацию, прекрасно!

Однако я замечаю, что я нахожусь на любой случайной странице, например, только на домашней странице, SSE все еще работает в фоновом режиме. Теперь ... очевидно, это имеет смысл, поскольку в коде нет ничего, что могло бы ограничить это ... но разве не должно быть ??? Например, разве не должно быть способа расширить SSE каким-либо образом? Разве это не большая трата ресурсов, если пользователь никогда не подключается к /dashboard, чтобы SSE постоянно работал в фоновом режиме, обновляя страницу /dashboard?

Опять же, новичок в SSE, если это в корне неправильно, пожалуйста, посоветуйте также. Спасибо!

1 Ответ

0 голосов
/ 25 февраля 2020

В вашем контроллере при обработке SSE ожидается, что вы будете выполнять обновления в al oop, тогда ActionController::Live::ClientDisconnected повышается на response.stream.write после ухода клиента:

def regular_update
  response.headers['Content-Type'] = 'text/event-stream'
  sse = SSE.new(response.stream, event: 'notice')
  loop do
    sse.write(NoticeTask.perform) # custom code returning the JSOn 
    sleep 60
  end
rescue ClientDisconnected
  logger.info "Client is gone"
ensure
  sse.close
end

ваш код отключает клиент после первого обновления и задержки, но все, кажется, работает, потому что EventSource автоматически переподключается (таким образом, вы получаете длинные опросы обновлений).

На клиенте EventSource должно быть close() d один раз не нужен Обычно это делается автоматически при переходе от страницы, на которой он находится, поэтому:

  1. убедитесь, что источник событий javascript находится только на странице панели инструментов, а не в пакете javascript (или в комплекте, но включается только на указанной странице c)
  2. если вы используете turbolinks - вам нужно close() установить соединение вручную, в качестве быстрого решения - попробуйте добавить <meta name="turbolinks-visit-control" content="reload"> в заголовок страницы или временно отключить турболинки .

Также еще раз подумайте, нужен ли вам SSE для этой конкретной задачи c, потому что для простых периодических c обновлений вы можете просто опросить действие json из кода на стороне клиента, которое будет визуализировать те же данные. Это упростит контроллер, не будет держать соединение занятым для каждого клиента, будет иметь более широкую совместимость с сервером и т. Д. c.

Для обоснования SSE - по крайней мере, проверьте, действительно ли что-то изменилось, и пропустите сообщение, если ничего нет , Лучше использовать какой-нибудь pub-sub (например, Redis 'SUBSCRIBE / PUBLISH или Postgres' LISTEN / NOTIFY) - отправлять события в topi c каждый раз, когда что-то влияет Изменения в панели управления, подписка на SSE-соединение и т. д. (также могут быть обновления газа, зависит от вашего приложения). Подобное может быть реализовано с помощью ActionCable (немного излишне, но может быть удобно, так как уже интегрировано в pub-sub)

...