【发布时间】:2021-05-15 21:18:32
【问题描述】:
问题陈述
上下文
- 我是一名软件工程师,正在测试运行餐厅菜单项的订单排列,以确认它们通过 POS 成功下单
- 简而言之,这会将 JSON 有效负载 POST 到端点,然后使用 POS 验证订单以定义成功/失败/其他
- POS 以及每秒事务数 (TPS) 可能会有所不同,但每个后端使用相同的核心处理
- 这可能高达每项约 22,000 个排列,JSON 大小易于管理,需要尽快处理
- 网络可能因餐厅和/或地区而异,其中一个正在测试中
- 例如其中一些的延迟比其他的高得多
- 因此,不管如何,HTTPClient 都应该能够智能地协商相同的内容和端点
直接问题
- 我正在使用带有 PoolingAsyncClientConnectionManager 的 Apache 的 HTTP 客户端 5 来执行菜单内容的 GET 和 POST 以检查订单是否成功
- 开箱即用,但有时会丢失与
Stream Refused的连接,具体而言:org.apache.hc.core5.http2.H2StreamResetException: Stream refused
- 我发现,似乎没有任何单独的调整适用于所有具有可变延迟的网络环境
- 跟踪堆栈跟踪似乎表明流已经关闭,因此需要一种方法来保持它打开或不执行已经关闭的连接
if (connState == ConnectionHandshake.GRACEFUL_SHUTDOWN) {
throw new H2StreamResetException(H2Error.PROTOCOL_ERROR, "Stream refused");
}
解决问题的一些尝试
- 尝试使用搜索引擎寻找答案,但 HTTPClient5 的点击率很少
- 尝试使用official documentation,但这很稀疏
- 将每条路由的最大连接数更改为减少的数量,将不活动验证或连接时间更改为有效
- 不活动检查可能会修复 POST,但会停止某些事务的 GET
- 对于一个区域/餐厅的调整可能适用于 1,然后中断另一个,只有网络作为变量
PoolingAsyncClientConnectionManagerBuilder builder = PoolingAsyncClientConnectionManagerBuilder
.create()
.setTlsStrategy(getTlsStrategy())
.setMaxConnPerRoute(12)
.setMaxConnTotal(12)
.setValidateAfterInactivity(TimeValue.ofMilliseconds(1000))
.setConnectionTimeToLive(TimeValue.ofMinutes(2))
.build();
- 切换到具有不同超时的自定义 RequestConfig
private HttpClientContext getHttpClientContext() {
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(Timeout.of(10, TimeUnit.SECONDS))
.setResponseTimeout(Timeout.of(10, TimeUnit.SECONDS))
.build();
HttpClientContext httpContext = HttpClientContext.create();
httpContext.setRequestConfig(requestConfig);
return httpContext;
}
用于分析的初始代码段
(除了上述带有更改尝试的片段)
- 包装器处理初始化并获得响应
public SimpleHttpResponse getFullResponse(String url, PoolingAsyncClientConnectionManager manager, SimpleHttpRequest req) {
try (CloseableHttpAsyncClient httpclient = getHTTPClientInstance(manager)) {
httpclient.start();
CountDownLatch latch = new CountDownLatch(1);
long startTime = System.currentTimeMillis();
Future<SimpleHttpResponse> future = getHTTPResponse(url, httpclient, latch, startTime, req);
latch.await();
return future.get();
} catch (IOException | InterruptedException | ExecutionException e) {
e.printStackTrace();
return new SimpleHttpResponse(999, CommonUtils.getExceptionAsMap(e).toString());
}
}
- 使用实际的处理程序和探测代码
private Future<SimpleHttpResponse> getHTTPResponse(String url, CloseableHttpAsyncClient httpclient, CountDownLatch latch, long startTime, SimpleHttpRequest req) {
return httpclient.execute(req, getHttpContext(), new FutureCallback<SimpleHttpResponse>() {
@Override
public void completed(SimpleHttpResponse response) {
latch.countDown();
logger.info("[{}][{}ms] - {}", response.getCode(), getTotalTime(startTime), url);
}
@Override
public void failed(Exception e) {
latch.countDown();
logger.error("[{}ms] - {} - {}", getTotalTime(startTime), url, e);
}
@Override
public void cancelled() {
latch.countDown();
logger.error("[{}ms] - request cancelled for {}", getTotalTime(startTime), url);
}
});
}
直接问题
- 有没有办法配置客户端,使其可以自行处理这些差异,而无需显式修改每个端点上下文的配置?
【问题讨论】: