Рубрики
Без рубрики

Запланированный толчок WebSocket с пружинной загрузкой

В этом уроке мы рассмотрим, как отправлять запланированные сообщения с сервера в браузер с помощью WebSockets.

Автор оригинала: baeldung.

1. Обзор

В этом уроке мы рассмотрим, как отправлять запланированные сообщения с сервера в браузер с помощью WebSockets . Альтернативой может быть использование событий отправки сервера (SSE), но мы не будем рассматривать это в этой статье.

Spring предоставляет множество вариантов планирования. Во-первых, мы рассмотрим аннотацию @Scheduled . Затем мы рассмотрим пример с методом Flux::interval , предоставленным Project Reactor. Эта библиотека доступна из коробки для приложений Webflux и может использоваться в качестве автономной библиотеки в любом проекте Java.

Кроме того, существуют более продвинутые механизмы, такие как планировщик Quartz , но мы не будем их освещать.

2. Простое Приложение Для Чата

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

2.1. Зависимости Maven

Давайте начнем с установки необходимых зависимостей в Maven. Чтобы построить этот проект, наш pom.xml должен был:


    org.springframework.boot
    spring-boot-starter-websocket


    io.projectreactor
    reactor-core


    com.github.javafaker
    javafaker
    1.0.2


    com.google.code.gson
    gson

2.2. Зависимость от JavaFaker

Мы будем использовать библиотеку JavaFaker для генерации сообщений наших ботов. Эта библиотека часто используется для создания тестовых данных. Здесь мы добавим гостя по имени ” Чак Норрис ” в наш чат.

Давайте посмотрим код:

Faker faker = new Faker();
ChuckNorris chuckNorris = faker.chuckNorris();
String messageFromChuck = chuckNorris.fact();

Факер предоставит заводские методы для различных генераторов данных. Мы будем использовать генератор Чака Норриса . Вызов chuck Norris.fact() отобразит случайное предложение из списка предопределенных сообщений.

2.3. Модель данных

Приложение чата использует простой POJO в качестве оболочки сообщения:

public class OutputMessage {

    private String from;
    private String text;
    private String time;

   // standard constructors, getters/setters, equals and hashcode
}

Собрав все это вместе, вот пример того, как мы создаем сообщение чата:

OutputMessage message = new OutputMessage(
  "Chatbot 1", "Hello there!", new SimpleDateFormat("HH:mm").format(new Date())));

2.4. Клиентская сторона

Наш чат-клиент представляет собой простую HTML-страницу. Он использует клиент SockJS и протокол сообщений STOMP .

Давайте посмотрим, как клиент подписывается на тему:



    
    
    


Во-первых, мы создали клиент Stomp по протоколу SockJS. Затем подписка на тему служит каналом связи между сервером и подключенными клиентами.

В нашем репозитории этот код находится в webapp/bots.html . Мы получаем к нему доступ при локальном запуске по адресу http://localhost:8080/bots.html . Конечно, нам нужно настроить хост и порт в зависимости от того, как мы развертываем приложение.

2.5. Серверная часть

Мы видели, как настроить WebSockets весной в предыдущей статье. Давайте немного изменим эту конфигурацию:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic");
        config.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        // ...
        registry.addEndpoint("/chatwithbots");
        registry.addEndpoint("/chatwithbots").withSockJS();
    }
}

Для отправки наших сообщений мы используем служебный класс SimpMessagingTemplate . По умолчанию он доступен как @Bean в контексте Spring. Мы можем видеть, как он объявляется с помощью автоматической настройки, когда AbstractMessageBrokerConfiguration находится в пути к классу. Поэтому мы можем ввести его в любой пружинный компонент.

После этого мы используем его для публикации сообщений в теме /тема/push-сообщения . Мы предполагаем, что наш класс имеет этот компонент, введенный в переменную с именем SimpMessagingTemplate :

simpMessagingTemplate.convertAndSend("/topic/pushmessages", 
  new OutputMessage("Chuck Norris", faker.chuckNorris().fact(), time));

Как показано ранее в нашем примере на стороне клиента, клиент подписывается на эту тему для обработки сообщений по мере их поступления.

3. Планирование Push-Сообщений

В экосистеме Spring мы можем выбирать из множества методов планирования. Если мы используем Spring MVC, аннотация @Scheduled является естественным выбором из-за своей простоты. Если мы используем Spring Web flux, мы также можем использовать метод Flux::interval Project Reactor. Мы рассмотрим по одному примеру каждого из них.

3.1. Конфигурация

Наши чат-боты будут использовать генератор Чака Норриса Java-фейкера. Мы настроим его как боб, чтобы мы могли вводить его туда, где нам это нужно.

@Configuration
class AppConfig {

    @Bean
    public ChuckNorris chuckNorris() {
        return (new Faker()).chuckNorris();
    }
}

3.2. Использование @Scheduled

Наши примеры ботов-это запланированные методы. Когда они запускаются, они отправляют наше Выходное сообщение POJOs через WebSocket, используя SimpMessagingTemplate .

Как следует из его названия, аннотация @Scheduled допускает повторное выполнение методов . С его помощью мы можем использовать простое планирование на основе скорости или более сложные выражения “cron”.

Давайте закодируем нашего первого чат-бота:

@Service
public class ScheduledPushMessages {

    @Scheduled(fixedRate = 5000)
    public void sendMessage(SimpMessagingTemplate simpMessagingTemplate, ChuckNorris chuckNorris) {
        String time = new SimpleDateFormat("HH:mm").format(new Date());
        simpMessagingTemplate.convertAndSend("/topic/pushmessages", 
          new OutputMessage("Chuck Norris (@Scheduled)", chuckNorris().fact(), time));
    }
    
}

Мы аннотируем метод SendMessage с помощью @Scheduled(fixedRate). Это имеет смысл Сообщения запускаться каждые пять секунд. Затем мы используем экземпляр SimpMessagingTemplate для отправки Выходного сообщения в тему. Экземпляры SimpMessagingTemplate и chuck Norris вводятся из контекста Spring в качестве параметров метода.

3.3. Использование потока::интервал()

Если мы используем Web Flux, мы можем использовать оператор Flux::interval . Он опубликует бесконечный поток Длинных элементов, разделенных выбранной Продолжительностью .

Теперь давайте используем Flux в нашем предыдущем примере. Цель будет состоять в том, чтобы посылать цитату из Чака Норриса каждые пять секунд. Во-первых, нам нужно реализовать интерфейс InitializingBean для подписки на Flux при запуске приложения :

@Service
public class ReactiveScheduledPushMessages implements InitializingBean {

    private SimpMessagingTemplate simpMessagingTemplate;

    private ChuckNorris chuckNorris;

    @Autowired
    public ReactiveScheduledPushMessages(SimpMessagingTemplate simpMessagingTemplate, ChuckNorris chuckNorris) {
        this.simpMessagingTemplate = simpMessagingTemplate;
        this.chuckNorris = chuckNorris;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Flux.interval(Duration.ofSeconds(5L))
            // discard the incoming Long, replace it by an OutputMessage
            .map((n) -> new OutputMessage("Chuck Norris (Flux::interval)", 
                              chuckNorris.fact(), 
                              new SimpleDateFormat("HH:mm").format(new Date()))) 
            .subscribe(message -> simpMessagingTemplate.convertAndSend("/topic/pushmessages", message));
    }
}

Здесь мы используем инъекцию конструктора для установки экземпляров SimpMessagingTemplate и chuck Norris . На этот раз логика планирования находится в afterPropertiesSet(), которую мы переопределяем при реализации InitializingBean . Метод будет запущен, как только служба запустится.

Оператор interval выдает Long каждые пять секунд. Затем оператор map отбрасывает это значение и заменяет его нашим сообщением. Наконец, мы подписываемся на Поток , чтобы запустить нашу логику для каждого сообщения.

4. Заключение

В этом уроке мы видели, что класс утилиты SimpMessagingTemplate позволяет легко передавать сообщения сервера через WebSocket. Кроме того, мы рассмотрели два способа планирования выполнения фрагмента кода.

Как всегда, исходный код примеров доступен на GitHub .