ru:https://highload.today/blogs/kak-integrirovat-na-proekte-websocket-i-ne-sgoret-poshagovaya-instruktsiya/ ua:https://highload.today/uk/blogs/yak-integruvati-na-proyekti-websocket-ta-ne-zgoriti-pokrokova-instruktsiya/
logo
Опыт      09/06/2022

Как интегрировать на проекте WebSocket и не сгореть: пошаговая инструкция

Роман Дашківський BLOG

Java Developer в NIX

Привет! Меня зовут Роман Дашковский, я Java Developer в NIX и спикер IT-конференции NIX MultiConf. В этой статье я расскажу, с какими трудностями можно столкнуться при интегрировании на проекте WebSocket и как их преодолеть.

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

Но что делать, когда клиент требует от пользователей наблюдать все апдейты «на лету»?

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

Что такое веб-сокет

Так что же такое сокеты, зачем их использовать и как вообще работают веб-приложения? Все это я объясню дальше на простом примере.

Веб-сокет — это протокол, который в отличие от HTTP позволяет организовать постоянную двунаправленную связь между клиентом и сервером.

Жизненный цикл соединения состоит из трех этапов:

  1. Инициализация соединения — происходит handshake-запрос по HTTP, после чего соединение обновляется в WebSocket.
  2. Пересылка данных — отправка сообщения может происходить от клиента на сервер и наоборот.
  3. Разрыв соединения — инициатором разрыва может быть как клиент (например, закрытие вкладки), так и сервер (программно).
  4. Онлайн курс з промт інжинірингу та ефективної роботи з ШІ від Powercode academy.
    Курс-інтенсив для отримання навичок роботи з ChatGPT та іншими інструментами ШІ для професійних та особистих задач, котрі допоможуть як новачку, так і професіоналу.
    Записатися на курс

Подключение к SpringBoot-приложению

Давайте рассмотрим, как это все подключить к классическому SpringBoot-приложению.

Предварительная подготовка

Любые апдейты начинаются с чего? Правильно — с добавления необходимых зависимостей:

  • Для простоты использования Spring имеет множество стартеров разных цветов и размеров. Нас интересуют сокеты, поэтому добавим подходящий стартер.
  • Так как мы будем работать с данными JSON-формата, добавим JSON от гуглов.
  • В нескольких местах я буду использовать некоторые утилиты, упрощающие процесс парсинга URL. Добавляем и третью зависимость от Apache.

Предварительная подготовка завершена. Перейдем к конфигурации Spring.

Конфигурация Spring

Обычно значительная часть подобной «магии» происходит в классах, отмеченных соответствующей аннотацией Configuration. Важно добавить еще одну — @EnableWebSockets.

Онлайн-курс "Business English" від Laba.
Вивчіть базу граматики, лексики та вокабуляру.Використовуйте англійську в спонтанній розмові з колегами та клієнтами.Прокачайте її до впевненого В1 — для розвитку кар’єри в бізнесі.
Приєднатись до курсу

В документации к ней говорится, что мы должны реализовать интерфейс WebSocketConfigurer. Как видите, у интерфейса есть только один способ. Здесь можно добавить обработчики сообщений и перехватчик хендшейка. Таким образом у нас происходит валидация соединения.

Учитывая то, что при инициализации сокета у нас нет возможности передать никаких параметров в теле или хедерах, единственным вариантом для того, чтобы идентифицировать пользователя, остаются URL-параметры. Поэтому мы можем считать наш токен, верифицировать его и, если он валиден, подтвердить создание соединения.

 

Обработчик событий

Перейдем к следующему шагу — обработчику событий. В нашем примере запланирована обработка только текстовых сообщений, поэтому мы полагаемся на абстрактный класс TextWebSocketHandler. Сообщения мы будем отправлять только с сервера на клиент, так что метод handleTextMessage можно оставить пустым.

Хранилищем соединений будет обычная карта, где ключом будет юзернейм пользователя, а значением — список всех его открытых сессий (вкладок браузера).

Теперь отправка сообщений уже тривиальная задача. Попробуем реализовать возможность отправлять сообщения как всем, так и одному отдельному пользователю.

Курс-професія "Дизайнер інтер'єрів" від Skvot.
Велика практична програма для всіх, хто хоче засвоїти професію дизайнера інтер'єрів і заробляти на реальних проєктах відразу після курсу. Досвідом та інсайтами діляться одразу три лектори.
Програма курсу

UI-часть

Как это выглядит с точки зрения UI-части? Как уже отмечалось, при инициализации сокета мы не можем добавлять ни хедеры, ни тело запроса. Но всегда есть возможность передать параметры через URL, что мы и сделаем.

Дальше добавляем все необходимые слушатели событий — и вот как это выглядит схематически:

В действительности же один клиент посылает HTTP-запрос на обновление данных. После чего в контроллере мы вызываем метод notifyAll или notifyUser по одной из предыдущих схем. Здесь появляется немного магии React. Как видим, данные обновляются сразу в двух окнах.

Все клево, все работает. Но если бы все было так просто, я не писал бы эту статью…

В enterprise-проектах обычно важный фактор — производительность. Но часто одни и те же микросервисы могут существовать в нескольких экземплярах, доступ к которым осуществляется непосредственно через Load Balancer. И в зависимости от загрузки CPU, занятости оперативной памяти или других параметров могут создаваться новые инстансы или удаляться существующие. Но как текущая реализация будет работать в этом случае?

Онлайн-інтенсив "Як створити рекомендаційну модель за 2 дні" від robot_dreams.
Ви пройдете етапи вибору, навчання, оцінки рекомендаційної моделі для електронної бібліотеки та отримаєте індивідуальний фідбек від лекторки.
Приєднатись до інтенсиву

Наша карта с конекшнами сохраняется In-Memory внутри каждого инстанса. Когда мы инициализируем соединение по WS, ELB выбирает, в какой сервис будет отправлен запрос, и конекшн хранится только в нем. Когда любой сервис будет проходить по карте, вероятно, будет такая ситуация, что часть клиентов так и не получит сообщения.

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

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

Удачный способ решения проблемы — создание единого хранилища событий, которое будет сообщать всем нашим инстансам о том, что каждый должен отправить сообщения клиентам, которые в них хранятся.

Пригодится здесь Message Broker, но я выбрал ActiveMQ по простой и банальной причине — он один из самых популярных. Так что продолжаем кодить…

Как обычно, сначала обновляем зависимости. Я решил заюзать такую ​​крутую штуку, как Apache Camel. Это открытый кроссплатформенный Java-фреймворк, позволяющий производить интеграцию приложений в простой и понятной форме. Camel очень упрощает флоу обработки данных. В особенности это комфортно, если нужно выстроить цепь из нескольких обработчиков. Также он очень легко интегрируется со Spring.

Онлайн-курс "С++ для GameDev" від robot_dreams.
Навчіться кодити на C++ з нуля, опануйте принципи обʼєктно-орієнтованого програмування, ключові бібліотеки та інструменти.Створюйте десктопні та мобільні ігри. Розвивайтеся в геймдеві.
Детальніше

Не нарушаем традиции и дальше переходим к Spring-конфигам. Здесь нужно добавить два бина:

  • ActiveMqConnectionFactory, которому мы скармливаем пропсы из application.properties;
  • ActiveMQComponent — компонент Camel, который упростит процесс отправки и получения сообщений с брокера.

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

Единственное, что хочу подчеркнуть — способ, которым мы передаем информацию о пользователе, которому отправляем сообщение. Это делается с помощью Header, которые позже можно будет считать при получении сообщения от брокера.

После того, как мы отправили эти сообщения, их нужно получить на каждом инстансе. Здесь возникает одна из причин, почему я добавил в проект Camel — невероятная простота в использовании. Вот пример считывания и логирования месседжа.

Онлайн-курс Frontend-разробник від Powercode academy.
Курс на якому ти напишеш свій чистий код на JavaScript, попрацюєш із різними видами верстки, а також адаптаціями проектів під будь-які екрани. .
Зарееструватися

Но просто логировать сообщение недостаточно. Дальше его нужно отправить всем клиентам.

Для этого создаем обработчик:

  • парсим тело сообщения из обычной String в JSON-объект и затем к нашему Java-объекту;
  • считываем хидер, который мы засетили раньше;
  • пересылаем дальше уже существующими методами;
  • добавляем его в наш Data Flow в одну строку.

Наконец-то переходим к тесту. Есть клиенты, подключенные к двум разным инстанциям.

Психологічний профорієнтаційний тест для IT-фахівців від Ithillel.
Пройдіть психологічний профорієнтаційний тест для IT-фахівців щоб дізнатися ваші сильні сторони, вподобання і інтереси і з'ясувати, яка IT-спеціальність вам підходить.
Пройти тест

Выполняем апдейт на одном и наблюдаем обновления сразу обоих:

Теперь все работает. Хотел бы я так сказать, но…

Добавляем еще эндпоинт

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

Таким образом сообщение с обновленными списками будет поступать к брокеру, откуда бродкаститься на все инстансы. Но брокеры не предназначены для таких целей. Они быстро и эффективно работают с небольшими объемами данных.

Как фиксить проблемы с производительностью

В противном случае это может привести к проблемам с производительностью и формированием так называемого Bottlneck. Итак, как это пофиксить?

Применим обновленный подход. Для подобного типа сообщений будем отправлять не весь обновленный объект, а информацию о том, какой объект изменился и как именно. После этого SpecProcessor на инстансах самостоятельно соберут все новые данные и разошлют их клиентам.

Онлайн-курс "Чистий код та патерни проєктування" від robot_dreams.
Прискорюйте й спрощуйте процес розробки.Під менторством лектора з 15-річним досвідом ви навчитеся застосовувати 20+ шаблонів, опануєте рефакторинг і принципи чистого коду.
Детальніше

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

  • класснейм процессора для обработки сообщений;
  • параметры в формате карты.

Далее создаем процессор, который будет извлекать обновленные данные из БД, 3rd Party Service или из другого места и передавать их дальше.

После этого обновляем наш Data Flow в роутере. Если найден подходящий процессор, на него и идет обработка. Затем происходит отправка сообщения клиентам по WebSocket.

Вот упрощенная схема, как это работает:

Онлайн-курс "Управління ІТ-командами" від Laba.
Прокачайте свої soft- і hard-скіли в управлінні кількома IT-командами, отримайте практичні стратегії та інструменти ефективного team-ліда.
Програма курсу і реєстрація

Вместо вывода

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

 

Надеюсь, теперь при необходимости реализовать сокеты вы заранее продумаете все детали, будете знать, с какими трудностями можете столкнуться и как их преодолеть. А если хотите подробнее рассмотреть приведенный в этой статье код, переходите по ссылкам на репозитории с фронтом и бэкендом:

Онлайн-курс Pyton від Powercode academy.
Опануйте PYTHON з нуля та майте проект у своєму портфоліо вже через 4 місяця.
Приєднатися

If you have found a spelling error, please, notify us by selecting that text and pressing Ctrl+Enter.

Онлайн-курс "Digital Marketing" від Laba.
Розширте пул навичок у роботі з аудиторією.Навчіться запускати рекламні кампанії без зайвих витрат бюджету з сучасними інструментами діджитал-маркетингу, включаючи AI.
Детальніше про курс

Этот материал – не редакционный, это – личное мнение его автора. Редакция может не разделять это мнение.

Топ-5 самых популярных блогеров марта

PHP Developer в ScrumLaunch
Всего просмотровВсего просмотров
2434
#1
Всего просмотровВсего просмотров
2434
Founder at Shallwe, Python Software Engineer (Django/React)
Всего просмотровВсего просмотров
113
#2
Всего просмотровВсего просмотров
113
Career Consultant в GoIT
Всего просмотровВсего просмотров
95
#3
Всего просмотровВсего просмотров
95
CEO & Founder в Trustee
Всего просмотровВсего просмотров
94
#4
Всего просмотровВсего просмотров
94
Рейтинг блогеров

Ваша жалоба отправлена модератору

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: