如何通过httpclient在Java中设置TCP Keep Alive

时间:2018-11-19 13:00:21

标签: java apache-httpclient-4.x tcp-keepalive

我位于AWS私有子网中的Java应用程序通过AWS Nat网关连接到http服务器。我正在通过httpclient调用POST请求到HTTP服务器。该请求将花费10多分钟才能完成。我已将套接字超时和连接超时配置为1小时,因为这是后台任务。但是中间的AWS NAT网关将在300秒[5分钟]后发送回RST数据包,并导致连接被重置,因此我无法增加NAT网关超时。所以我需要从我的应用程序端处理问题。

我的策略是使用tcp保持活动时间,它将每240秒发送一次数据包以保持连接活动。我已经配置了 如下

CloseableHttpClient httpClient = HttpClients.createDefault()
HttpParams params = httpClient.getParams();
HttpConnectionParams.setConnectionTimeout(params, 3600000); //connection Timeout
HttpConnectionParams.setSoTimeout(params, 3600000); // Socket Time out
HttpConnectionParams.setSoKeepalive(params, true); //Enable Socket level keep alive time

,然后通过execute方法

调用发布请求
HttpPost post = new HttpPost("http://url");
HttpResponse response = httpClient.execute(post);

由于我使用的是Linux系统,因此我已为服务器配置了以下Sysctl值

sysctl -w net.ipv4.tcp_keepalive_time=240 
sysctl -w net.ipv4.tcp_keepalive_intvl=240
sysctl -w net.ipv4.tcp_keepalive_probes=10

但是在执行程序时,未启用保持活动状态,并且连接失败。

我已经使用netstat -o选项进行了检查,如下所示,保持活动状态已关闭

tcp        0      0 192.168.1.141:43770     public_ip:80          ESTABLISHED 18134/java           off (0.00/0/0)

有什么方法可以使用httpclient设置TCP从Java代码保持活动。我也可以看到不推荐使用HttpConnectionParams。但是我找不到任何可以设置保持活动状态的新类

谢谢

3 个答案:

答案 0 :(得分:1)

我找到了解决该问题的方法。奇怪的情况是,我无法使用httpclient中的某些构建器类来传递套接字保持活动状态。我在问题中指定的一种方法是使用HttpConnectionParams,如下所示,但这不起作用,并且现在不推荐使用此类。

HttpParams params = httpClient.getParams();
HttpConnectionParams.setSoKeepalive(params, true);

因此,在检查apache http文档时,我可以看到现在连接参数已通过RequestConfig类传递给httpclient。此类的构建器提供了用于设置connection_time_out和socket_time_out的解决方案。但是检查此代码,我看不到启用SocketKeepAlive的选项。因此,唯一的解决方案是使用SocketBuilder类直接创建一个Socket,并将其传递给HttpClientBuilder。

以下是工作代码

SocketConfig socketConfig = SocketConfig.custom().setSoKeepAlive(true).setSoTimeout(3600000).build(); //We need to set socket keep alive
        RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(3600000).build();
        CloseableHttpClient httpClient = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).
                                           setDefaultSocketConfig(socketConfig).build();
HttpPost post = new HttpPost(url.toString());
HttpResponse response = httpClient.execute(post);

在执行上述操作时,我可以根据我在Linux内核中设置的sysctl值,在套接字中正确设置了保持活动状态

tcp        0      0 localip:48314     public_ip:443     ESTABLISHED 14863/java          keepalive (234.11/0/0)

如果有人有更好的解决方案可以从Requestconfig类或任何其他高级构建器类启用Socket Keep alive,我可以提出建议。

答案 1 :(得分:0)

使HTTP连接保持打开状态但长时间不活动是一个不好的设计选择。 HTTP是一种请求-响应协议,意味着请求和响应都很快。

保持连接打开将保留资源。从服务器(以及网络防火墙和路由器)的角度来看,打开连接并开始请求(在您的情况下为POST)但长时间不发送任何字节的客户端与将永远不要发送任何数据,因为它是有缺陷的或恶意的(造成DOS攻击)。服务器(和网络硬件)得出的正确结论是,正确的做法是关闭连接并回收用于其的资源,这是正确的。您正试图与出于正确原因而发生的正确行为作斗争。即使您设法解决TCP关闭问题,您也会发现其他问题,例如HTTP服务器超时和数据库超时。

您应该重新考虑两个组件之间的通信设计。也就是说,这看起来像XY问题。您可能会考虑

  • 让客户端等待,直到客户端完整上传后才能在开始POST之前执行
  • 将上传内容拆分为更小,更频繁的上传内容。
  • 使用HTTP以外的协议。

答案 2 :(得分:0)

以上使用Socket的方法在AWS Network Load Balancer超时以下重置tcp_keepalive_intvl值时效果很好。同时使用两者,重置允许Java小时+连接的NLB tcp空闲超时。