Spring Webflux 性能测试抛出 PoolAcquirePendingLimitException
Spring Webflux Performance Test throwing PoolAcquirePendingLimitException
我正在努力学习 Spring webflux。我编写了以下代码来测试响应式编程的性能。
这是我的一项服务的控制器:
@RestController
public class PlayerController {
@Autowired
private PlayerRepository playerRepository;
@GetMapping("/players/{id}")
public Mono<Player> getPlayerById(@PathVariable int id) {
return playerRepository.findById(id);
}
型号class:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Player {
@Id
private Integer id;
private String name;
private Integer age;
}
存储库:
@Repository
public interface PlayerRepository extends ReactiveCrudRepository<Player, Integer> {}
以下是调用上述服务的客户端,然后执行数据库调用,然后检查它们是否返回相同的数据。客户端的存储库和模型相同。
@RestController
public class PlayerController {
@Autowired
private PlayerRepository playerRepository;
@Autowired
private WebClient webClient;
@GetMapping("/players/{id}")
public Mono<Response> getPlayerById(@PathVariable int id) {
LocalDateTime start = LocalDateTime.now();
Mono<Player> playerExternal = webClient.get().uri("/players/{id}", id).retrieve().bodyToMono(Player.class);
Mono<Player> playerDB = playerRepository.findById(2);
Mono<Response> result = playerExternal.zipWith(playerDB, (ext, db) -> {
Response response = new Response();
response.setFlag(ext.getId() == db.getId());
LocalDateTime end = LocalDateTime.now();
response.setTimeTaken(end.from(start).until(end, ChronoUnit.MILLIS) + " ms");
return response;
});
return result;
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>reactive-spring</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>reactive-spring</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>dev.miku</groupId>
<artifactId>r2dbc-mysql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
客户端在8080端口运行,调用服务在9090端口运行。我使用的是spring boot 2.5.0。
我也在使用 jmeter 来模拟 10000 个并发请求。在执行 jmeter 测试计划时,出现以下异常,
org.springframework.web.reactive.function.client.WebClientRequestException: Pending acquire queue has reached its maximum size of 1000; nested exception is reactor.netty.internal.shaded.reactor.pool.PoolAcquirePendingLimitException: Pending acquire queue has reached its maximum size of 1000
at org.springframework.web.reactive.function.client.ExchangeFunctions$DefaultExchangeFunction.lambda$wrapException(ExchangeFunctions.java:141) ~[spring-webflux-5.3.7.jar:5.3.7]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ Request to GET http://localhost:9090/players/1 [DefaultWebClient]
|_ checkpoint ⇢ Handler com.example.demo.controller.PlayerController#getPlayerById(int) [DispatcherHandler]
|_ checkpoint ⇢ HTTP GET "/players/1" [ExceptionHandlingWebHandler]
Stack trace:
at org.springframework.web.reactive.function.client.ExchangeFunctions$DefaultExchangeFunction.lambda$wrapException(ExchangeFunctions.java:141) ~[spring-webflux-5.3.7.jar:5.3.7]
at reactor.core.publisher.MonoErrorSupplied.subscribe(MonoErrorSupplied.java:70) ~[reactor-core-3.4.6.jar:3.4.6]
at reactor.core.publisher.Mono.subscribe(Mono.java:4150) ~[reactor-core-3.4.6.jar:3.4.6]
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:103) ~[reactor-core-3.4.6.jar:3.4.6]
at reactor.core.publisher.FluxPeek$PeekSubscriber.onError(FluxPeek.java:221) ~[reactor-core-3.4.6.jar:3.4.6]
at reactor.core.publisher.FluxPeek$PeekSubscriber.onError(FluxPeek.java:221) ~[reactor-core-3.4.6.jar:3.4.6]
at reactor.core.publisher.FluxPeek$PeekSubscriber.onError(FluxPeek.java:221) ~[reactor-core-3.4.6.jar:3.4.6]
at reactor.core.publisher.MonoNext$NextSubscriber.onError(MonoNext.java:93) ~[reactor-core-3.4.6.jar:3.4.6]
at reactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onError(MonoFlatMapMany.java:204) ~[reactor-core-3.4.6.jar:3.4.6]
如果我需要提供完整的堆栈跟踪,请告诉我。如果我正在做任何我不应该做的事情,你能告诉我吗?我也可以分享 jmeter 测试结果。我看到很多请求都失败了,其中大部分都需要很长时间(范围在 1 秒到 18 秒之间)才能响应。
默认情况下WebClient
使用连接池运行。池的默认设置是最大 500 个连接和最大 1000 个挂起请求。您有 JMeter
并尝试模拟 10000,但您没有指定如何分配负载。您可能需要增加最大待处理请求数。看看这个 documentation and this documentation.
如果你想配置 WebClient
那么你需要:
@Bean
public ReactorResourceFactory resourceFactory() {
ConnectionProvider provider =
ConnectionProvider.builder("test")
.maxConnections(500)
// Set custom max pending requests
.pendingAcquireMaxCount(...)
.build();
ReactorResourceFactory factory = new ReactorResourceFactory();
factory.setUseGlobalResources(false);
factory.setConnectionProvider(provider);
return factory;
}
@Bean
public WebClient webClient() {
Function<HttpClient, HttpClient> mapper = client -> {
// Further customizations...
};
ClientHttpConnector connector =
new ReactorClientHttpConnector(resourceFactory(), mapper);
return WebClient.builder().clientConnector(connector).build();
}
我正在努力学习 Spring webflux。我编写了以下代码来测试响应式编程的性能。 这是我的一项服务的控制器:
@RestController
public class PlayerController {
@Autowired
private PlayerRepository playerRepository;
@GetMapping("/players/{id}")
public Mono<Player> getPlayerById(@PathVariable int id) {
return playerRepository.findById(id);
}
型号class:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Player {
@Id
private Integer id;
private String name;
private Integer age;
}
存储库:
@Repository
public interface PlayerRepository extends ReactiveCrudRepository<Player, Integer> {}
以下是调用上述服务的客户端,然后执行数据库调用,然后检查它们是否返回相同的数据。客户端的存储库和模型相同。
@RestController
public class PlayerController {
@Autowired
private PlayerRepository playerRepository;
@Autowired
private WebClient webClient;
@GetMapping("/players/{id}")
public Mono<Response> getPlayerById(@PathVariable int id) {
LocalDateTime start = LocalDateTime.now();
Mono<Player> playerExternal = webClient.get().uri("/players/{id}", id).retrieve().bodyToMono(Player.class);
Mono<Player> playerDB = playerRepository.findById(2);
Mono<Response> result = playerExternal.zipWith(playerDB, (ext, db) -> {
Response response = new Response();
response.setFlag(ext.getId() == db.getId());
LocalDateTime end = LocalDateTime.now();
response.setTimeTaken(end.from(start).until(end, ChronoUnit.MILLIS) + " ms");
return response;
});
return result;
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>reactive-spring</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>reactive-spring</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>dev.miku</groupId>
<artifactId>r2dbc-mysql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
客户端在8080端口运行,调用服务在9090端口运行。我使用的是spring boot 2.5.0。 我也在使用 jmeter 来模拟 10000 个并发请求。在执行 jmeter 测试计划时,出现以下异常,
org.springframework.web.reactive.function.client.WebClientRequestException: Pending acquire queue has reached its maximum size of 1000; nested exception is reactor.netty.internal.shaded.reactor.pool.PoolAcquirePendingLimitException: Pending acquire queue has reached its maximum size of 1000
at org.springframework.web.reactive.function.client.ExchangeFunctions$DefaultExchangeFunction.lambda$wrapException(ExchangeFunctions.java:141) ~[spring-webflux-5.3.7.jar:5.3.7]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ Request to GET http://localhost:9090/players/1 [DefaultWebClient]
|_ checkpoint ⇢ Handler com.example.demo.controller.PlayerController#getPlayerById(int) [DispatcherHandler]
|_ checkpoint ⇢ HTTP GET "/players/1" [ExceptionHandlingWebHandler]
Stack trace:
at org.springframework.web.reactive.function.client.ExchangeFunctions$DefaultExchangeFunction.lambda$wrapException(ExchangeFunctions.java:141) ~[spring-webflux-5.3.7.jar:5.3.7]
at reactor.core.publisher.MonoErrorSupplied.subscribe(MonoErrorSupplied.java:70) ~[reactor-core-3.4.6.jar:3.4.6]
at reactor.core.publisher.Mono.subscribe(Mono.java:4150) ~[reactor-core-3.4.6.jar:3.4.6]
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:103) ~[reactor-core-3.4.6.jar:3.4.6]
at reactor.core.publisher.FluxPeek$PeekSubscriber.onError(FluxPeek.java:221) ~[reactor-core-3.4.6.jar:3.4.6]
at reactor.core.publisher.FluxPeek$PeekSubscriber.onError(FluxPeek.java:221) ~[reactor-core-3.4.6.jar:3.4.6]
at reactor.core.publisher.FluxPeek$PeekSubscriber.onError(FluxPeek.java:221) ~[reactor-core-3.4.6.jar:3.4.6]
at reactor.core.publisher.MonoNext$NextSubscriber.onError(MonoNext.java:93) ~[reactor-core-3.4.6.jar:3.4.6]
at reactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onError(MonoFlatMapMany.java:204) ~[reactor-core-3.4.6.jar:3.4.6]
如果我需要提供完整的堆栈跟踪,请告诉我。如果我正在做任何我不应该做的事情,你能告诉我吗?我也可以分享 jmeter 测试结果。我看到很多请求都失败了,其中大部分都需要很长时间(范围在 1 秒到 18 秒之间)才能响应。
默认情况下WebClient
使用连接池运行。池的默认设置是最大 500 个连接和最大 1000 个挂起请求。您有 JMeter
并尝试模拟 10000,但您没有指定如何分配负载。您可能需要增加最大待处理请求数。看看这个 documentation and this documentation.
如果你想配置 WebClient
那么你需要:
@Bean
public ReactorResourceFactory resourceFactory() {
ConnectionProvider provider =
ConnectionProvider.builder("test")
.maxConnections(500)
// Set custom max pending requests
.pendingAcquireMaxCount(...)
.build();
ReactorResourceFactory factory = new ReactorResourceFactory();
factory.setUseGlobalResources(false);
factory.setConnectionProvider(provider);
return factory;
}
@Bean
public WebClient webClient() {
Function<HttpClient, HttpClient> mapper = client -> {
// Further customizations...
};
ClientHttpConnector connector =
new ReactorClientHttpConnector(resourceFactory(), mapper);
return WebClient.builder().clientConnector(connector).build();
}