我正在尝试为 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 个请求。 这让我觉得它耗尽了一些操作系统级别的资源,需要一段时间才能补充? 比如手柄什么的?
有人有什么想法吗?
编辑:值得一提的是,如果我终止服务器进程并快速重新启动它,那么我仍然会看到这个问题,这让我相信这不是服务器端代码中累积的某种错误。
您的测试并没有完全分离问题。
将不同的概念分成不同的测试。
测试客户端的请求生成。 例如。进行单元测试:给定特定的传感器输入(或场景中的任何输入),期望特定的 JSON。
按特定顺序测试服务器对选定的正确请求的响应。 (如果可能,对请求进行硬编码。)这纯粹是正确性测试,而不是负载测试。
测试服务器是否可以处理重负载而不会生成“连接被拒绝”。 您可以在此处发送任何请求正文(例如单个硬编码的有效或无效请求)。 您没有测试业务逻辑(服务器执行和返回的内容),只是测试网络和服务器基础设施。
您可以为此使用已建立的负载测试工具,例如 Apache JMeter 或 k6。
这将使您能够专注于特定的业务逻辑,而不是编写新的负载测试器。
再次,如果可能的话,对请求主体进行硬编码。