Отправленные сервером события с помощью 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
Хорошего дня.