Как извлечь содержимое из Mono<List в WebFlux, чтобы передать его по цепочке вызовов

#spring-boot #spring-webflux #project-reactor

Вопрос:

Я хочу иметь возможность извлечь Listlt;Payloadgt; из Monolt;Listlt;Payloadgt;gt; , чтобы передать его в нижестоящую службу для обработки (или, возможно, вернуть из read(RequestParams params) метода, вместо того, чтобы он возвращался void ):

 @PostMapping("/subset")  public void read(@RequestBody RequestParams params){  Monolt;Listlt;Payloadgt;gt; result = reader.read(params.getDate(), params.getAssetClasses(), params.getFirmAccounts(), params.getUserId(), params.getPassword());    ....  }  

где reader.read(...) находится метод в службе autowired Spring, использующий веб-клиент для получения данных из внешнего API веб-службы:

 public Monolt;Listlt;Payloadgt;gt; read(String date, String assetClasses, String firmAccounts, String id, String password) {  Fluxlt;Payloadgt; nodes = client  .get()  .uri(uriBuilder -gt; uriBuilder  .path("/api/subset")  .queryParam("payloads", true)  .queryParam("date", date)  .queryParam("assetClasses", assetClasses)  .queryParam("firmAccounts", firmAccounts)  .build())  .headers(header -gt; header.setBasicAuth("abc123", "XXXXXXX"))  .retrieve()  .onStatus(HttpStatus::is4xxClientError, response -gt; {  System.out.println("4xx error");  return Mono.error(new RuntimeException("4xx"));  })  .onStatus(HttpStatus::is5xxServerError, response -gt; {  System.out.println("5xx error");  return Mono.error(new RuntimeException("5xx"));  })  .bodyToFlux(Payload.class);    Monolt;Listlt;Payloadgt;gt; records = nodes  .collectList();    return records;  }  

Выполнение блокировки result.block() не допускается в WebFlux и создает исключение:

 new IllegalStateException("block()/blockFirst()/blockLast() are blocking, which is not supported in thread ..." ;  

Как правильно извлечь содержимое Моно в WebFlux? Это что-то вроде а subscribe() ? Каков будет синтаксис?

Заранее спасибо.

Ответ №1:

«Правильного пути» не существует, и в этом весь смысл. Чтобы получить значение, вам нужно заблокировать, а блокировка в webflux плохая по многим причинам (в которые я сейчас не буду вдаваться).

Что вам следует сделать, так это полностью вернуть издателя вызывающему клиенту.

Одна из вещей, которую многим обычно трудно понять, заключается в том, что webflux работает с производителем ( Mono или Flux ) и подписчиком.

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

Думайте об этом как о длинной цепочке, которая начинается с источника данных и заканчивается в клиенте, отображающем данные.

Простое эмпирическое правило состоит в том, что кто бы ни был конечным потребителем данных, он является подписчиком, все остальные являются производителями.

Поэтому в вашем случае вы просто возвращаете Monolt;Listlt;Tgt; сообщение вызывающему клиенту.

 @PostMapping("/subset") public Monolt;Listlt;Payloadgt;gt; read(@RequestBody RequestParams params){  Monolt;Listlt;Payloadgt;gt; result = reader.read(params.getDate(), params.getAssetClasses(), params.getFirmAccounts(), params.getUserId(), params.getPassword());    return result; }  

Комментарии:

1. @Toerktumalare Как же тогда получить Listlt;Payloadgt;gt; выход из этого Mono в конце этой цепочки, поскольку блокировка запрещена?

2. если вы этого не сделаете, сама платформа подпишется на вашу цепочку и выдаст ответ вызывающему клиенту. Почему вы чувствуете такую потребность в получении Listlt;Tgt; , если вам нужно работать со списком, который вы используете Mono#map , или Mono#flatMap нет необходимости обращаться к списку вне Mono контекста

3. Клиенту, получающему результат read() вызова, нужен список, который он затем может передать по потоку, поэтому мне нужно извлечь список и вернуть его как тип. Вопрос в том, как это сделать с map flatMap полученным Моно или на нем?

4. @SimeonLeyzerzon ваш вопрос не имеет смысла, я надеюсь, вы об этом знаете. Поскольку функция помечена символом a @PostMapping , я предполагаю, что ваш клиент делает POST запрос. Вы написали I need to extract a List and return it as a type , что вы имеете в виду, что вам нужно вернуть его как тип? Вам нужно, чтобы платформа сериализовала все, что вы возвращаете, в формат, который использует ваш клиент.

5. Код, который я опубликовал, который вы даже не пробовали (иначе вы бы не написали свой странный вопрос), автоматически сериализует http-ответ, содержащий список в формате json, содержащий объект Payload . Пожалуйста, попробуйте код, прежде чем задавать какие-либо дополнительные вопросы. я повторю это еще раз, вы ВООБЩЕ НИКАК НЕ можете получить a Listlt;Payloadgt; без блокировки… ни за что, ни за что, ноль, никогда.

Ответ №2:

В то время как следующее возвращает значение Моно, наблюдаемое в журналах:

 @PostMapping("/subset") @ResponseBody public Monolt;ResponseEntitylt;Listlt;Payloadgt;gt;gt; read1(@RequestBody RequestParams params){  Monolt;Listlt;Payloadgt;gt; result = reader.read(params.getDate(), params.getAssetClasses(), params.getFirmAccounts(), params.getUserId(), params.getPassword());    return result  .map(e -gt; new ResponseEntitylt;Listlt;PayloadByStandardBasisgt;gt;(e, HttpStatus.OK));    }  

понимание, которое я искал, заключалось в правильном способе составления цепочки вызовов с помощью WebFlux, при котором ответ от одного из его операторов/ветвей (материализованный в результате вызова webclient, создающий набор записей, как указано выше) может быть передан другому оператору/ветви, чтобы облегчить побочный эффект сохранения этих записей в БД или что-то в этом роде.

Вероятно, было бы хорошей идеей смоделировать каждый из этих шагов как отдельную конечную точку REST, а затем иметь другую конечную точку для операции компоновки, которая внутренне вызывает каждую независимую конечную точку в правильном порядке, или другие варианты проектирования были бы более предпочтительными?

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

Спасибо.