Я пытаюсь продемонстрировать преимущества использования Reactive Streams в Spring MVC. Для этого у меня есть небольшой сервер Jetty, работающий с двумя конечными точками:
/normal
возвращает POJO
/flux
возвращает тот же объект, завернутый в Mono
Затем я запускаю клиент и запускаю несколько тысяч одновременных запросов в одной из этих конечных точек. Я ожидал бы увидеть меньше ошибок со второй конечной точкой, где обработка происходит асинхронно. Однако иногда я наблюдаю больше ошибок на конечной точке с поддержкой асинхронизации; в обоих случаях, где-то между 60 - 90% ошибок Connection refused: no further information
.
Либо я здесь что-то делаю не так, либо я не совсем понимаю. Connection refused
- это именно то, чего я бы хотел избежать.
Сервер
Вот мой код с сервера. В случае normal
я буквально блокирую поток с помощью .sleep()
:
@Controller
public class FluxController {
@GetMapping(value = "/normal", produces = MediaType.APPLICATION_JSON_VALUE)
public Map normal() throws Exception {
Thread.sleep(randomTime());
return Collections.singletonMap("type", "normal");
}
@GetMapping(value = "/flux", produces = MediaType.APPLICATION_JSON_VALUE)
public Mono<Map> flux() {
return Mono.delay(Duration.ofMillis(randomTime()))
.map(x -> Collections.singletonMap("type", "flux"));
}
private static long randomTime() {
return ThreadLocalRandom.current().nextLong(200, 1000);
}
}
Сервер работает на Jetty 9.4.15 через Maven и web.xml определяется с помощью:
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1">
Клиент
Мой клиент использует Spring WebClient:
public class ClientApplication {
private static final String ENDPOINT = "normal";
private static final int REPETITIONS = 10_000;
public static void main(String[] args) {
WebClient client = WebClient.create("http://localhost:8080");
AtomicInteger errors = new AtomicInteger(0);
List<Mono<Response>> responses = IntStream.range(0, REPETITIONS)
.mapToObj(i -> client.get()
.uri(ENDPOINT)
.retrieve()
.bodyToMono(Response.class)
.doOnError(e -> errors.incrementAndGet())
.onErrorResume(e -> Mono.empty())
)
.collect(Collectors.toList());
Mono.when(responses)
.block();
System.out.println(String.format("%-2f %% errors", errors.get() * 100.0 / REPETITIONS));
}
static class Response {
public String type;
}
}
Аналогичная предпосылка к вопросу здесь: Асинхронная обработка WebFlux . Основное различие заключается в том, что я проверяю частоту ошибок или количество синхронных соединений; Я не ожидаю увеличения скорости.