Объяснение поведения потока в RESTful API в Spring - PullRequest
0 голосов
/ 26 июня 2019

Я работаю над пониманием Webflux и реактивных API Spring и был бы признателен за объяснение различий в поведении потока, который я вижу, когда конечная точка REST объявляется с помощью аннотаций вместо маршрута / обработчика.

Я вижу, что поток (поток?) Выполняется при получении через конечную точку REST, определенную посредством аннотаций, но не из конечной точки REST, определенной с использованием семантики маршрута / обработчика.

Кто-нибудь может объяснить разницу в наблюдаемом поведении? Код и вывод на консоль приведены ниже ...

Примечание: закомментированный код в DemoRequestHandler.getOddIntsMult () заставит поток выполнить и перебрать содержащиеся в нем целые числа, чтобы найти / вернуть нечетные значения. Я думаю, что мой реальный вопрос: «Почему subscribe () требуется в одном случае, а не в другом?»

Аннотированный REST-контроллер ...

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

import static org.springframework.web.bind.annotation.RequestMethod.GET;

@RestController
public class DemoFluxController {

    @RequestMapping(method=GET, value="/v1/fluxMult")
    public Flux<Integer> getMultFlux() {
        System.out.println("DEBUG -> FluxController.getMultFlux()");
        return DemoFlux.getOddInts(DemoFlux.multIntFlux);
    }
}

Тестовый класс, используемый для возврата потока с нечетными целыми числами ...

import reactor.core.publisher.Flux;

public class DemoFlux {

    public static Flux<Integer> multIntFlux = Flux.range(1, 20);

    private static boolean isOdd(Integer intVal) {
        System.out.printf("DEBUG -> DemoFlux.isOdd( %d )%n", intVal);
        return intVal % 2 != 0;
    }

    public static Flux<Integer> getOddInts(Flux<Integer> intFlux) {
        return intFlux.filter(DemoFlux::isOdd);
    }
}

Реализация маршрутизатора, объявляющая альтернативную конечную точку REST ...

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;

@Configuration
public class DemoRouter {

    @Bean
    public RouterFunction<ServerResponse> route(DemoRequestHandler requestHandler) {
        return RouterFunctions.route(RequestPredicates.GET("/v2/fluxMult")
                          .and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), requestHandler::getOddIntsMult);
    }
}

Обработчик запроса, связанный с маршрутом для конечной точки REST.

import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

@Component
public class DemoRequestHandler {

    public Mono<ServerResponse> getOddIntsMult(ServerRequest request) {
        System.out.println("DEBYG -> DemoRequestHandler.getOddIntsMult()");

        return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
            .body(BodyInserters.fromObject(DemoFlux.getOddInts(DemoFlux.multIntFlux)));
//                .body(BodyInserters.fromObject(DemoFlux.getOddInts(DemoFlux.multIntFlux).subscribe()));
    }
}

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

DEBUG -> DemoFluxController.getMultFlux()
DEBUG -> DemoFlux.getOddInts()
DEBUG -> DemoFlux.isOdd( 1 )
DEBUG -> DemoFlux.isOdd( 2 )
DEBUG -> DemoFlux.isOdd( 3 )
DEBUG -> DemoFlux.isOdd( 4 )
DEBUG -> DemoFlux.isOdd( 5 )
DEBUG -> DemoFlux.isOdd( 6 )
DEBUG -> DemoFlux.isOdd( 7 )
DEBUG -> DemoFlux.isOdd( 8 )
DEBUG -> DemoFlux.isOdd( 9 )
DEBUG -> DemoFlux.isOdd( 10 )
DEBUG -> DemoFlux.isOdd( 11 )
DEBUG -> DemoFlux.isOdd( 12 )
DEBUG -> DemoFlux.isOdd( 13 )
DEBUG -> DemoFlux.isOdd( 14 )
DEBUG -> DemoFlux.isOdd( 15 )
DEBUG -> DemoFlux.isOdd( 16 )
DEBUG -> DemoFlux.isOdd( 17 )
DEBUG -> DemoFlux.isOdd( 18 )
DEBUG -> DemoFlux.isOdd( 19 )
DEBUG -> DemoFlux.isOdd( 20 )

DEBUG -> DemoRequestHandler.getOddIntsMult()
DEBUG -> DemoFlux.getOddInts()

1 Ответ

0 голосов
/ 26 июня 2019

они не действуют одинаково, потому что вы не закодировали их одинаково.

Одна из самых распространенных ошибок, которые люди допускают при работе с webflux / реактивным программированием, заключается в том, что они подписываются в середине своих приложений.потому что они хотят или нуждаются в конкретном значении чего-либо.

WebFlux / реактивное программирование в основном заключается в том, что мы объявляем цепочку событий, которые мы хотим, чтобы происходило, когда кто-то подписывается.И в этом случае это когда клиент подписывается.Так что очень приятно, что вы чувствуете, что что-то не так, потому что есть!

Ваша проблема здесь:

public Mono<ServerResponse> getOddIntsMult(ServerRequest request) {
    System.out.println("DEBYG -> DemoRequestHandler.getOddIntsMult()");

    return ServerResponse.ok()
        .contentType(MediaType.APPLICATION_JSON)
        .body(BodyInserters.fromObject(              // This body inserter here
            DemoFlux.getOddInts(DemoFlux.multIntFlux)));
}

Что происходит, когда BodyInserter ожидает конкретное значение, и есливы заглядываете в исходный код, и видите, что тело вставляет тело в моно.То, что вы делаете, это то, что он оборачивает Flux в Mono, так что вы, по сути, получаете Mono<Flux<Integer>>.

После того, как вы поработали некоторое время с webflux, вы можете сказать, что этослучай, когда вы получаете что-то вроде:

{
    "disposed": true,
    "scanAvailable": true
}

Тогда вы знаете, что получаете упакованный моно / поток и пропустили куда-то плоскую карту.

Если переписать и удалить BodyInserterон будет работать как положено.

public Mono<ServerResponse> getOddIntsMult(ServerRequest request) {
    System.out.println("DEBYG -> DemoRequestHandler.getOddIntsMult()");

    return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
            .body(DemoFlux.getOddInts(DemoFlux.multIntFlux), Integer.class);                
}

Таким образом, нет никакой разницы, ваш @RequestMapping(method=GET, value="/v1/fluxMult") не использует DemoRequestHandler, следовательно, вы получите другие результаты.

...