负载测试 BindException

Posted

技术标签:

【中文标题】负载测试 BindException【英文标题】:Load Test BindException 【发布时间】:2018-02-15 05:22:48 【问题描述】:

我们有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 Mission Control 进行监控并检查线程阻塞状态。发生异常时,由于 log4j 写入错误 300TPS,线程被阻塞。但这并不能解决为什么我们有这些例外的问题。将日志级别从 Info 降低到 Error。

到目前为止没有任何改进,在某些时候仍然会出现 BindExceptions。有什么建议吗?

【问题讨论】:

您的客户端端口用完了。要么你正在泄漏套接字,要么你只是太快地创建它们。你有很多处于 TIME_WAIT 状态的端口吗? 是的,你是对的,看起来我的连接有些问题。当我输入这个命令时:netstat -an | grep 9090(服务器端口),在客户端我看到 100.000 个连接,其中大部分是 ESTABLISHED 状态。在客户端,所有这些连接都处于 TIME_WAIT 状态。我使用 Spring 的 RestTemplate。我没有为每个连接创建新的rest模板,我将它注入到bean中。 【参考方案1】:

感谢 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;
    

【讨论】:

以上是关于负载测试 BindException的主要内容,如果未能解决你的问题,请参考以下文章

性能测试负载测试压力测试的异同

负载测试 - 恒定负载问题

压力测试、负载测试、并发测试的区别是啥?

什么是压力测试和负载测试,压力测试和负载测试有什么...

VSTS 负载测试 - 如何动态读取当前运行的负载测试文件的运行设置

最大限度。 Visual Studio 负载测试中的负载测试运行持续时间