Проектирование сервера WebSocket в микросервисной архитектуре для связи в реальном времени

Эта статья написана, чтобы поделиться моим исследованием связи в реальном времени между интерфейсом и сервером с использованием WebSocket. В последние годы микросервис — это архитектурный подход, принятый многими разработчиками, и одним из ключевых принципов микросервисной архитектуры является «принцип единой ответственности».

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

Примечание. В этой статье не будет подробно рассказано о том, как работает WebSocket или шаблон обмена сообщениями "публикация-подписка".



Моя серия серверов WebSocket

Фоновый контекст

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

Предположим, для каждого варианта использования есть микросервис; на приведенной выше диаграмме показано, как устанавливаются соединения WebSocket между клиентом (внешний интерфейс) и каждым из отдельных микросервисов (внутренняя часть).

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

Дизайн сервера WebSocket

В приведенном выше дизайне сервер WebSocket является единственным микросервисом, который устанавливает соединение WebSocket с веб-приложением (интерфейсом). Для других микросервисов существует два основных способа связи в реальном времени с веб-приложением (интерфейсом):

  1. Однонаправленный (от внутреннего интерфейса к внешнему)
    — все микросервисы (внутренний интерфейс) могут отправлять сообщения на сервер WebSocket через API, откуда сообщение затем будет перенаправлено в веб-приложение (интерфейс) через WebSocket.
  2. Двунаправленный (между внешним и внутренним интерфейсом)
     – веб-приложение (интерфейсный интерфейс) может отправлять сообщения на сервер WebSocket через WebSocket, откуда сообщение затем будет перенаправлено в микросервисы (бэкэнд) через Pub/Sub.
     – Микросервисы (бэкенд) могут отправлять сообщения на сервер WebSocket через Pub/Sub, где сообщение затем будет перенаправлено в веб-приложение (интерфейс) через WebSocket.

Создание сервера WebSocket

Мы создадим сервер WebSocket, используя Spring Boot, Stomp и Redis Pub/Sub на основе приведенного выше дизайна. Поскольку я не буду вдаваться в подробности, вы можете обратиться к этим замечательным статьям от Tomasz Dąbrowski и Baeldung.com, чтобы узнать больше о реализации WebSocket с помощью Spring Boot.

Шаг 1. Инициализация проекта Spring Boot

Перейдите на https://start.spring.io/ и инициализируйте проект Spring Boot. Как минимум, вам потребуются зависимости Spring Web, Redis и Websocket.

Шаг 2.Настройте WebSocket и обмен сообщениями STOMP.

Создайте файл конфигурации WebsocketConfig.kt и добавьте приведенную ниже конфигурацию. Конфигурация включает возможности WebSocket для приложения Spring Boot.

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

Шаг 3.Создайте конечную точку API для однонаправленного обмена данными в режиме реального времени.

Конечная точка API предоставляет микрослужбам (бэкенду) возможность отправлять сообщения в веб-приложение (интерфейс). Поскольку для сообщений требуется только односторонний поток (бэкэнд → сервер WebSocket → внешний интерфейс), использование API будет хорошим средством связи между микросервисами (бэкэнд → сервер веб-сокетов).

Приведенный выше код создает контроллер REST с конечной точкой запроса POST, который принимает тело запроса «NewMessageRequest», где topic — это пункт назначения STOMP, на который подписывается клиент (интерфейс), а message — это фактическое сообщение в формате String. При этом теперь вы можете отправить сообщение через API на сервер WebSocket, которое затем будет перенаправлено в веб-приложение (интерфейс) через WebSocket.

Шаг 4.Настройте Redis Pub/Sub для двусторонней связи в реальном времени (необязательно)

Примечание. В зависимости от варианта использования вы можете пропустить этот шаг, если вам не требуется двунаправленная связь в реальном времени между веб-приложением (внешний интерфейс) и микросервисами (внутренняя часть).

Связь через API между микросервисами (бэкэнд и сервер Websocket) не будет оптимальной для связи в реальном времени по сравнению с использованием шаблона обмена сообщениями «публикация-подписка». Следовательно, для двунаправленной связи мы будем использовать шаблон обмена сообщениями «публикация-подписка».

Существует много способов реализовать шаблон обмена сообщениями «публикация-подписка», но для демонстрации и простоты мы будем использовать Redis Pub/Sub.

Для начала запустите сервер Redis локально с помощью докера (docker run — name redis-server -p 6379:6379 -d redis) и добавьте следующую конфигурацию в файл application.yml, чтобы сервер WebSocket мог подключаться к серверу Redis.

# application.yml
spring.redis:
    host: localhost
    port: 6379

Затем создайте файл конфигурации RedisConfig.kt и добавьте приведенную ниже конфигурацию. По сути, мы настраиваем ReactiveRedisTemplate, который взаимодействует с сервером Redis и настроен на сериализацию и десериализацию сообщений как String.

После этого создайте RedisService, содержащий логику для подписки и публикации на сервере Redis. В приведенном ниже примере мы подписались на тему входящего канала GREETING_CHANNEL_INBOUND, которая прослушивает входящие сообщения от других микросервисов (бэкэнд) и пересылает все полученные сообщения в пункт назначения STOMP /topic/greetings.

Наконец, создайте Controller, который обрабатывает сообщения от веб-приложения (интерфейса), которые отправляются на сервер WebSocket с префиксом /app. В приведенном ниже примере сообщения, отправленные на /app/greet, будут перенаправлены (опубликованы) в тему исходящего канала GREETING_CHANNEL_OUTBOUND, которая затем будет обработана любой микрослужбой (серверной частью), прослушивающей этот канал.

При этом мы настроили сервер WebSocket для работы в качестве промежуточного программного обеспечения (или прокси), которое взаимодействует с веб-приложением (интерфейс) через WebSocket и взаимодействует с микросервисами (бэкэнд) через Redis Pub/Sub.

Тестирование соединения WebSocket

Используя инструмент отладчика клиента веб-сокета с открытым исходным кодом, созданный jiangxy в качестве макета веб-приложения (интерфейс), мы можем протестировать сервер веб-сокетов, который мы создали выше.

Тест 1. Отправка сообщения из серверной части во внешний интерфейс (через API)

Разверните сервер WebSocket и подключитесь к серверу WebSocket ws://localhost:8080/stomp по протоколу STOMP, используя инструмент отладчика WebSocket. После подключения настройте инструмент отладчика WebSocket для подписки на тему /topic/toast.

Затем отправьте запрос HTTP POST на сервер WebSocket, используя следующую команду:

curl -X POST -d '{"topic": "/topic/toast", "message": "testing API endpoint" }' -H 'Content-Type: application/json' localhost:8080/api/notification

Инструмент отладчика WebSocket должен иметь вывод, показанный ниже:

Это показывает, что сервер WebSocket успешно получил сообщение через API и перенаправил сообщение в веб-приложение (интерфейс) через WebSocket.

Тест 2. Отправка сообщения из бэкэнда во внешний интерфейс (через Pub/Sub)

Разверните сервер WebSocket и подключитесь к серверу WebSocket ws://localhost:8080/stomp по протоколу STOMP, используя инструмент отладчика WebSocket. После подключения настройте инструмент отладчика WebSocket для подписки на тему /topic/greetings (определено выше).

Используя Redis CLI, опубликуйте сообщение в теме канала GREETING_CHANNEL_INBOUND (определено выше) с помощью команды PUBLISH GREETING_CHANNEL_INBOUND “\"Test Message from Backend PubSub\"”.

Обратите внимание, что дополнительный \” необходим, так как сервер WebSocket настроен на получение строковых сообщений. Инструмент отладчика WebSocket должен получить сообщение, как показано ниже.

Это показывает, что сервер WebSocket успешно получил сообщение через Redis Pub/Sub и перенаправил сообщение в веб-приложение (интерфейс) через WebSocket.

Тест 3. Отправка сообщения из внешнего интерфейса в серверный (через Pub/Sub)

Разверните сервер WebSocket и подключитесь к серверу WebSocket ws://localhost:8080/stomp по протоколу STOMP, используя инструмент отладчика WebSocket. После подключения с помощью CLI Redis подпишитесь на тему канала GREETING_CHANNEL_OUTBOUND (определено выше) с помощью команды SUBSCRIBE GREETING_CHANNEL_OUTBOUND. Отправьте сообщение в пункт назначения STOMP /app/greet с помощью инструмента отладчика WebSocket, и вы должны соблюдать следующее:

Это показывает, что сервер WebSocket успешно получил сообщение через WebSocket и перенаправил сообщение в микросервисы (бэкенд) через Redis Pub/Sub.

Краткое содержание

Таким образом, мы рассмотрели возможный дизайн сервера WebSocket в микросервисной архитектуре. Наличие сервера WebSocket в значительной степени соответствует «принципу единой ответственности» микросервисов, где он управляет всеми соединениями WebSocket с веб-приложением (интерфейс), а также обрабатывает связь в реальном времени между веб-приложением (интерфейс) и другими микросервисами (бэкэнд). .

Вот и все! Надеюсь, вы узнали что-то новое из этой статьи. Оставайтесь с нами до следующего, где мы рассмотрим масштабирование сервера WebSocket.

Если вам понравилась эта статья, пожалуйста, следуйте за мной для получения дополнительной информации :).

Спасибо, что дочитали до конца. Приятного обучения!