使用 Apache `httpclient` lib 与本地主机建立 1000 个连接时出现问题

问题描述 投票:0回答:1

我正在尝试为 Web 服务器编写一个测试用例,该服务器接受带有小型 JSON 正文的 POST 请求。

我试图模拟每秒数百个请求。 我使用

PoolingHttpClientConnectionManager
来尝试最小化测试程序本身的工作负载,因为我有兴趣了解服务器如何响应这种负载。 我在与服务器相同的机器上运行测试,因此连接到
http://localhost:8080/example

实际的测试程序相当冗长,因为它涉及生成合理的数据,但我遇到的核心问题是:

    //
    // http connection pool is set up like this:
    //
    httpPool = new PoolingHttpClientConnectionManager();
    httpPool.setMaxTotal(100);
    httpPool.setDefaultMaxPerRoute(100);

    //
    // then this is called frequently:
    //
    private void post(String url, String json) throws IOException {
        exec.submit(() -> {
            try  {
                CloseableHttpClient client = HttpClients.custom().setConnectionManager(httpPool).build();
                HttpPost post = new HttpPost(url);
                StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
                post.setEntity(entity);

                try (CloseableHttpResponse response = client.execute(post)) {
                    //log(url + ": " + EntityUtils.toString(response.getEntity()));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

目前运行良好,速度约为 150 个请求/秒。 大约一分钟后它就死了,每个请求都会抛出这个异常:

org.apache.hc.client5.http.HttpHostConnectException: Connect to http://localhost:8080 [localhost/127.0.0.1, localhost/0:0:0:0:0:0:0:1] failed: Connection refused: getsockopt
    at java.base/sun.nio.ch.Net.pollConnect(Native Method)
    at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:682)
    at java.base/sun.nio.ch.NioSocketImpl.timedFinishConnect(NioSocketImpl.java:542)
    at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:592)
    at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327)
    at java.base/java.net.Socket.connect(Socket.java:751)
    at [email protected]/org.apache.hc.client5.http.socket.PlainConnectionSocketFactory$1.run(PlainConnectionSocketFactory.java:87)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:571)
    at [email protected]/org.apache.hc.client5.http.socket.PlainConnectionSocketFactory.connectSocket(PlainConnectionSocketFactory.java:84)
    at [email protected]/org.apache.hc.client5.http.impl.io.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:148)
    at [email protected]/org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:407)
    at [email protected]/org.apache.hc.client5.http.impl.classic.InternalExecRuntime.connectEndpoint(InternalExecRuntime.java:168)
    at [email protected]/org.apache.hc.client5.http.impl.classic.InternalExecRuntime.connectEndpoint(InternalExecRuntime.java:178)
    at [email protected]/org.apache.hc.client5.http.impl.classic.ConnectExec.execute(ConnectExec.java:136)
    at [email protected]/org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
    at [email protected]/org.apache.hc.client5.http.impl.classic.ExecChainElement$1.proceed(ExecChainElement.java:57)
    at [email protected]/org.apache.hc.client5.http.impl.classic.ProtocolExec.execute(ProtocolExec.java:165)
    at [email protected]/org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
    at [email protected]/org.apache.hc.client5.http.impl.classic.ExecChainElement$1.proceed(ExecChainElement.java:57)
    at [email protected]/org.apache.hc.client5.http.impl.classic.HttpRequestRetryExec.execute(HttpRequestRetryExec.java:96)
( this goes on... )

服务器上似乎没有任何问题,它只是停止看到新连接(它是建立在 Undertow 之上的,如果相关的话)

有趣的是,如果我立即重新启动测试,那么它几乎会立即失败并出现此错误。 如果我等待几分钟,那么它会再次运行大约一分钟,并在崩溃之前管理接近 10k 个请求。 这让我觉得它耗尽了一些操作系统级别的资源,需要一段时间才能补充? 比如手柄什么的?

有人有什么想法吗?

编辑:值得一提的是,如果我终止服务器进程并快速重新启动它,那么我仍然会看到这个问题,这让我相信这不是服务器端代码中累积的某种错误。

java httpclient
1个回答
0
投票

您的测试并没有完全分离问题。

将不同的概念分成不同的测试。

  1. 测试客户端的请求生成。 例如。进行单元测试:给定特定的传感器输入(或场景中的任何输入),期望特定的 JSON。

  2. 按特定顺序测试服务器对选定的正确请求的响应。 (如果可能,对请求进行硬编码。)这纯粹是正确性测试,而不是负载测试。

  3. 测试服务器是否可以处理重负载而不会生成“连接被拒绝”。 您可以在此处发送任何请求正文(例如单个硬编码的有效或无效请求)。 您没有测试业务逻辑(服务器执行和返回的内容),只是测试网络和服务器基础设施。

您可以为此使用已建立的负载测试工具,例如 Apache JMeterk6

这将使您能够专注于特定的业务逻辑,而不是编写新的负载测试器。

再次,如果可能的话,对请求主体进行硬编码。

  1. 测试服务器在重负载下是否正确响应。 这是为了验证业务逻辑的正确性在负载下不会降低。
© www.soinside.com 2019 - 2024. All rights reserved.