Как использовать сервлет 3.1 в весенний MVC? - PullRequest
12 голосов
/ 01 июля 2019

Доступны 2 различные функции:

  1. сервлет 3.0 позволяет обрабатывать запрос в потоке, отличном от потока контейнера.

  2. сервлет 3.1 позволяет выполнять чтение / запись в сокет без блокировки потока чтения / записи

В интернете много примеров использования сервлета 3.0. Мы можем использовать его весной очень легко. Мы просто должны вернуть DefferedResult или CompletableFuture

Но я не могу найти пример использования сервлета 3.1 весной. Насколько я знаю, мы должны зарегистрировать WriteListener и ReadListener и выполнить грязную работу внутри. Но я не могу найти пример этого слушателя. Я считаю, что это не очень легко.

Не могли бы вы привести пример функции сервлета 3.1 весной с объяснением реализации Listener?

Ответы [ 3 ]

1 голос
/ 09 июля 2019

Не должно быть слишком сложно отследить некоторые примеры. Я нашел один из IBM в WASdev / sample.javaee7.servlet.nonblocking . Для работы с javax.servlet API в Spring или Spring Boot достаточно просто попросить Spring ввести HttpServletRequest или HttpServletResponse. Итак, простой пример может быть:

@SpringBootApplication
@Controller
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @RequestMapping(path = "")
    public void writeStream(HttpServletRequest request, HttpServletResponse response) throws IOException {
        ServletOutputStream output = response.getOutputStream();
        AsyncContext context = request.startAsync();
        output.setWriteListener(new WriteListener() {
            @Override
            public void onWritePossible() throws IOException {
                if ( output.isReady() ) {
                    output.println("WriteListener:onWritePossible() called to send response data on thread : " + Thread.currentThread().getName());
                }
                context.complete();
            }
            @Override
            public void onError(Throwable t) {
                context.complete();
            }
        });
    }
}

Это просто создает WriteListener и присоединяет его к потоку вывода запроса, а затем возвращает. Ничего особенного.

EDITS: Дело в том, что контейнер сервлета, например, Tomcat, вызывает onWritePossible, когда данные могут быть записаны без блокировки. Подробнее об этом на Неблокирующие операции ввода-вывода с использованием Servlet 3.1: Масштабируемые приложения с использованием Java EE 7 (TOTD # 188). .

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

Поэтому onWritePossible вызывается только тогда, когда out.println можно вызывать без блокировки.

Вызов методов setXXXListener указывает, что вместо традиционного ввода-вывода используется неблокирующий ввод-вывод.

Предположительно, что вы должны сделать, проверьте output.isReady, чтобы узнать, можете ли вы продолжать записывать байты. Похоже, что вам придется иметь какое-то неявное соглашение с отправителем / получателем о размерах блоков. Я никогда не использовал его, так что я не знаю, но вы попросили пример этого в среде Spring, и это то, что предоставляется.

Таким образом, onWritePossible вызывается только тогда, когда out.println может быть вызван без блокировки. Это звучит правильно, но как я могу понять, сколько байтов может быть записано? Как мне это контролировать?

РЕДАКТИРОВАТЬ 2: Это очень хороший вопрос, на который я не могу дать вам точный ответ. Я бы предположил, что onWritePossible вызывается, когда сервер выполняет код в отдельном (асинхронном) потоке от основного сервлета. Из примера вы проверяете input.isReady() или output.isReady(), и я предполагаю, что блокирует ваш поток, пока отправитель / получатель не будет готов к большему. Поскольку это делается асинхронно, сам сервер не блокируется и может обрабатывать другие запросы. Я никогда не использовал это, поэтому я не эксперт.

Когда я сказал, что будет какое-то неявное соглашение с отправителем / получателем о размерах блоков, это означает, что, если получатель способен принимать блоки по 1024 байта, вы запишете эту сумму, когда output.isReady истинно. Вы должны были бы знать об этом, читая документацию, ничего в API об этом. В противном случае вы можете написать отдельные байты, но в примере из оракула используются блоки по 1024 байта. 1024-байтовые блоки - это достаточно стандартный размер блока для потокового ввода-вывода. Приведенный выше пример должен быть расширен для записи байтов в цикле while, как показано в примере оракула. Это упражнение осталось для читателя.

Проектный реактор и Spring Webflux имеют концепцию backpressure, которая может решить эту проблему более тщательно. Это был бы отдельный вопрос, и я не изучал, как это объединяет отправителей и получателей (или наоборот).

1 голос
/ 04 июля 2019

Если вы ищете пример неблокирующего объявления API-интерфейса Spring / Servlet 3.1, попробуйте следующее:

@GetMapping(value = "/asyncNonBlockingRequestProcessing")
public CompletableFuture<String> asyncNonBlockingRequestProcessing(){
        ListenableFuture<String> listenableFuture = getRequest.execute(new AsyncCompletionHandler<String>() {
            @Override
            public String onCompleted(Response response) throws Exception {
                logger.debug("Async Non Blocking Request processing completed");
                return "Async Non blocking...";
             }
        });
        return listenableFuture.toCompletableFuture();
}

Требуется поддержка Spring Web 5.0+ и Servlet 3.1 на уровне контейнера сервлетов (Tomcat 8.5+, Jetty 9.4+, WildFly 10+)

0 голосов
/ 18 июля 2019

Для сервлета 3.1 вы можете поддерживать неблокирующий ввод / вывод , используя мост Reactive Streams

Сервлет 3.1+ Контейнер

Для развертывания в качестве WAR в любой контейнер Servlet 3.1+ вы можете расширить и включить в WAR {api-spring-framework} /web/server/adapter/AbstractReactiveWebInitializer.html [AbstractReactiveWebInitializer].Этот класс оборачивает HttpHandler с ServletHttpHandlerAdapter и регистрирует его как сервлет.

Таким образом, вы должны расширить AbstractReactiveWebInitializer , который добавляет асинхронную поддержку

registration.setAsyncSupported(true);

И поддержка в ServletHttpHandlerAdapter

AsyncContext asyncContext = request.startAsync();
...