连接重置 - Java(基础套接字状态保持已建立)Azure VM
Connection reset - Java (underlying socket status remain established) Azure VM
我正在调试一个连接重置问题,需要一些帮助。
这是背景
使用 java 版本 8,apache httpClient 4.5.2
我有以下程序,它在 windows 10、7 上成功运行,但最终在 Azure windows 服务器 2016 VM 上重置连接。
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
public class TestConnectionReset
{
static PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
static {
connManager.setMaxTotal(10);
connManager.setDefaultMaxPerRoute(2);
}
public static void main(String[] args) throws ClientProtocolException, IOException, InterruptedException {
while (true) {
HttpClientBuilder clientBuilder = HttpClientBuilder.create();
RequestConfig config = RequestConfig.custom().setConnectTimeout(1800000).setConnectionRequestTimeout(1800000)
.setSocketTimeout(1800000).build();
clientBuilder.setDefaultRequestConfig(config);
clientBuilder.setConnectionManager(connManager);
String userName = "xxxxx";
String password = "xxxxx";
String userNamePasswordPair = String.valueOf(userName) + ":" + password;
String authenticationData = "Basic " + new String((new Base64()).encode(userNamePasswordPair.getBytes()));
HttpPost post = new HttpPost("https://url/rest/oauth/token");
Map<String, String> requestBodyMap = new HashMap<String, String>();
requestBodyMap.put("grant_type", "client_credentials");
String req = getFormUrlEncodedBodyFromMap(requestBodyMap);
StringEntity stringEntity = new StringEntity(req);
post.setEntity(stringEntity);
post.setHeader("Authorization", authenticationData);
post.setHeader("Content-Type", "application/x-www-form-urlencoded");
CloseableHttpClient closeableHttpClient = clientBuilder.build();
HttpResponse response = closeableHttpClient.execute(post);
Header[] hs = response.getAllHeaders();
for (Header header : hs) {
System.out.println(header.toString());
}
System.out.println(EntityUtils.toString(response.getEntity()));
Thread.sleep(10*60*1000L);
}
}
public static String getFormUrlEncodedBodyFromMap(Map<String, String> formData) {
StringBuilder requestBody = new StringBuilder();
Iterator<Map.Entry<String, String>> itrFormData = formData.entrySet().iterator();
while (itrFormData.hasNext()) {
Map.Entry<?, ?> entry = (Map.Entry)itrFormData.next();
requestBody.append(entry.getKey()).append("=").append(entry.getValue());
if (itrFormData.hasNext()) {
requestBody.append("&");
}
}
return requestBody.toString();
}
}
我正在使用池化 httpclient 连接管理器。第一次循环执行中的第一个请求成功,但下一个请求的 for 循环的后续迭代失败。
我的发现
如果我们在 windows 10 上看到底层套接字连接,则在第一个请求套接字进入 CLOSE_WAIT 状态后执行下一个请求,关闭现有连接并创建新连接。
实际上服务器在 5 分钟内关闭了连接。但是 windows 10 能够检测到它并在触发下一个请求时重新发起连接。
现在,在 windows 服务器 2016 上,我可以看到 netstat 显示套接字已建立状态。意味着连接已准备好使用,并且它会选择相同的连接,最后服务器已经关闭它,因此导致连接重置错误。
我怀疑这是一个环境问题,服务器 2016 即使在服务器终止后仍保持套接字已建立,但在 windows 10 套接字状态更改为 CLOSE_WAIT。
非常感谢对此的帮助
终于找到了根本原因,
微软 Azure 的问题。他们正在使用 SNAT 并在 4 分钟空闲时间后关闭出站 TCP 连接。这浪费了我 5 天的时间来弄清楚。
表示如果您使用keep-alive连接到服务器,并希望您可以重用连接直到服务器超时并发送FIN。但在此之前,如果空闲时间达到 4 分钟,azure 会杀死它。繁荣!!。最糟糕的是,它甚至没有用 RST 通知服务器或客户端,这意味着违反 TCP 并质疑其可靠性。
clientBuilder.setKeepAliveStrategy(new ConnectionKeepAliveStrategy() {
@Override
public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
// TODO Auto-generated method stub
return 3*60*1000;
}
});
使用上面的代码,我设法在 3 分钟到期时关闭连接,并在 azure 杀死它之前关闭它。
我正在调试一个连接重置问题,需要一些帮助。
这是背景
使用 java 版本 8,apache httpClient 4.5.2
我有以下程序,它在 windows 10、7 上成功运行,但最终在 Azure windows 服务器 2016 VM 上重置连接。
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
public class TestConnectionReset
{
static PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
static {
connManager.setMaxTotal(10);
connManager.setDefaultMaxPerRoute(2);
}
public static void main(String[] args) throws ClientProtocolException, IOException, InterruptedException {
while (true) {
HttpClientBuilder clientBuilder = HttpClientBuilder.create();
RequestConfig config = RequestConfig.custom().setConnectTimeout(1800000).setConnectionRequestTimeout(1800000)
.setSocketTimeout(1800000).build();
clientBuilder.setDefaultRequestConfig(config);
clientBuilder.setConnectionManager(connManager);
String userName = "xxxxx";
String password = "xxxxx";
String userNamePasswordPair = String.valueOf(userName) + ":" + password;
String authenticationData = "Basic " + new String((new Base64()).encode(userNamePasswordPair.getBytes()));
HttpPost post = new HttpPost("https://url/rest/oauth/token");
Map<String, String> requestBodyMap = new HashMap<String, String>();
requestBodyMap.put("grant_type", "client_credentials");
String req = getFormUrlEncodedBodyFromMap(requestBodyMap);
StringEntity stringEntity = new StringEntity(req);
post.setEntity(stringEntity);
post.setHeader("Authorization", authenticationData);
post.setHeader("Content-Type", "application/x-www-form-urlencoded");
CloseableHttpClient closeableHttpClient = clientBuilder.build();
HttpResponse response = closeableHttpClient.execute(post);
Header[] hs = response.getAllHeaders();
for (Header header : hs) {
System.out.println(header.toString());
}
System.out.println(EntityUtils.toString(response.getEntity()));
Thread.sleep(10*60*1000L);
}
}
public static String getFormUrlEncodedBodyFromMap(Map<String, String> formData) {
StringBuilder requestBody = new StringBuilder();
Iterator<Map.Entry<String, String>> itrFormData = formData.entrySet().iterator();
while (itrFormData.hasNext()) {
Map.Entry<?, ?> entry = (Map.Entry)itrFormData.next();
requestBody.append(entry.getKey()).append("=").append(entry.getValue());
if (itrFormData.hasNext()) {
requestBody.append("&");
}
}
return requestBody.toString();
}
}
我正在使用池化 httpclient 连接管理器。第一次循环执行中的第一个请求成功,但下一个请求的 for 循环的后续迭代失败。
我的发现
如果我们在 windows 10 上看到底层套接字连接,则在第一个请求套接字进入 CLOSE_WAIT 状态后执行下一个请求,关闭现有连接并创建新连接。
实际上服务器在 5 分钟内关闭了连接。但是 windows 10 能够检测到它并在触发下一个请求时重新发起连接。
现在,在 windows 服务器 2016 上,我可以看到 netstat 显示套接字已建立状态。意味着连接已准备好使用,并且它会选择相同的连接,最后服务器已经关闭它,因此导致连接重置错误。
我怀疑这是一个环境问题,服务器 2016 即使在服务器终止后仍保持套接字已建立,但在 windows 10 套接字状态更改为 CLOSE_WAIT。
非常感谢对此的帮助
终于找到了根本原因,
微软 Azure 的问题。他们正在使用 SNAT 并在 4 分钟空闲时间后关闭出站 TCP 连接。这浪费了我 5 天的时间来弄清楚。
表示如果您使用keep-alive连接到服务器,并希望您可以重用连接直到服务器超时并发送FIN。但在此之前,如果空闲时间达到 4 分钟,azure 会杀死它。繁荣!!。最糟糕的是,它甚至没有用 RST 通知服务器或客户端,这意味着违反 TCP 并质疑其可靠性。
clientBuilder.setKeepAliveStrategy(new ConnectionKeepAliveStrategy() {
@Override
public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
// TODO Auto-generated method stub
return 3*60*1000;
}
});
使用上面的代码,我设法在 3 分钟到期时关闭连接,并在 azure 杀死它之前关闭它。