[Разработка веб-сайтов, Программирование, Java] Пишем чат с использованием Spring Boot и WebSockets (перевод)
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Всем привет. В преддверии старта курса «Разработчик на Spring Framework» мы подготовили для вас еще один полезный перевод. Но, прежде чем перейти к статье, хотим поделиться с вами бесплатной записью урока от наших преподавателей по теме: «Рефакторинг кода приложений на Spring», а также предлагаем посмотреть запись вебинара из которого вы сможете подробно узнать о программе курса и формате обучения.
А теперь перейдем к статье
В статье Building Scalable Facebook-like Notification using Server-Sent Event and Redis для отправки сообщений от сервера клиенту мы использовали Server-sent Events. Также там было упомянуто о WebSocket — технологии двунаправленной связи между сервером и клиентом.
В этой статье мы посмотрим на один из распространенных примеров использования WebSocket. Мы напишем приложение для обмена приватными сообщениями.
Ниже на видео продемонстрировано то, что мы собираемся сделать.
https://www.google.com/url?q=https://youtu.be/fgfSy...p9UR1KURKO9HEaMA
Введение в WebSockets и STOMP
WebSocket — это протокол для двусторонней связи между сервером и клиентом.
WebSocket, в отличие от HTTP, протокола прикладного уровня, является протоколом транспортного уровня (TCP). Хотя для первоначальной установки соединения используется HTTP, но потом соединение «обновляется» до TCP-соединения, используемого в WebSocket.
WebSocket — протокол низкого уровня, который не определяет форматы сообщений. Поэтому WebSocket RFC определяет подпротоколы, описывающие структуру и стандарты сообщений. Мы будем использовать STOMP поверх WebSockets (STOMP over WebSockets).
Протокол STOMP (Simple / Streaming Text Oriented Message Protocol) определяет правила обмена сообщениями между сервером и клиентом.
STOMP похож на HTTP и работает поверх TCP, используя следующие команды:
- CONNECT
- SUBSCRIBE
- UNSUBSCRIBE
- SEND
- BEGIN
- COMMIT
- ACK
Спецификацию и полный список команд STOMP можно найти здесь.
Архитектура
- Сервис аутентификации (Auth Service) ответственен за аутентификацию и управление пользователями. Здесь мы не будем изобретать колесо и воспользуемся сервисом аутентификации из статьи JWT and Social Authentication using Spring Boot.
- Сервис чата (Chat Service) ответственен за настройку WebSocket, обработку STOMP-сообщений, а также за сохранение и обработку сообщений пользователей.
- Клиент (Chat Client) — это приложение на ReactJS, использующее STOMP-клиента для подключения и подписки на чат. Также здесь находится пользовательский интерфейс.
Модель сообщения
Первое, о чем нужно подумать — это модель сообщения. ChatMessage выглядит следующим образом:
public class ChatMessage {
@Id
private String id;
private String chatId;
private String senderId;
private String recipientId;
private String senderName;
private String recipientName;
private String content;
private Date timestamp;
private MessageStatus status;
}
Класс ChatMessage довольно простой, с полями, необходимыми для идентификации отправителя и получателя.
В нем также есть поле статуса, указывающее доставлено ли сообщение клиенту.
public enum MessageStatus {
RECEIVED, DELIVERED
}
Когда сервер получает сообщение из чата, он не отправляет сообщение адресату напрямую, а отправляет уведомление (ChatNotification), чтобы оповестить клиента о получении нового сообщения. После этого клиент сам может получить новое сообщение. Как только клиент получит сообщение, оно помечается как доставленное (DELIVERED).
Уведомление выглядит следующим образом:
public class ChatNotification {
private String id;
private String senderId;
private String senderName;
}
В уведомлении есть идентификатор нового сообщения и информация об отправителе для того, чтобы клиент мог показать информацию о новом сообщении или о количестве новых сообщений, как показано ниже.
Настройка WebSocket и STOMP в Spring
Первым делом настраиваем конечную точку STOMP и брокер сообщений.
Для этого создаем класс WebSocketConfig с аннотациями @Configuration и @EnableWebSocketMessageBroker.
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker( "/user");
config.setApplicationDestinationPrefixes("/app");
config.setUserDestinationPrefix("/user");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry
.addEndpoint("/ws")
.setAllowedOrigins("*")
.withSockJS();
}
@Override
public boolean configureMessageConverters(List<MessageConverter> messageConverters) {
DefaultContentTypeResolver resolver = new DefaultContentTypeResolver();
resolver.setDefaultMimeType(MimeTypeUtils.APPLICATION_JSON);
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setObjectMapper(new ObjectMapper());
converter.setContentTypeResolver(resolver);
messageConverters.add(converter);
return false;
}
}
Первый метод конфигурирует простой брокер сообщений в памяти с одним адресом с префиксом /user для отправки и получения сообщений. Адреса с префиксом /app предназначены для сообщений, обрабатываемых методами с аннотацией @MessageMapping, которые мы обсудим в следующем разделе.
Второй метод регистрирует конечную точку STOMP /ws. Эта конечная точка будет использоваться клиентами для подключения к STOMP-серверу. Здесь также включается резервный SockJS, который будет использоваться, если WebSocket будет недоступен.
Последний метод настраивает конвертер JSON, который используется Spring'ом для преобразования сообщений из/в JSON.
Контроллер для обработки сообщений
В этом разделе мы создадим контроллер, который будет обрабатывать запросы. Он будет получать сообщение от пользователя и отправлять его получателю.
@Controller
public class ChatController {
@Autowired private SimpMessagingTemplate messagingTemplate;
@Autowired private ChatMessageService chatMessageService;
@Autowired private ChatRoomService chatRoomService;
@MessageMapping("/chat")
public void processMessage(@Payload ChatMessage chatMessage) {
var chatId = chatRoomService
.getChatId(chatMessage.getSenderId(), chatMessage.getRecipientId(), true);
chatMessage.setChatId(chatId.get());
ChatMessage saved = chatMessageService.save(chatMessage);
messagingTemplate.convertAndSendToUser(
chatMessage.getRecipientId(),"/queue/messages",
new ChatNotification(
saved.getId(),
saved.getSenderId(),
saved.getSenderName()));
}
}
С помощью аннотации @MessageMapping мы настраиваем, что при отправке сообщения в /app/chat вызывается метод processMessage. Обратите внимание, что к маппингу добавится сконфигурированный ранее application-префикс /app.
Этот метод сохраняет сообщение в MongoDB, а затем вызывает метод convertAndSendToUser для отправки уведомления адресату.
Метод convertAndSendToUser добавляет префикс /user и recipientId к адресу /queue/messages. Конечный адрес будет выглядеть так:
/user/{recipientId}/queue/messages
Все подписчики данного адреса (в нашем случае один) получат сообщение.
Генерация chatId
Для каждой беседы между двумя пользователями мы создаем чат-комнату и для ее идентификации генерируем уникальный chatId.
Класс ChatRoom выглядит следующим образом:
public class ChatRoom {
private String id;
private String chatId;
private String senderId;
private String recipientId;
}
Значение chatId равно конкатенации senderId_recipientId. Для каждой беседы мы сохраняем две сущности с одинаковыми chatId: одну между отправителем и получателем, а другую между получателем и отправителем, чтобы оба пользователя получали одинаковый chatId.
JavaScript-клиент
В этом разделе мы создадим JavaScript-клиента, который будет отправлять сообщения на WebSocket/STOMP-сервер и получать их оттуда.
Мы будем использовать SockJS и Stomp.js для общения с сервером с использованием STOMP over WebSocket.
const connect = () => {
const Stomp = require("stompjs");
var SockJS = require("sockjs-client");
SockJS = new SockJS("http://localhost:8080/ws");
stompClient = Stomp.over(SockJS);
stompClient.connect({}, onConnected, onError);
};
Метод connect() устанавливает соединение с /ws, где ожидает подключений наш сервер, и также определяет callback-функцию onConnected, которая будет вызвана при успешном подключении, и onError, вызываемую, если при подключении к серверу произошла ошибка.
const onConnected = () => {
console.log("connected");
stompClient.subscribe(
"/user/" + currentUser.id + "/queue/messages",
onMessageReceived
);
};
Метод onConnected() подписывается на определенный адрес и получает все отправляемые туда сообщения.
const sendMessage = (msg) => {
if (msg.trim() !== "") {
const message = {
senderId: currentUser.id,
recipientId: activeContact.id,
senderName: currentUser.name,
recipientName: activeContact.name,
content: msg,
timestamp: new Date(),
};
stompClient.send("/app/chat", {}, JSON.stringify(message));
}
};
В конце метода sendMessage() сообщение отправляется по адресу /app/chat, который указан в нашем контроллере.
Заключение
В этой статье мы рассмотрели все важные моменты создания чата с использованием Spring Boot и STOMP over WebSocket.
Мы также создали JavaScript-клиент с применением библиотек SockJs и Stomp.js.
Пример исходного кода можно найти здесь.
Узнать о курсе подробнее.
Читать ещё:
- Интеграционное тестирование в SpringBoot с TestContainers-стартером
- Что нового в Spring Data (Klara Dan von) Neumann
===========
Источник:
habr.com
===========
===========
Автор оригинала: Amr Khaled
===========Похожие новости:
- [Поисковая оптимизация, Разработка веб-сайтов] Ленивая загрузка для карт
- [Ненормальное программирование, Программирование, Совершенный код, C++] Совершенный цикл
- [JavaScript, Angular, TypeScript] Что можно положить в механизм Dependency Injection в Angular?
- [DevOps, Информационная безопасность, Разработка веб-сайтов, Разработка мобильных приложений] DevSecOps: принципы работы и сравнение SCA. Часть первая
- [Программирование микроконтроллеров, Промышленное программирование] Ethercat для начинающих
- [JavaScript] Решение частых алгоритмических вопросов на JavaScript
- [Разработка веб-сайтов, Разработка мобильных приложений, Разработка под Android, Карьера в IT-индустрии, Разработка под Windows] Нужен ли еще один сервис (opensource)? (в конце опрос)
- [Разработка веб-сайтов, JavaScript, Программирование, ReactJS] Изучаем хук useRef в React.js (перевод)
- [Высокая производительность, Программирование, Go] Чистая архитектура с Go
- [Программирование микроконтроллеров, Разработка для интернета вещей, DIY или Сделай сам] «Умная» детская коляска «Максимка»
Теги для поиска: #_razrabotka_vebsajtov (Разработка веб-сайтов), #_programmirovanie (Программирование), #_java, #_programming, #_java, #_spring_boot, #_web_development, #_websocket, #_blog_kompanii_otus._onlajnobrazovanie (
Блог компании OTUS. Онлайн-образование
), #_razrabotka_vebsajtov (
Разработка веб-сайтов
), #_programmirovanie (
Программирование
), #_java
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 23-Ноя 00:30
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Всем привет. В преддверии старта курса «Разработчик на Spring Framework» мы подготовили для вас еще один полезный перевод. Но, прежде чем перейти к статье, хотим поделиться с вами бесплатной записью урока от наших преподавателей по теме: «Рефакторинг кода приложений на Spring», а также предлагаем посмотреть запись вебинара из которого вы сможете подробно узнать о программе курса и формате обучения.
А теперь перейдем к статье В статье Building Scalable Facebook-like Notification using Server-Sent Event and Redis для отправки сообщений от сервера клиенту мы использовали Server-sent Events. Также там было упомянуто о WebSocket — технологии двунаправленной связи между сервером и клиентом. В этой статье мы посмотрим на один из распространенных примеров использования WebSocket. Мы напишем приложение для обмена приватными сообщениями. Ниже на видео продемонстрировано то, что мы собираемся сделать. https://www.google.com/url?q=https://youtu.be/fgfSy...p9UR1KURKO9HEaMA Введение в WebSockets и STOMP WebSocket — это протокол для двусторонней связи между сервером и клиентом. WebSocket, в отличие от HTTP, протокола прикладного уровня, является протоколом транспортного уровня (TCP). Хотя для первоначальной установки соединения используется HTTP, но потом соединение «обновляется» до TCP-соединения, используемого в WebSocket. WebSocket — протокол низкого уровня, который не определяет форматы сообщений. Поэтому WebSocket RFC определяет подпротоколы, описывающие структуру и стандарты сообщений. Мы будем использовать STOMP поверх WebSockets (STOMP over WebSockets). Протокол STOMP (Simple / Streaming Text Oriented Message Protocol) определяет правила обмена сообщениями между сервером и клиентом. STOMP похож на HTTP и работает поверх TCP, используя следующие команды:
Спецификацию и полный список команд STOMP можно найти здесь. Архитектура
Модель сообщения Первое, о чем нужно подумать — это модель сообщения. ChatMessage выглядит следующим образом: public class ChatMessage {
@Id private String id; private String chatId; private String senderId; private String recipientId; private String senderName; private String recipientName; private String content; private Date timestamp; private MessageStatus status; } Класс ChatMessage довольно простой, с полями, необходимыми для идентификации отправителя и получателя. В нем также есть поле статуса, указывающее доставлено ли сообщение клиенту. public enum MessageStatus {
RECEIVED, DELIVERED } Когда сервер получает сообщение из чата, он не отправляет сообщение адресату напрямую, а отправляет уведомление (ChatNotification), чтобы оповестить клиента о получении нового сообщения. После этого клиент сам может получить новое сообщение. Как только клиент получит сообщение, оно помечается как доставленное (DELIVERED). Уведомление выглядит следующим образом: public class ChatNotification {
private String id; private String senderId; private String senderName; } В уведомлении есть идентификатор нового сообщения и информация об отправителе для того, чтобы клиент мог показать информацию о новом сообщении или о количестве новых сообщений, как показано ниже. Настройка WebSocket и STOMP в Spring Первым делом настраиваем конечную точку STOMP и брокер сообщений. Для этого создаем класс WebSocketConfig с аннотациями @Configuration и @EnableWebSocketMessageBroker. @Configuration
@EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker( "/user"); config.setApplicationDestinationPrefixes("/app"); config.setUserDestinationPrefix("/user"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry .addEndpoint("/ws") .setAllowedOrigins("*") .withSockJS(); } @Override public boolean configureMessageConverters(List<MessageConverter> messageConverters) { DefaultContentTypeResolver resolver = new DefaultContentTypeResolver(); resolver.setDefaultMimeType(MimeTypeUtils.APPLICATION_JSON); MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter(); converter.setObjectMapper(new ObjectMapper()); converter.setContentTypeResolver(resolver); messageConverters.add(converter); return false; } } Первый метод конфигурирует простой брокер сообщений в памяти с одним адресом с префиксом /user для отправки и получения сообщений. Адреса с префиксом /app предназначены для сообщений, обрабатываемых методами с аннотацией @MessageMapping, которые мы обсудим в следующем разделе. Второй метод регистрирует конечную точку STOMP /ws. Эта конечная точка будет использоваться клиентами для подключения к STOMP-серверу. Здесь также включается резервный SockJS, который будет использоваться, если WebSocket будет недоступен. Последний метод настраивает конвертер JSON, который используется Spring'ом для преобразования сообщений из/в JSON. Контроллер для обработки сообщений В этом разделе мы создадим контроллер, который будет обрабатывать запросы. Он будет получать сообщение от пользователя и отправлять его получателю. @Controller
public class ChatController { @Autowired private SimpMessagingTemplate messagingTemplate; @Autowired private ChatMessageService chatMessageService; @Autowired private ChatRoomService chatRoomService; @MessageMapping("/chat") public void processMessage(@Payload ChatMessage chatMessage) { var chatId = chatRoomService .getChatId(chatMessage.getSenderId(), chatMessage.getRecipientId(), true); chatMessage.setChatId(chatId.get()); ChatMessage saved = chatMessageService.save(chatMessage); messagingTemplate.convertAndSendToUser( chatMessage.getRecipientId(),"/queue/messages", new ChatNotification( saved.getId(), saved.getSenderId(), saved.getSenderName())); } } С помощью аннотации @MessageMapping мы настраиваем, что при отправке сообщения в /app/chat вызывается метод processMessage. Обратите внимание, что к маппингу добавится сконфигурированный ранее application-префикс /app. Этот метод сохраняет сообщение в MongoDB, а затем вызывает метод convertAndSendToUser для отправки уведомления адресату. Метод convertAndSendToUser добавляет префикс /user и recipientId к адресу /queue/messages. Конечный адрес будет выглядеть так: /user/{recipientId}/queue/messages
Все подписчики данного адреса (в нашем случае один) получат сообщение. Генерация chatId Для каждой беседы между двумя пользователями мы создаем чат-комнату и для ее идентификации генерируем уникальный chatId. Класс ChatRoom выглядит следующим образом: public class ChatRoom {
private String id; private String chatId; private String senderId; private String recipientId; } Значение chatId равно конкатенации senderId_recipientId. Для каждой беседы мы сохраняем две сущности с одинаковыми chatId: одну между отправителем и получателем, а другую между получателем и отправителем, чтобы оба пользователя получали одинаковый chatId. JavaScript-клиент В этом разделе мы создадим JavaScript-клиента, который будет отправлять сообщения на WebSocket/STOMP-сервер и получать их оттуда. Мы будем использовать SockJS и Stomp.js для общения с сервером с использованием STOMP over WebSocket. const connect = () => {
const Stomp = require("stompjs"); var SockJS = require("sockjs-client"); SockJS = new SockJS("http://localhost:8080/ws"); stompClient = Stomp.over(SockJS); stompClient.connect({}, onConnected, onError); }; Метод connect() устанавливает соединение с /ws, где ожидает подключений наш сервер, и также определяет callback-функцию onConnected, которая будет вызвана при успешном подключении, и onError, вызываемую, если при подключении к серверу произошла ошибка. const onConnected = () => {
console.log("connected"); stompClient.subscribe( "/user/" + currentUser.id + "/queue/messages", onMessageReceived ); }; Метод onConnected() подписывается на определенный адрес и получает все отправляемые туда сообщения. const sendMessage = (msg) => {
if (msg.trim() !== "") { const message = { senderId: currentUser.id, recipientId: activeContact.id, senderName: currentUser.name, recipientName: activeContact.name, content: msg, timestamp: new Date(), }; stompClient.send("/app/chat", {}, JSON.stringify(message)); } }; В конце метода sendMessage() сообщение отправляется по адресу /app/chat, который указан в нашем контроллере. Заключение В этой статье мы рассмотрели все важные моменты создания чата с использованием Spring Boot и STOMP over WebSocket. Мы также создали JavaScript-клиент с применением библиотек SockJs и Stomp.js. Пример исходного кода можно найти здесь. Узнать о курсе подробнее. Читать ещё:
=========== Источник: habr.com =========== =========== Автор оригинала: Amr Khaled ===========Похожие новости:
Блог компании OTUS. Онлайн-образование ), #_razrabotka_vebsajtov ( Разработка веб-сайтов ), #_programmirovanie ( Программирование ), #_java |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 23-Ноя 00:30
Часовой пояс: UTC + 5