译:原始性能表格 - Spring Boot 2 Webflux vs. Spring Boot 1

1,575 阅读3分钟
原文链接: www.spring4all.com

原始性能表格 - Spring Boot 2 Webflux vs. Spring Boot 1

原文链接:dzone.com/articles/ra…

作者:Biju Kunjummen

译者:FraserYu

对于IO繁重的工作负载,基于Spring Webflux应用程序的Spring Boot 2要优于基于Spring Boot 1的应用程序, 以下是负载测试的总结性结果 - 对于具有不同并发用户的IO繁忙事务的响应时间:

Boot1VsBoot2

当并发用户数量很少(比如少于1,000)时,Spring Boot 1和Spring Boot 2都能很好地处理负载,并且95%的响应时间仍然保持毫秒级,超过期望值300 ms。

在更高的并发水平上,Spring Boot 2中的异步非阻塞IO和响应式的支持开始显示它们的能力 - 即使有5000个用户的重负载,95%的响应时间仍然在312毫秒左右!而 Spring Boot 1在这些并发级别上则记录了很多失败和高响应时间。

详细

我的性能测试设置如下:

Setup

示例应用暴露出一个端点 (/passthrough/message) ,该端点又顺序调用了下游服务,到端点的请求消息类似这样:

{
  "id": "1",
  "payload": "sample payload",
  "delay": 3000
}

下游服务将根据消息中 "delay" 属性进行延迟(以毫秒为单位)。

Spring Boot 1 应用

我使用Spring Boot 1.5.8.RELEASE作为应用程序的Boot 1版本。 该端点是一个简单的Spring MVC控制器,该控制器使用Spring的 RestTemplate 进行下游服务的调用。 一切都是同步和阻塞的,并且在运行时我使用默认嵌入式的Tomcat容器。 这是下游调用的原始代码:

public MessageAck handlePassthrough(Message message) {
    ResponseEntity<MessageAck> responseEntity = this.restTemplate.postForEntity(targetHost 
                                                            + "/messages", message, MessageAck.class);
    return responseEntity.getBody();
}

Spring Boot 2 应用

该应用程序的 Spring Boot 2 版本暴露出一个基于 Spring Webflux 的端点并且使用 WebClient , 这是对 RestTemplate 的新型非阻塞,响应式的替代方案,用于进行下游服务的调用,我还使用了 Kotlin 进行实现,它不影响性能。 运行时服务是 Netty:

import org.springframework.http.HttpHeaders
import org.springframework.http.MediaType
import org.springframework.web.reactive.function.BodyInserters.fromObject
import org.springframework.web.reactive.function.client.ClientResponse
import org.springframework.web.reactive.function.client.WebClient
import org.springframework.web.reactive.function.client.bodyToMono
import org.springframework.web.reactive.function.server.ServerRequest
import org.springframework.web.reactive.function.server.ServerResponse
import org.springframework.web.reactive.function.server.bodyToMono
import reactor.core.publisher.Mono
class PassThroughHandler(private val webClient: WebClient) {
    fun handle(serverRequest: ServerRequest): Mono<ServerResponse> {
        val messageMono = serverRequest.bodyToMono<Message>()
        return messageMono.flatMap { message ->
            passThrough(message)
                    .flatMap { messageAck ->
                        ServerResponse.ok().body(fromObject(messageAck))
                    }
        }
    }
    fun passThrough(message: Message): Mono<MessageAck> {
        return webClient.post()
                .uri("/messages")
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
                .body(fromObject<Message>(message))
                .exchange()
                .flatMap { response: ClientResponse ->
                    response.bodyToMono<MessageAck>()
                }
    }
}

性能测试详细

测试很简单;对于不同组的并发用户量(300,1000,1500,3000,5000),我发送一个延迟属性设置为 300 毫秒的消息, 并且每个用户在请求之间以 1 到 2 秒的延迟重复该场景 30 次,我正在使用出色的 Gatling 工具来生成这个负载。

结果

这些是通过 Gatling 捕获的结果:

Image title

Image title

Image title

Image title

Image title

参考

示例应用和负载脚本在我的GitHub仓库 my GitHub repo 可用。

本文由spring4all.com翻译小分队创作,采用 知识共享-署名-非商业性使用-相同方式共享 4.0 国际 许可 协议进行许可。