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

Потоковая передача Spring MVC и обработка запросов SSE

Узнайте, как создавать асинхронные и потоковые ответы на данные с помощью Spring MVC 5 и использовать их с помощью простого клиента AJAX.

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

1. введение

Этот простой учебник демонстрирует использование нескольких асинхронных и потоковых объектов в Spring MVC 5.x.x.

В частности, мы рассмотрим три ключевых класса:

  • ResponseBodyEmitter
  • SseEmitter
  • Потоковый ответ

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

2. ResponseBodyEmitter

ResponseBodyEmitter обрабатывает асинхронные ответы.

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

2.1. Серверная сторона

Лучше использовать ResponseBodyEmitter вместе с его собственным выделенным асинхронным потоком и обернутым ResponseEntity (в который мы можем напрямую ввести эмиттер ):

@Controller
public class ResponseBodyEmitterController {
 
    private ExecutorService executor 
      = Executors.newCachedThreadPool();

    @GetMapping("/rbe")
    public ResponseEntity handleRbe() {
        ResponseBodyEmitter emitter = new ResponseBodyEmitter();
        executor.execute(() -> {
            try {
                emitter.send(
                  "/rbe" + " @ " + new Date(), MediaType.TEXT_PLAIN);
                emitter.complete();
            } catch (Exception ex) {
                emitter.completeWithError(ex);
            }
        });
        return new ResponseEntity(emitter, HttpStatus.OK);
    }
}

Таким образом, в приведенном выше примере мы можем обойти необходимость использования Completablefuture , более сложных асинхронных обещаний или использования аннотации @Async .

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

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

Для использования на стороне клиента мы можем использовать простой метод XHR и вызывать наши конечные точки API так же, как в обычной операции AJAX:

var xhr = function(url) {
    return new Promise(function(resolve, reject) {
        var xmhr = new XMLHttpRequest();
        //...
        xmhr.open("GET", url, true);
        xmhr.send();
       //...
    });
};

xhr('http://localhost:8080/javamvcasync/rbe')
  .then(function(success){ //... });

3. SseEmitter

SseEmitter на самом деле является подклассом ResponseBodyEmitter и обеспечивает дополнительную Отправленное сервером событие (SSE) поддержку из коробки.

3.1. Серверная сторона

Итак, давайте быстро рассмотрим пример контроллера, использующего эту мощную сущность:

@Controller
public class SseEmitterController {
    private ExecutorService nonBlockingService = Executors
      .newCachedThreadPool();
    
    @GetMapping("/sse")
    public SseEmitter handleSse() {
         SseEmitter emitter = new SseEmitter();
         nonBlockingService.execute(() -> {
             try {
                 emitter.send("/sse" + " @ " + new Date());
                 // we could send more events
                 emitter.complete();
             } catch (Exception ex) {
                 emitter.completeWithError(ex);
             }
         });
         return emitter;
    }   
}

Довольно стандартный тариф, но мы заметим несколько отличий между этим и нашим обычным контроллером ОТДЫХА:

  • Во-первых, мы возвращаем SseEmitter
  • Кроме того, мы заключаем основную информацию об ответе в ее собственный Поток
  • Наконец, мы отправляем информацию об ответе с помощью emitter.send()

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

На этот раз наш клиент работает немного по-другому, так как мы можем использовать постоянно подключенное Отправленное сервером событие Библиотека:

var sse = new EventSource('http://localhost:8080/javamvcasync/sse');
sse.onmessage = function (evt) {
    var el = document.getElementById('sse');
    el.appendChild(document.createTextNode(evt.data));
    el.appendChild(document.createElement('br'));
};

4. Потоковый ответ

Наконец, мы можем использовать Streaming ResponseBody для записи непосредственно в OutputStream перед передачей этой записанной информации обратно клиенту с помощью ResponseEntity.

4.1. Серверная сторона

@Controller
public class StreamingResponseBodyController {
 
    @GetMapping("/srb")
    public ResponseEntity handleRbe() {
        StreamingResponseBody stream = out -> {
            String msg = "/srb" + " @ " + new Date();
            out.write(msg.getBytes());
        };
        return new ResponseEntity(stream, HttpStatus.OK);
    }
}

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

Как и раньше, мы будем использовать обычный метод XHR для доступа к контроллеру выше:

var xhr = function(url) {
    return new Promise(function(resolve, reject) {
        var xmhr = new XMLHttpRequest();
        //...
        xmhr.open("GET", url, true);
        xmhr.send();
        //...
    });
};

xhr('http://localhost:8080/javamvcasync/srb')
  .then(function(success){ //... });

Далее давайте рассмотрим некоторые успешные примеры использования этих примеров.

5. Сведение Всего Этого Воедино

После того, как мы успешно скомпилировали наш сервер и запустили наш клиент выше (доступ к предоставленному index.jsp ), мы должны увидеть следующее в нашем браузере: