【发布时间】:2019-05-10 14:34:26
【问题描述】:
我试图展示在 Spring MVC 中使用 Reactive Streams 的优势。为此,我有一个运行两个端点的小型 Jetty 服务器:
-
/normal返回一个 POJO -
/flux返回包装在Mono中的相同对象
然后我启动一个客户端并在其中一个端点同时启动几千个请求。我本来希望看到第二个端点的错误更少,处理是异步发生的。但是,我有时会在启用异步的端点上观察到 more 错误;在这两种情况下,Connection refused: no further information 的错误率在 60 - 90 % 之间。
要么我做错了,要么我不太明白。 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);
}
}
服务器通过 Maven 在 Jetty 9.4.15 上运行,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 async processing。主要区别在于我正在测试错误率,或同步连接数;我预计速度不会提高。
【问题讨论】:
-
Spring MVC 和 Spring Webflux 是不同的技术。你用哪一个?你到底想证明什么? Servlet 3.0 仅支持阻塞 I/O。
-
技术上这与 WebFlux 没有任何关系。甚至是项目反应堆。通过返回 Deferred 结果可以达到相同的效果。这是在 Servlet 3.1 上运行的。我已经更新了标签以反映这一点。
-
Spring MVC 返回
Mono和返回DeferredResult或多或少是相同的。然而,Webflux 的工作方式不同。因此,使用哪一个会有所不同。异步处理可能会在您的测试场景中引入开销并抵消其潜在优势。 CPU 可能会在线程切换上花费太多时间。
标签: java spring-mvc project-reactor servlet-3.1