微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Apache HTTPClient5 - 如何防止连接/流被拒绝 强制 HTTP 1为 POST 设置有效的标题按类型设置不活动检查

如何解决Apache HTTPClient5 - 如何防止连接/流被拒绝 强制 HTTP 1为 POST 设置有效的标题按类型设置不活动检查

问题说明

上下文

  • 我是一名软件工程师,负责测试运行餐厅菜单项的订单排列,以确认它们通过 POS 成功下单
    • 简而言之,这会将 JSON 有效负载发布到端点,然后端点验证带有 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();
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] - {} - {}",url,e);
                }

                @Override
                public void cancelled() {
                    latch.countDown();
                    logger.error("[{}ms] - request cancelled for {}",url);
                }

            });
        }

直接提问

  • 有没有一种方法可以配置客户端,使其可以自行处理这些差异,而无需显式修改每个端点上下文的配置?

解决方法

通过以下组合进行修复以确保连接实时/就绪

(或者至少是稳定的)

强制 HTTP 1

HttpAsyncClients.custom()
    .setConnectionManager(manager)
    .setRetryStrategy(getRetryStrategy())
    .setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_1)
    .setConnectionManagerShared(true);

为 POST 设置有效的标题

  • 特别是关闭标题
    • req.setHeader("Connection","close,TE");
    • 注意:不活动检查有帮助,但有时仍然会被拒绝

按类型设置不活动检查

  • 设置 POST 以在不活动后立即验证
    • 注意:两者都使用 1000 会导致某些系统的掉率很高
PoolingAsyncClientConnectionManagerBuilder
    .create()
    .setValidateAfterInactivity(TimeValue.ofMilliseconds(0))
  • 设置 GET 以在 1 秒后验证
PoolingAsyncClientConnectionManagerBuilder
    .create()
    .setValidateAfterInactivity(TimeValue.ofMilliseconds(1000))

给定错误上下文

  • 在stacktrace中追踪连接问题到AbstractH2StreamMultiplexer
  • 将 ConnectionHandshake.GRACEFUL_SHUTDOWN 显示为触发流拒绝
 if (connState == ConnectionHandshake.GRACEFUL_SHUTDOWN) {
    throw new H2StreamResetException(H2Error.PROTOCOL_ERROR,"Stream refused");
}
  • 对应于
connState = streamMap.isEmpty() ? ConnectionHandshake.SHUTDOWN : ConnectionHandshake.GRACEFUL_SHUTDOWN;

推理

  • 如果我理解正确:
    • 连接被无意/有意关闭
      • 然而,在再次执行之前,它们并没有被确认准备好
      • 这导致它失败,因为流不可行
    • 因此修复有效,因为(似乎)
      • Given Forcing HTTP1 允许管理单个上下文
        • HttpVersionPolicy NEGOTIATE/FORCE_HTTP_2 在整个区域/菜单范围内出现更大或同等失败的情况
      • 并确保所有连接在使用前均有效
      • 并且 POST 由于关闭标头而始终关闭,这对 HTTP2 不可用
      • 因此
        • 以合理的周期检查 GET 的有效性
        • POST 每次都检查,由于是强行关闭,执行前重新获取
        • 没有任何意外关闭的余地
          • 否则可能会错误地切换到 HTTP2

将接受这个直到出现更好的答案,因为这是稳定但次优的。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?