2

我在私有子网中有一个带有 Spring Boot 应用程序的 Amazon Linux 2 应用程序服务器。在公共子网中,该应用程序服务器前面有一个 Nat 网关。应用程序向远程主机发送带有Connection: keep-alive标头的请求,远程主机使用相同的标头发送回响应。所以我可以通过 netstat 看到已建立的连接。

netstat -t | grep <remote server ip>
tcp6       0      0 ip-172-30-4-31.eu:57324 <remote server ip>:http       ESTABLISHED

由于 350 秒内没有流量,Nat 网关根据本文档关闭连接:https ://docs.aws.amazon.com/vpc/latest/userguide/nat-gateway-troubleshooting.html#nat-gateway-troubleshooting-timeout 但是应用程序服务器上的连接仍处于已建立状态,因此对远程服务器的下一个请求给了我:

java.net.SocketException: Connection reset

我试图在sysctl.conf中的应用程序服务器上进行更改,以几乎与 Nat 网关同时关闭连接:

net.ipv4.tcp_keepalive_time=351
net.ipv4.tcp_keepalive_intvl=30
net.ipv4.tcp_keepalive_probes=2

但是什么都没有发生,并且通过 tcpdump 将流量从应用程序服务器转储到远程服务器不会给我任何保持活动的数据包。那么除了删除应用程序中的 Connection 标头之外,我能做些什么来避免这个问题呢?

4

1 回答 1

3

问题在于用于打开套接字的方法。我使用了 Apache Fluent API:

Request.Post(mainProperties.getPartnerURL())
                .addHeader("Signature", SecurityHelper.getSignature(requestBody.getBytes("UTF-8"),
                        mainProperties.getPartnerKey()))
                .addHeader("Content-Type", "application/x-www-form-urlencoded")
                .connectTimeout(mainProperties.getRequestTimeoutMillis())
                .bodyByteArray(requestBody.getBytes(UTF_8))
                .execute().returnContent().asString();

但我已将 so_keepalive 参数设置为套接字。可以使用 HttpClient 来完成:

    SocketConfig socketConfig = SocketConfig.custom()
            .setSoKeepAlive(true)
            .build();

    RequestConfig requestConfig = RequestConfig.custom()
            .setConnectTimeout(mainProperties.getRequestTimeoutMillis())
            .build();

    CloseableHttpClient httpClient = HttpClientBuilder.create()
            .setDefaultSocketConfig(socketConfig)
            .setDefaultRequestConfig(requestConfig)
            .build();
            
    HttpPost post = new HttpPost(mainProperties.getPartnerURL());

    post.addHeader("Signature", SecurityHelper.getSignature(requestBody.getBytes("UTF-8"),
                mainProperties.getPartnerKey()));
    post.addHeader("Content-Type", "text/xml");
    post.setEntity(new StringEntity(requestBody, UTF_8));

    CloseableHttpResponse response = httpClient.execute(post);
    return EntityUtils.toString(response.getEntity(), UTF_8);

然后将我的sysctl.conf中设置的net.ipv4.tcp_keepalive_time=350(应用更改需要 sysctl -p)应用到新连接,可以这样检查:

netstat -o | grep <remote-host>
tcp6       0      0 ip-172-30-4-233.e:50414 <remote-host>:http ESTABLISHED **keepalive (152.12/0/0)**

因此,在最后一个数据包 350 秒后发送的 TCP-Keep-Alive 数据包没有响应会关闭 ESTABLISHED 连接。通过 tcp dump 可以看到所有 TCP-Keep-Alive 数据包:

在此处输入图像描述

于 2020-09-02T16:05:05.623 回答