1. Обзор
RSocket – это протокол приложения, обеспечивающий семантику реактивных потоков-он функционирует, например, как альтернатива HTTP.
В этом уроке мы рассмотрим сокет, использующий Spring Boot , и, в частности, как он помогает абстрагироваться от API RSocket более низкого уровня.
2. Зависимости
Давайте начнем с добавления зависимости spring-boot-starter-rocket :
org.springframework.boot spring-boot-starter-rsocket
Это приведет к транзитивному втягиванию зависимостей, связанных с ракетой, таких как rocket-core и socket-transport-netty .
3. Пример приложения
Теперь мы продолжим с нашим примером приложения. Чтобы выделить модели взаимодействия, которые предоставляет Сокет, мы создадим приложение для трейдеров. Наше торговое приложение будет состоять из клиента и сервера.
3.1. Настройка сервера
Во-первых, давайте настроим сервер, который будет приложением Spring Boot, загружающим сервер сокетов.
Поскольку у нас есть зависимость spring-boot-starter-socket , Spring Boot автоматически настраивает для нас сервер сокетов. Как обычно при весенней загрузке, мы можем изменить значения конфигурации по умолчанию для сервера сокетов в зависимости от свойств.
Например, давайте изменим порт нашего сервера сокетов, добавив следующую строку в наш файл application.properties :
spring.rsocket.server.port=7000
Мы также можем изменить другие свойства для дальнейшего изменения нашего сервера в соответствии с нашими потребностями.
3.2. Настройка клиента
Далее, давайте настроим клиент, который также будет приложением Spring Boot.
Хотя Spring Boot автоматически настраивает большинство компонентов, связанных с Ракетой, мы также должны определить некоторые компоненты для завершения настройки:
@Configuration public class ClientConfiguration { @Bean public RSocket rSocket() { return RSocketFactory .connect() .mimeType(MimeTypeUtils.APPLICATION_JSON_VALUE, MimeTypeUtils.APPLICATION_JSON_VALUE) .frameDecoder(PayloadDecoder.ZERO_COPY) .transport(TcpClientTransport.create(7000)) .start() .block(); } @Bean RSocketRequester rSocketRequester(RSocketStrategies rSocketStrategies) { return RSocketRequester.wrap(rSocket(), MimeTypeUtils.APPLICATION_JSON, rSocketStrategies); } }
Здесь мы создаем клиент Socket и настраиваем его для использования транспорта TCP на порту 7000. Обратите внимание, что это порт сервера, который мы настроили ранее.
Далее мы определяем R Socket Requester bean, который является оболочкой вокруг Socket . Этот компонент поможет нам при взаимодействии с сервером сокетов.
После определения этих конфигураций бобов у нас есть структура с голыми костями.
Далее мы рассмотрим различные модели взаимодействия и посмотрим, как Spring Boot поможет нам в этом.
4. Запрос/ответ с гнездом и пружинным загрузчиком
Давайте начнем с запроса/Ответа. Это, вероятно, самая распространенная и знакомая модель взаимодействия, поскольку HTTP также использует этот тип связи.
В этой модели взаимодействия клиент инициирует связь и отправляет запрос. После этого сервер выполняет операцию и возвращает ответ клиенту – таким образом, связь завершается.
В нашем приложении для трейдеров клиент запросит текущие рыночные данные по данной акции. В ответ сервер передаст запрошенные данные.
4.1. Сервер
На стороне сервера мы должны сначала создать контроллер для хранения наших методов обработчика. Но вместо @RequestMapping или @GetMapping аннотаций, как в Spring MVC, мы будем использовать @MessageMapping аннотацию :
@Controller public class MarketDataRSocketController { private final MarketDataRepository marketDataRepository; public MarketDataRSocketController(MarketDataRepository marketDataRepository) { this.marketDataRepository = marketDataRepository; } @MessageMapping("currentMarketData") public MonocurrentMarketData(MarketDataRequest marketDataRequest) { return marketDataRepository.getOne(marketDataRequest.getStock()); } }
Итак, давайте исследуем наш контроллер.
Мы используем аннотацию @ Controller для определения обработчика, который должен обрабатывать входящие запросы сокетов. Кроме того, аннотация @MessageMapping позволяет нам определить, какой маршрут нас интересует и как реагировать на запрос.
В этом случае сервер прослушивает текущие рыночные данные маршрут, который возвращает клиенту один результат в виде Mono .
4.2. Клиент
Затем наш клиент Сокета должен запросить текущую цену акции и получить один ответ.
Чтобы инициировать запрос, мы должны использовать запрос сокета или класс:
@RestController public class MarketDataRestController { private final RSocketRequester rSocketRequester; public MarketDataRestController(RSocketRequester rSocketRequester) { this.rSocketRequester = rSocketRequester; } @GetMapping(value = "/current/{stock}") public Publishercurrent(@PathVariable("stock") String stock) { return rSocketRequester .route("currentMarketData") .data(new MarketDataRequest(stock)) .retrieveMono(MarketData.class); } }
Обратите внимание, что в нашем случае клиент сокета также является контроллером СБРОСА, из которого мы вызываем наш сервер сокетов. Итак, мы используем @RestController и @GetMapping для определения конечной точки запроса/ответа.
В методе конечной точки мы используем Запрос сокета R и указание маршрута. Фактически, это маршрут, который ожидает сервер сокетов. Когда мы передаем данные запроса. И, наконец, когда мы вызываем метод retrieve Mono () , Spring Boot инициирует взаимодействие запроса/ответа .
5. Огонь и Забудь С Ракетой и Пружинным Ботинком
Далее мы рассмотрим модель взаимодействия “огонь и забудь”. Как следует из названия, клиент отправляет запрос на сервер, но не ожидает ответа.
В нашем приложении для трейдеров некоторые клиенты будут служить источником данных и будут передавать рыночные данные на сервер.
5.1. Сервер
Давайте создадим еще одну конечную точку в нашем серверном приложении:
@MessageMapping("collectMarketData") public MonocollectMarketData(MarketData marketData) { marketDataRepository.add(marketData); return Mono.empty(); }
Опять же, мы определяем новый @MessageMapping со значением маршрута сбор рыночных данных . Кроме того, Spring Boot автоматически преобразует входящую полезную нагрузку в экземпляр Market Data .
Однако большая разница здесь в том, что мы возвращаем Mono , поскольку клиенту не нужен ответ от нас.
5.2. Клиент
Давайте посмотрим, как мы можем инициировать наш запрос “огонь и забудь”.
Мы создадим еще одну конечную точку ОТДЫХА:
@GetMapping(value = "/collect") public Publishercollect() { return rSocketRequester .route("collectMarketData") .data(getMarketData()) .send(); }
Здесь мы указываем наш маршрут, и нашей полезной нагрузкой будет экземпляр Market Data . Поскольку мы используем метод send() для инициирования запроса вместо retrieve Mono() , модель взаимодействия становится “огонь и забудь” .
6. Поток запросов с гнездом и пружинным загрузчиком
Потоковая передача запросов – это более сложная модель взаимодействия, когда клиент отправляет запрос, но получает несколько ответов с течением времени от сервера.
Чтобы смоделировать эту модель взаимодействия, клиент запросит все рыночные данные по данной акции.
6.1. Сервер
Давайте начнем с нашего сервера. Мы добавим еще один метод сопоставления сообщений:
@MessageMapping("feedMarketData") public FluxfeedMarketData(MarketDataRequest marketDataRequest) { return marketDataRepository.getAll(marketDataRequest.getStock()); }
Как мы видим, этот метод обработчика очень похож на другие. Другое дело, что мы возвращаем Flux<Рыночные данные> вместо Mono<Рыночные данные> . В конце концов, наш сервер сокетов отправит клиенту несколько ответов.
6.2. Клиент
На стороне клиента мы должны создать конечную точку, чтобы инициировать связь между запросом и потоком:
@GetMapping(value = "/feed/{stock}", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Publisherfeed(@PathVariable("stock") String stock) { return rSocketRequester .route("feedMarketData") .data(new MarketDataRequest(stock)) .retrieveFlux(MarketData.class); }
Давайте рассмотрим наш запрос на сокет.
Во-первых, мы определяем маршрут и запрашиваем полезную нагрузку. Затем мы определяем наше ожидание ответа с помощью вызова метода retrieve Flux () /. Это та часть, которая определяет модель взаимодействия.
Также обратите внимание, что, поскольку наш клиент также является сервером REST, он определяет тип носителя ответа как Тип носителя.TEXT_EVENT_STREAM_VALUE.
7. Обработка исключений
Теперь давайте посмотрим, как мы можем обрабатывать исключения в нашем серверном приложении декларативным способом.
При выполнении запроса/ответа мы можем просто использовать аннотацию @MessageExceptionHandler :
@MessageExceptionHandler public MonohandleException(Exception e) { return Mono.just(MarketData.fromException(e)); }
Здесь мы аннотировали наш метод обработчика исключений с помощью @MessageExceptionHandler . В результате он будет обрабатывать все типы исключений, поскольку класс Exception является суперклассом всех остальных.
Мы можем быть более конкретными и создавать различные методы обработки исключений для разных типов исключений.
Это, конечно, для модели запроса/ответа, и поэтому мы возвращаем Mono<Рыночные данные>. Мы хотим, чтобы наш тип возврата здесь соответствовал типу возврата нашей модели взаимодействия.
8. Резюме
В этом уроке мы рассмотрели поддержку сокетов Spring Boot и подробно описали различные модели взаимодействия, которые предоставляет сокет.
Как всегда, вы можете проверить все примеры кода на GitHub .