Статьи

Использование rx-java Observable в потоке Spring MVC

Spring MVC уже некоторое время поддерживает поток асинхронной обработки запросов, и эта поддержка внутренне использует асинхронную поддержку Servlet 3 для таких контейнеров, как Tomcat / Jetty.

Поддержка Spring Web Async

Рассмотрим вызов службы, который занимает немного времени для обработки, имитируемого с задержкой:

1
2
3
4
5
6
7
8
public CompletableFuture<Message> getAMessageFuture() {
    return CompletableFuture.supplyAsync(() -> {
        logger.info("Start: Executing slow task in Service 1");
        Util.delay(1000);
        logger.info("End: Executing slow task in Service 1");
        return new Message("data 1");
    }, futureExecutor);
}

Если бы я вызывал этот сервис в потоке пользовательских запросов, традиционный поток контроллера блокировки был бы похож на это:

1
2
3
4
@RequestMapping("/getAMessageFutureBlocking")
public Message getAMessageFutureBlocking() throws Exception {
    return service1.getAMessageFuture().get();
}

Лучшим подходом является использование асинхронной поддержки Spring для возврата результата пользователю, когда он доступен из CompletableFuture, таким образом, не задерживая поток контейнеров:

01
02
03
04
05
06
07
08
09
10
11
12
13
@RequestMapping("/getAMessageFutureAsync")
public DeferredResult<Message> getAMessageFutureAsync() {
    DeferredResult<Message> deffered = new DeferredResult<>(90000);
    CompletableFuture<Message> f = this.service1.getAMessageFuture();
    f.whenComplete((res, ex) -> {
        if (ex != null) {
            deffered.setErrorResult(ex);
        } else {
            deffered.setResult(res);
        }
    });
    return deffered;
}

Использование наблюдаемого в асинхронном потоке

Теперь перейдем к теме этой статьи. В последнее время я использовал превосходный тип Observable в Rx-java в качестве типов, возвращаемых службой, и хотел убедиться, что веб-слой также остается асинхронным при обработке типа Observable, возвращаемого из вызова службы.

Рассмотрим сервис, который был описан выше, теперь модифицированный, чтобы возвращать Observable:

1
2
3
4
5
6
7
8
9
public Observable<Message> getAMessageObs() {
    return Observable.<Message>create(s -> {
        logger.info("Start: Executing slow task in Service 1");
        Util.delay(1000);
        s.onNext(new Message("data 1"));
        logger.info("End: Executing slow task in Service 1");
        s.onCompleted();
    }).subscribeOn(Schedulers.from(customObservableExecutor));
}

Я могу свести на нет все преимущества возврата Observable, заканчивая блокирующим вызовом на веб-слое. Наивный вызов будет следующим:

1
2
3
4
@RequestMapping("/getAMessageObsBlocking")
public Message getAMessageObsBlocking() {
    return service1.getAMessageObs().toBlocking().first();
}

Чтобы сделать этот поток асинхронным через веб-слой, лучшим способом обработки этого вызова является следующий, по сути, путем преобразования Observable в тип Spring DeferredResult:

1
2
3
4
5
6
7
@RequestMapping("/getAMessageObsAsync")
public DeferredResult<Message> getAMessageAsync() {
    Observable<Message> o = this.service1.getAMessageObs();
    DeferredResult<Message> deffered = new DeferredResult<>(90000);
    o.subscribe(m -> deffered.setResult(m), e -> deffered.setErrorResult(e));
    return deffered;
}

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

Если вы заинтересованы в дальнейшем изучении этого, вот репозиторий Github с рабочими образцами: https://github.com/bijukunjummen/spring-web-observable.

Рекомендации:

Справочное руководство Spring по асинхронным потокам на веб-уровне: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-async

Подробнее о Spring DeferredResult от неподражаемого Томаша Нуркевича в блоге NoBlogDefFound — http://www.nurkiewicz.com/2013/03/deferredresult-asynchronous-processing.html

Ссылка: Использование rx-java Observable в потоке Spring MVC от нашего партнера по JCG Биджу Кунджуммена из блога all and sundry.