Go

Server-sent events

Описание проблемы

Современные web-сервисы нуждаются в постоянном получении изменений со стороны сервера. Для решения этих задач часто используют:

  • Long polling - постоянное опрашивание сервера клиентом. Плюсы: простота реализации. Минусы: множество запросов.
  • WebSocket - устанавливается постоянное двунаправленное соединение с сервером. Плюсы: постоянное соединение для отправки и получения данных с сервера. Минусы: сложность реализации, может блокироваться антивирусами.
  • Server-sent events - устанавливается постоянное однонаправленное соединение с сервером. Плюсы: постоянное соединение для получения данных с сервера. Минусы: если нет HTTPS , то максимум 6 открытых страниц.

Мы рассмотрим небольшой пример реализации сервера и клиента для Server-sent events.

Код примера: github.com

Server-sent events требования для клиента

Все современные браузеры поддерживают event-streeming, кроме всех версий Internet Explorer.

Так же, есть возможность использовать polyfill. Например: Yaffle/EventSource

Формат работы

SSE определяет определённый формат сообщения:

retry: 5000
event: ping
data: {"message":"ping"}

retry - количество миллисекунд для новой попытки подключения к серверу (в случае разрыва соединения). Достаточно отправить только в первом сообщении (при подключении клиента). По умолчанию: 3000.

event - тип сообщения. Параметр необязательный. Если не указано, то отловить сообщение на клиенте возможно подписавшись на событие eventSource.onmessage = function (event) {...}

data - полезная нагрузка. После всегда должна идти пустая строка.

При подключении отправляется запрос на сервер с заголовками connection: keep-alive и content-type: text/event-stream. Все оповещения от сервера так же будут видны в этом запросе.

subscribe


Клиент

Для соединения с сервером используется объект EventSorsing (см. Код клиента).

Подробное описание объекта: Описание объекта. Опишу основные моменты.

  • Подключение
const eventSource = new EventSource('http://localhost:8080/subscribe', {withCredentials: false});

EventSource принимает url подключения к серверу и объект с настройками соединения. withCredentials - передавать ли авторизационные данные(COOKIE и Authorization заголовки).

Если withCredentials: true, то сервер обязан вернуть:

Access-Control-Allow-Origin: https://sse.site.test
Access-Control-Allow-Credentials: true

Access-Control-Allow-Origin: * не будет работать с withCredentials: true.

  • Подписка на определённые события
eventSource.addEventListener("ping", function (event) {
    console.log(event.type + " " + event.data)
});

eventSource.addEventListener("example", function (event) {
    const data = JSON.parse(event.data)

    log("Получено сообщение (" + event.type + "): " + data.message);
});

eventSource.addEventListener("example", function (event) {...}) - подписываемся на событие example.

Сервер

  • Структуры

DTO для отправки клиенту (internal/message.go)

type Message struct {
	Message string `json:"message"`
}

Структура подключения (internal/connect.go)

type Connect struct {
	Id        string
	Event     string
	MessageCh chan *Message
	PingCh    chan string
	DoneCh    chan struct{}
}

Event - тип события.

MessageCh - канал для отправки сообщения подписанным клиентам.

PingCh - канал для отправки сообщения при подключении клиента.

DoneCh - канал для отключения клиента.

  • Брокер сообщений

Хранит в себе всех подписчиков. Может добавлять, удалять, оповещать и пинговать подписчиков. В данном примере реализовано хранение пользователей в памяти. Для хранения подписчиков лучше использовать Redis.

Файл: internal/broker.go

  • main.go

Предоставляет в точки входа: подписаться на событие, отправить сообщение.

Файл: cmd/main.go

Запрос для публикации сообщения

http://localhost:8080/publish?message=new

Запуск

  1. $ make init
  2. Открыть public/client.html в браузере.
  3. Подписаться
  4. Отправить запрос на публикацию сообщения: http://localhost:8080/publish?message=new

Подробнее:

Авторизуйтесь, что бы оставить комментарий!