Отправленные сервером события с помощью Go и React

События, отправленные сервером (SSE), могут использоваться для однонаправленной отправки данных с сервера клиенту, что будет полезно для таких случаев использования, как; ведение журнала, прямая трансляция и т. д. В этой истории мы будем использовать события, отправленные сервером, для получения информации журнала от сервера и отображения ее на клиенте.

Для этого нам потребуется система публикации-подписки, в которой любой запрос на потоковую передачу конечной точки подписывается на событие, и при любом событии все подписчики, подписанные на событие, будут уведомлены.

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

СЕРВЕР

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

type SSEChannel struct {
    Clients  []chan string
    Notifier chan string
}
var sseChannel SSEChannel

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

Здесь мы получаем события от sseChannel.Notifier, и все клиенты, зарегистрированные на сервере, отправляют данные, полученные от уведомителя.

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

Обратите внимание на строку № 10, здесь мы отправляем сообщение журнала после создания в канал уведомления, который будет обрабатываться нашим вещателем, работающим параллельно с основной подпрограммой go.

Давайте создадим обработчик http, который может обрабатывать /sse запросы и отвечать потоком. Нам потребуется обработчик создать канал, добавить его в SSEChannel.Clients и прослушивать сообщения из канала.

В приведенном выше коде мы можем отметить, что:

  • Со строк №2 по №5 мы настраиваем заголовки, которые будут отправлены клиенту SSE. Заголовок Content-Type: text/event-stream важен. Это то, что говорит клиенту, что сервер поддерживает поток событий и не закрывает это TCP-соединение после получения ответа.
  • В строке №7 мы преобразуем наш HTTPWriter в Flusher, который сбрасывает вывод. Это означает, что после завершения подготовки к отправке клиенту очистки писатель отправит сообщения, записанные писателю, и подготовит его для другого потока, который можно увидеть в flusher.Flush() в строке # 28.
  • Как упоминалось ранее, мы создаем канал и добавляем его в список клиентов в строках №13 и №14.
  • В строках с №20 по №30 мы прослушиваем события, которые будут отправлены от нашей программы вещания go, определенной ранее. data:= <-sseChan используется для получения отправленных сообщений журнала, которые будут отправлены через наш поток sse, выполненный в строке № 27, и после этого мы сбрасываем писатель.
  • В строке № 17 мы можем отметить, что, data: %v \n\n, этот формат является обязательным при отправке sse и не используется только для форматирования сообщений.

Теперь на стороне сервера осталось соединить все точки в функции main(). т.е.

Что следует отметить в нашей реализации:

  • Это базовая реализация, которая не распространяется на удаление закрытых каналов от клиентов.
  • Мы могли бы использовать шаблон брокера для системы вещания, как показано в этом ответе на переполнение стека.

Наша реализация на стороне сервера завершена, мы можем проверить, работает ли это, перейдя к localhost:5000/sse и увидев, что запрос никогда не разрешается по индикатору загрузки. Теперь, если мы посетим localhost:5000/log, мы увидим, что сообщение журнала появилось на вкладке, содержащей /sse конечную точку.

КЛИЕНТ

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

yarn create react-app frontend --template typescript

И создайте пользовательский обработчик useBufferedState, который может хранить только список строк длиной len, и если их больше, чем len, данные будут отображаться в порядке «первым пришел - первым обслужен».

Из реализации мы можем заметить, что в строках с №16 по №24 мы создаем пользовательскую функцию, которая устанавливает состояние. Обратный вызов в setBuffer используется для установки состояния для просмотра текущего значения состояния, а не предыдущего значения при использовании с обработчиками событий в response.

В нашем App.tsx давайте создадим глобальную переменную es, которая будет хранить дескриптор EventSource.

  • В строках с 4 по 12 мы создаем EventSource в глобальной переменной es. И когда сообщение отправляется в поток событий, мы переводим его в наше буферизованное состояние.
  • При нажатии кнопки закрытия потока es.close() используется для закрытия Steam и перевода сообщения в состояние.
  • Сообщение sendRequest используется для отправки случайных запросов к конечной точке /log, чтобы было получено сообщение потока событий.

Рендеринг нашего компонента будет выглядеть так:

Здесь мы просто присоединяем наши ранее определенные обработчики событий. И рендеринг тега pre для всех сообщений буфера. После того, как все будет завершено, вывод кода реакции будет выглядеть так:

И мы можем отправлять и получать потоки событий с сервера go.

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

Из документации Mozilla здесь: вы должны быть осторожны, когда не используете SSE поверх HTTP / 2, потому что он страдает от ограничения максимального количества открытых соединений, которое установлено на очень низкое число (6). И эта проблема не будет исправлена ​​в Chrome и Firebox.

Полный код вы можете проверить;



Следуйте за мной на GitHub: https://github.com/dipeshdulal

Основные понятия о проверке голанга; Https://github.com/dipeshdulal/learning-golang

Хорошего дня.