Надіслані сервером події навесні

1. Огляд

У цьому посібнику ми побачимо, як ми можемо впровадити API-інтерфейси, що базуються на сервері, з Spring.

Простіше кажучи, Server-Sent-Events, або коротше SSE - це стандарт HTTP, який дозволяє веб-програмі обробляти односпрямований потік подій та отримувати оновлення щоразу, коли сервер видає дані.

Версія Spring 4.2 його вже підтримувала, але починаючи з Spring 5, тепер у нас є більш ідіоматичний та зручний спосіб з нею працювати.

2. SSE з Spring 5 Webflux

Для цього ми можемо використовувати такі реалізації, як клас Flux, наданий бібліотекою Reactor , або потенційно сутність ServerSentEvent , яка надає нам контроль над метаданими подій.

2.1. Трансляція подій за допомогою Flux

Flux - це реактивне представлення потоку подій - воно обробляється по-різному залежно від вказаного запиту або типу носія відповіді.

Щоб створити кінцеву точку потокового передавання SSE, нам доведеться слідувати специфікаціям W3C та визначити її тип MIME як текст / потік подій :

@GetMapping(path = "/stream-flux", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux streamFlux() { return Flux.interval(Duration.ofSeconds(1)) .map(sequence -> "Flux - " + LocalTime.now().toString()); }

Метод інтервалу створює потік, який поступово випромінює довгі значення. Потім ми прив'язуємо ці значення до бажаного результату.

Давайте розпочнемо наш додаток і спробуємо його, переглянувши тоді кінцеву точку.

Ми побачимо, як браузер реагує на події, які сервер штовхає за секундою. Для отримання додаткової інформації про потік та ядро реактора ми можемо ознайомитися з цим постом.

2.2. Використання елемента ServerSentEvent

Тепер ми обернемо наш вихідний рядок в об'єкт ServerSentSevent і вивчимо переваги цього:

@GetMapping("/stream-sse") public Flux
    
      streamEvents() { return Flux.interval(Duration.ofSeconds(1)) .map(sequence -> ServerSentEvent. builder() .id(String.valueOf(sequence)) .event("periodic-event") .data("SSE - " + LocalTime.now().toString()) .build()); }
    

Як ми можемо оцінити, є кілька переваг використання сутності ServerSentEvent :

  1. ми можемо обробляти метадані подій, які нам потрібні в реальному випадку
  2. ми можемо ігнорувати декларацію типу « текст / потік подій »

У цьому випадку ми вказали ідентифікатор , назву події та, що найголовніше, фактичні дані події.

Крім того, ми могли б додати атрибут comments та значення повторної спроби , яке визначатиме час повторного підключення, який використовуватиметься при спробі надіслати подію.

2.3. Споживання відправлених сервером подій за допомогою WebClient

Тепер давайте споживатимемо наш потік подій за допомогою WebClient .:

public void consumeServerSentEvent() { WebClient client = WebClient.create("//localhost:8080/sse-server"); ParameterizedTypeReference
    
      type = new ParameterizedTypeReference
     
      () {}; Flux
      
        eventStream = client.get() .uri("/stream-sse") .retrieve() .bodyToFlux(type); eventStream.subscribe( content -> logger.info("Time: {} - event: name[{}], id [{}], content[{}] ", LocalTime.now(), content.event(), content.id(), content.data()), error -> logger.error("Error receiving SSE: {}", error), () -> logger.info("Completed!!!")); }
      
     
    

Метод передплати дозволяє нам вказати, як ми будемо діяти, коли ми успішно отримаємо подію, коли трапиться помилка та коли трансляція завершиться.

У нашому прикладі ми використовували метод отримання , який є простим і зрозумілим способом отримання тіла відповіді.

Цей метод автоматично видає WebClientResponseException, якщо ми отримуємо відповідь 4xx або 5xx, якщо ми не обробляємо сценарії, додаючи оператор onStatus .

З іншого боку, ми могли б також використовувати метод обміну , який забезпечує доступ до ClientResponse, а також не сигналізує про помилки при невдалих відповідях.

Ми повинні враховувати, що ми можемо обійти обгортку ServerSentEvent, якщо нам не потрібні метадані події.

3. Потік SSE у весняному MVC

Як ми вже говорили, специфікація SSE підтримувалася з весни 4.2, коли був представлений клас SseEmitter .

Простіше кажучи, ми визначимо ExecutorService , потік, де SseEmitter буде виконувати свою роботу, надсилаючи дані, і повертати екземпляр випромінювача, залишаючи з'єднання відкритим таким чином:

@GetMapping("/stream-sse-mvc") public SseEmitter streamSseMvc() { SseEmitter emitter = new SseEmitter(); ExecutorService sseMvcExecutor = Executors.newSingleThreadExecutor(); sseMvcExecutor.execute(() -> { try { for (int i = 0; true; i++) { SseEventBuilder event = SseEmitter.event() .data("SSE MVC - " + LocalTime.now().toString()) .id(String.valueOf(i)) .name("sse event - mvc"); emitter.send(event); Thread.sleep(1000); } } catch (Exception ex) { emitter.completeWithError(ex); } }); return emitter; }

Завжди переконайтеся, що вибрали відповідну службу ExecutorService для вашого сценарію використання.

Ми можемо дізнатись більше про SSE у Spring MVC та поглянути на інші приклади, прочитавши цей цікавий підручник.

4. Розуміння подій, надісланих сервером

Тепер, коли ми знаємо, як реалізувати кінцеві точки SSE, спробуємо трохи глибше зрозуміти деякі базові концепції.

SSE - це специфікація, прийнята більшістю браузерів, щоб дозволити потокове передавання подій односпрямовано в будь-який час.

"Події" - це лише потік закодованих текстових даних UTF-8, які відповідають формату, визначеному специфікацією.

Цей формат складається з ряду елементів ключ-значення (ідентифікатор, повторна спроба, дані та подія, що вказує ім'я), розділених розривами рядків.

Коментарі також підтримуються.

Специфікація жодним чином не обмежує формат корисного навантаження даних; ми можемо використовувати простий рядок або більш складну структуру JSON або XML.

Останній момент, який ми повинні взяти до уваги, - це різниця між використанням потокового передавання SSE та WebSockets .

Хоча WebSockets пропонують повнодуплексний (двонаправлений) зв’язок між сервером і клієнтом, тоді як SSE використовує односпрямований зв’язок.

Крім того, WebSockets не є протоколом HTTP, і, на відміну від SSE, він не пропонує стандарти обробки помилок.

5. Висновок

Підводячи підсумок, у цій статті ми дізналися основні концепції потокового передавання SSE, що, безсумнівно, є чудовим ресурсом, який дозволить нам створювати системи наступного покоління.

Зараз ми в чудовому стані зрозуміти, що відбувається під капотом, коли ми використовуємо цей протокол.

Крім того, ми доповнили теорію кількома простими прикладами, які можна знайти у нашому сховищі Github.