SpringBoot性能比较:Spring MVC与WebFlux

栏目: Java · 发布时间: 5年前

内容简介:在这里我想谈谈曾经在项目中遇到的有趣的事情。我们为我们的客户在AWS中编写了一些轻量级微服务,它只是通过HTTP代理对某些底层服务的请求,并将其返回给客户端。乍一看,什么可能比编写REST代理服务更简单?所以,当然,我们从Spring Boot开始编写简单的RestControllers。我们做了POC,结果很好。第三方服务具有符合要求的服务响应时间SLA,我们使用此值进行性能测试,第三方服务的响应时间非常好〜大约10-100ms。我们还决定利用CPU作为我们的微服务的扩展策略,这个服务是在Docker中作

在这里我想谈谈曾经在项目中遇到的有趣的事情。我们为我们的客户在AWS中编写了一些轻量级微服务,它只是通过HTTP代理对某些底层服务的请求,并将其返回给客户端。

乍一看,什么可能比编写REST代理服务更简单?

所以,当然,我们从Spring Boot开始编写简单的RestControllers。我们做了POC,结果很好。第三方服务具有符合要求的服务响应时间SLA,我们使用此值进行性能测试,第三方服务的响应时间非常好〜大约10-100ms。我们还决定利用CPU作为我们的微服务的扩展策略,这个服务是在 Docker 中作为AWS ECS服务运行。我们在AWS中配置了自动扩展并上线了。

事实上,并非一切顺利。运行经常超时,我们经常重启AWS ECS任务。我们只是运行很少的任务,另外,看到CPU和内存消耗很低但我们的服务还是太慢,有时甚至有超时错误。

问题在于第三方服务。第三方服务响应时间变为500-1000ms。但它从来没有超时问题,能够处理更多的客户端。

所以关键问题还是在于我们的服务。我们没有在需要时扩展我们的应用程序。我们进行了500-1000毫秒的性能测试,并感到震惊。

CPU很低,内存很好,但我们只能处理200个请求/秒。

这是Servlet线程的连接问题,默认线程池是200,这就是为什么我们在1000毫秒响应时间内有200个请求/秒的原因。

但我们需要一个弹性服务:我们应该处理与底层服务一样多的请求。响应时间应与基础服务几乎相同。

我们研究它并找到了几个选项:

  1. 增加线程池大小
  2. 使用Servlet的DeferredResult或CompletableFuture
  3. Spring与WebFlux反应

选项1:增加线程池大小

是的,这是一个很好的解决方法,但只是解决方法!我们不能将这个值设置为几千,因为它是具有非常有限的内存的Docker。每个线程都需要堆栈内存。

另一个问题是,如果某些第三方服务的响应时间很长,例如,5秒,我们仍会遇到同样的问题。吞吐量等于=线程池大小/响应时间。如果我们有1000个线程和5秒延迟,则吞吐量是200个请求/秒。CPU再次很低,服务有足够的资源进行处理。

选项2:带Servlet的DeferredResult或CompletableFurure(非阻塞)

Servlet 3.1支持异步处理。为了使它工作,我们需要返回一些Promise,Servlet将以异步方式处理它。

我们将DeferredResult与CompletableFurure进行了比较,结果相同。因此,我们同意测试CompletableFurure。

选项3:Spring与WebFlux反应

这是现在最热门的话题。从Spring 文档

“使用少量线程处理并发性并使用更少的硬件资源进行扩展的非阻塞Web堆栈”

测试

测试环境:

Spring Boot:2.1.2.RELEASE(最新)

Java:11 OpenJDK

节点:t2.micro(亚马逊Linux)

代码: https //github.com/Aleksandr-Filichkin/spring-mvc-vs-webflux

Http客户端: Java 11 Http客户端,Apache Http客户端,Spring WebClient

Test-Service(我们的代理服务)公开了几个GET端点进行测试。所有端点都有一个延迟(以毫秒为单位)参数,用于模拟第三方服务延迟。

@GetMapping(value = <font>"/sync"</font><font>)
<b>public</b> String getUserSync(@RequestParam <b>long</b> delay) {
    <b>return</b> sendRequestWithJavaHttpClient(delay).thenApply(x -> </font><font>"sync: "</font><font> + x).join();
}
@GetMapping(value = </font><font>"/completable-future-java-client"</font><font>)
<b>public</b> CompletableFuture<String> getUserUsingWithCFAndJavaClient(@RequestParam <b>long</b> delay) {
    <b>return</b> sendRequestWithJavaHttpClient(delay).thenApply(x -> </font><font>"completable-future-java-client: "</font><font> + x);
}
@GetMapping(value = </font><font>"/completable-future-apache-client"</font><font>)
<b>public</b> CompletableFuture<String> getUserUsingWithCFAndApacheCLient(@RequestParam <b>long</b> delay) {
    <b>return</b> sendRequestWithApacheHttpClient(delay).thenApply(x -> </font><font>"completable-future-apache-client: "</font><font> + x);
}
@GetMapping(value = </font><font>"/webflux-java-http-client"</font><font>)
<b>public</b> Mono<String> getUserUsingWebfluxJavaHttpClient(@RequestParam <b>long</b> delay) {
    CompletableFuture<String> stringCompletableFuture = sendRequestWithJavaHttpClient(delay).thenApply(x -> </font><font>"webflux-java-http-client: "</font><font> + x);
    <b>return</b> Mono.fromFuture(stringCompletableFuture);
}
@GetMapping(value = </font><font>"/webflux-webclient"</font><font>)
<b>public</b> Mono<String> getUserUsingWebfluxWebclient(@RequestParam <b>long</b> delay) {
    <b>return</b> webClient.get().uri(</font><font>"/user/?delay={delay}"</font><font>, delay).retrieve().bodyToMono(String.<b>class</b>).map(x -> </font><font>"webflux-webclient: "</font><font> + x);
}
@GetMapping(value = </font><font>"/webflux-apache-client"</font><font>)
<b>public</b> Mono<String> apache(@RequestParam <b>long</b> delay) {
    <b>return</b> Mono.fromCompletionStage(sendRequestWithApacheHttpClient(delay).thenApply(x -> </font><font>"webflux-apache-client: "</font><font> + x));
}
</font>

User-Service(第三方服务)公开单个端点GET“/ user?delay = {delay}”。延迟(ms)参数用于延迟仿真。如果我们发送/ user?delay = 10,则响应时间将为10 ms +网络延迟(AWS内部最小);

这个用户服务是我们的第三方服务(用户服务),它非常快,可以处理超过4000个请求/秒

对于性能测试,我们将使用Jmeter。我们将测试100,200,400,800个并发请求的服务,延迟10,100,500毫秒。每个实施总共12个测试。

重要的提示:

我们仅针对热服务器测量性能:在每次测试之前,我们的服务处理了100万个请求(用于JIT编译器和JVM优化)

测试代码 https://github.com/Aleksandr-Filichkin/spring-mvc-vs-webflux

测试结果点击标题见原文

结论(在单核,1GB RAM服务器实例上):

Spring Webflux在所有测试情况下都获胜 ,包括使用WebClient和Apache clients情况! 

当底层服务很慢(500ms)时,有最显着的差异(比阻塞Servlet快4倍);它比使用CompetableFuture的非阻塞Servlet快15-20%;此外,与Servlet(20 vs 220)相比,它不会创建大量线程。

不幸的是,我们无法在任何地方使用WebFlux,因为我们需要异步驱动程序/客户端。否则,我们必须创建自定义线程池/包装器。

Servlet阻塞方式仅适用于底层服务快速(10ms)的情况。

Servlet非阻塞方式是一个非常好的解决方案,对于底层服务很慢(500毫秒)的情况。只有在有大量请求的情况下,它才会输给Webflux。

附注:

  • 对于单核,1GB RAM服务器实例,Java 11 Http Client比Apache Http客户端慢(性能降低约30%)
  • Spring WebClient与Apache Http Client(都使用netty)在单核,1GB RAM服务器实例上具有相同的性能
  • 当你只有一个核心和一个小内存时,WebFlux和 Java 11 Http Client的组合运行时模型不能很好地工作( https://github.com/spring-projects/spring-framework/issues/22333

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

图解CSS3

图解CSS3

廖伟华 / 机械工业出版社 / 2014-7-1 / CNY 79.00

本书是CSS3领域的标准性著作,由资深Web前端工程师根据CSS3的最新技术标准撰写。内容极为全面、丰富和翔实,由浅入深地讲解了CSS3新特性的语法、功能和使用技巧,涵盖选择器、边框、背景、文本、颜色、UI、动画、新型盒模型、媒体查询、响应式设计等各种模块;写作方式创新,有趣且易懂,用图解的方式来描述CSS3的每一个特性甚至每一个步骤都配有实战效果图;包含大量案例,实战性强,每个特性都有作者从实践......一起来看看 《图解CSS3》 这本书的介绍吧!

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具