负载测试 BindException
Load Test BindException
我们有2个APP,一个是服务端,一个是客户端。
服务器在 weblogic (APP A) 上,客户端在 spring-boot(在 tomcat - APP B 上)。当我们进行 A 向 B 发送请求的负载测试时。但是在某个时间点之后,有一段时间出现大量的 Bind Exception 错误,比负载测试保持 运行 正常,比 Bind Exception,比正常等。如果我们使用更高的 TPS 进行负载测试,我们会更频繁地遇到这些异常。这是场景:
- 负载测试保持 运行,没有错误,300 TPS,消息计数 10.000
- 负载测试保持 运行,没有错误,300 TPS,消息数 30.000
- 绑定异常,300 TPS,消息数 32.000
- 负载测试保持 运行,没有错误,300 TPS,消息数 40.000
- 负载测试保持 运行,没有错误,300 TPS,消息数 50.000
- 绑定异常,300 TPS,消息数 52.000
- 负载测试保持 运行,没有错误,300 TPS,消息数 60.000
...
...
错误是:
org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://XXXXXXX:9090/api/8252": Cannot assign requested address; nested exception is java.net.BindException: Cannot assign requested address
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:666)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613)
at org.springframework.web.client.RestTemplate.postForLocation(RestTemplate.java:355)
at com.ttech.tims.tes.pushws.impl.PushConsumerThread.tryToSendPushRequest(PushConsumerThread.java:207)
at com.ttech.tims.tes.pushws.impl.PushConsumerThread.pushMessage(PushConsumerThread.java:162)
at com.ttech.tims.tes.pushws.impl.PushConsumerThread.run(PushConsumerThread.java:350)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.net.BindException: Cannot assign requested address
at sun.nio.ch.Net.connect0(Native Method)
at sun.nio.ch.Net.connect(Net.java:454)
at sun.nio.ch.Net.connect(Net.java:446)
at sun.nio.ch.SocketChannelImpl.connect(SocketChannelImpl.java:648)
at weblogic.socket.NIOSocketMuxer.newSocket(NIOSocketMuxer.java:432)
at weblogic.socket.NIOSocketMuxer.newSocket(NIOSocketMuxer.java:364)
at weblogic.socket.ChannelSocketFactory.createSocket(ChannelSocketFactory.java:98)
at weblogic.net.http.HttpClient.openServer(HttpClient.java:384)
at weblogic.net.http.HttpClient.openServer(HttpClient.java:511)
at weblogic.net.http.HttpClient.New(HttpClient.java:313)
at weblogic.net.http.HttpClient.New(HttpClient.java:292)
at weblogic.net.http.HttpURLConnection.connect(HttpURLConnection.java:295)
at org.springframework.http.client.SimpleBufferingClientHttpRequest.executeInternal(SimpleBufferingClientHttpRequest.java:78)
at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:652)
... 6 more
到目前为止我尝试过的:
- 增加客户端最大线程数(application.properties 从 100 到 500 等)
- 通过控制台将 weblogics 最大并发线程数从 100 增加到 200(300、400 等)
- 已检查服务器和客户端的最大打开文件数。
- 检查服务器和客户端之间的防火墙。
- 使用 Java 任务控制进行监控并检查线程阻塞状态。发生异常时300TPS的log4j写入错误导致线程阻塞。但这并不能解决为什么我们有这些例外的问题。将日志级别从信息降低到错误。
到目前为止没有任何改进,在某些时候仍然会出现 BindExceptions。有什么建议吗?
感谢 EJP 的建议。这是我解决问题的方法。 Spring Rest 模板正在等待 unix 关闭连接。由于这个原因,在我达到最大套接字连接数后,连接会在 TIME_WAIT 状态下等待。当我们将 Apache ClientHttpRequestFactory 作为 spring RestTemplate 的工厂发送时,apache 会使用其连接池处理请求。这是实现:
@Bean("apacheRequestFactory")
public ClientHttpRequestFactory createRequestFactory() {
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
// maximum connections in the pool
connectionManager.setMaxTotal(this.systemPropertyBean.getPushConsumerThreadCnt());
// maximum concurrent connection to the hosts is equal to the our push thread count.
connectionManager.setDefaultMaxPerRoute(this.systemPropertyBean.getPushConsumerThreadCnt());
RequestConfig config = RequestConfig.custom()
.setConnectTimeout(this.systemPropertyBean.getPushTimeoutMillis())// 3 sn
.setConnectionRequestTimeout(this.systemPropertyBean.getPushTimeoutMillis())
.setSocketTimeout(this.systemPropertyBean.getPushTimeoutMillis()).build(); // read timeout
/* the Connection Timeout (http.connection.timeout) – the time to establish the connection with the remote host
the Socket Timeout (http.socket.timeout) – the time waiting for data – after the connection was established; maximum time of inactivity between two data packets
the Connection Manager Timeout (http.connection-manager.timeout) – the time to wait for a connection from the connection manager/pool
*/
CloseableHttpClient httpClient = HttpClientBuilder.create().setConnectionManager(connectionManager).setDefaultRequestConfig(config).build();
return new HttpComponentsClientHttpRequestFactory(httpClient);
}
@Bean
public RestTemplate restTemplate() {
// I was using SimpleClientHttpRequestFactory before.
RestTemplate restTemplate = new RestTemplate(createRequestFactory());
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
return restTemplate;
}
我们有2个APP,一个是服务端,一个是客户端。
服务器在 weblogic (APP A) 上,客户端在 spring-boot(在 tomcat - APP B 上)。当我们进行 A 向 B 发送请求的负载测试时。但是在某个时间点之后,有一段时间出现大量的 Bind Exception 错误,比负载测试保持 运行 正常,比 Bind Exception,比正常等。如果我们使用更高的 TPS 进行负载测试,我们会更频繁地遇到这些异常。这是场景:
- 负载测试保持 运行,没有错误,300 TPS,消息计数 10.000
- 负载测试保持 运行,没有错误,300 TPS,消息数 30.000
- 绑定异常,300 TPS,消息数 32.000
- 负载测试保持 运行,没有错误,300 TPS,消息数 40.000
- 负载测试保持 运行,没有错误,300 TPS,消息数 50.000
- 绑定异常,300 TPS,消息数 52.000
- 负载测试保持 运行,没有错误,300 TPS,消息数 60.000 ... ...
错误是:
org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://XXXXXXX:9090/api/8252": Cannot assign requested address; nested exception is java.net.BindException: Cannot assign requested address
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:666)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613)
at org.springframework.web.client.RestTemplate.postForLocation(RestTemplate.java:355)
at com.ttech.tims.tes.pushws.impl.PushConsumerThread.tryToSendPushRequest(PushConsumerThread.java:207)
at com.ttech.tims.tes.pushws.impl.PushConsumerThread.pushMessage(PushConsumerThread.java:162)
at com.ttech.tims.tes.pushws.impl.PushConsumerThread.run(PushConsumerThread.java:350)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.net.BindException: Cannot assign requested address
at sun.nio.ch.Net.connect0(Native Method)
at sun.nio.ch.Net.connect(Net.java:454)
at sun.nio.ch.Net.connect(Net.java:446)
at sun.nio.ch.SocketChannelImpl.connect(SocketChannelImpl.java:648)
at weblogic.socket.NIOSocketMuxer.newSocket(NIOSocketMuxer.java:432)
at weblogic.socket.NIOSocketMuxer.newSocket(NIOSocketMuxer.java:364)
at weblogic.socket.ChannelSocketFactory.createSocket(ChannelSocketFactory.java:98)
at weblogic.net.http.HttpClient.openServer(HttpClient.java:384)
at weblogic.net.http.HttpClient.openServer(HttpClient.java:511)
at weblogic.net.http.HttpClient.New(HttpClient.java:313)
at weblogic.net.http.HttpClient.New(HttpClient.java:292)
at weblogic.net.http.HttpURLConnection.connect(HttpURLConnection.java:295)
at org.springframework.http.client.SimpleBufferingClientHttpRequest.executeInternal(SimpleBufferingClientHttpRequest.java:78)
at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:652)
... 6 more
到目前为止我尝试过的:
- 增加客户端最大线程数(application.properties 从 100 到 500 等)
- 通过控制台将 weblogics 最大并发线程数从 100 增加到 200(300、400 等)
- 已检查服务器和客户端的最大打开文件数。
- 检查服务器和客户端之间的防火墙。
- 使用 Java 任务控制进行监控并检查线程阻塞状态。发生异常时300TPS的log4j写入错误导致线程阻塞。但这并不能解决为什么我们有这些例外的问题。将日志级别从信息降低到错误。
到目前为止没有任何改进,在某些时候仍然会出现 BindExceptions。有什么建议吗?
感谢 EJP 的建议。这是我解决问题的方法。 Spring Rest 模板正在等待 unix 关闭连接。由于这个原因,在我达到最大套接字连接数后,连接会在 TIME_WAIT 状态下等待。当我们将 Apache ClientHttpRequestFactory 作为 spring RestTemplate 的工厂发送时,apache 会使用其连接池处理请求。这是实现:
@Bean("apacheRequestFactory")
public ClientHttpRequestFactory createRequestFactory() {
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
// maximum connections in the pool
connectionManager.setMaxTotal(this.systemPropertyBean.getPushConsumerThreadCnt());
// maximum concurrent connection to the hosts is equal to the our push thread count.
connectionManager.setDefaultMaxPerRoute(this.systemPropertyBean.getPushConsumerThreadCnt());
RequestConfig config = RequestConfig.custom()
.setConnectTimeout(this.systemPropertyBean.getPushTimeoutMillis())// 3 sn
.setConnectionRequestTimeout(this.systemPropertyBean.getPushTimeoutMillis())
.setSocketTimeout(this.systemPropertyBean.getPushTimeoutMillis()).build(); // read timeout
/* the Connection Timeout (http.connection.timeout) – the time to establish the connection with the remote host
the Socket Timeout (http.socket.timeout) – the time waiting for data – after the connection was established; maximum time of inactivity between two data packets
the Connection Manager Timeout (http.connection-manager.timeout) – the time to wait for a connection from the connection manager/pool
*/
CloseableHttpClient httpClient = HttpClientBuilder.create().setConnectionManager(connectionManager).setDefaultRequestConfig(config).build();
return new HttpComponentsClientHttpRequestFactory(httpClient);
}
@Bean
public RestTemplate restTemplate() {
// I was using SimpleClientHttpRequestFactory before.
RestTemplate restTemplate = new RestTemplate(createRequestFactory());
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
return restTemplate;
}