[英]Connection reset - Java (underlying socket status remain established) Azure VM
我正在調試連接重置的一個問題,需要一些幫助。
這是背景
使用Java版本8,Apache httpClient 4.5.2
我有一個以下程序,該程序可在Windows 10、7上成功運行,但最終在Azure Windows Server 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 Server 2016上,我可以看到netstat顯示套接字已建立狀態。 意味着連接已准備就緒,可以使用該連接,並且服務器已關閉該連接,因此導致連接重置錯誤。
我懷疑它是一個環境問題,即使服務器終止后,服務器2016仍保持套接字已建立,但在Windows 10上套接字狀態已更改為CLOSE_WAIT。
對此的幫助非常感謝
終於找到了根本原因,
Microsoft Azure的問題。 他們使用SNAT並在4分鍾的空閑時間后關閉出站TCP連接。 這浪費了我5天的時間去弄清楚。
表示如果您通過保持活動狀態與服務器連接,並希望您可以重用連接,直到服務器超時並發送FIN。 但是在此之前,如果閑置時間達到4分鍾,天藍色就會殺死它。 繁榮!!。 最糟糕的是,它甚至沒有通過RST通知服務器或客戶端,這意味着違反TCP並質疑其可靠性。
clientBuilder.setKeepAliveStrategy(new ConnectionKeepAliveStrategy() {
@Override
public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
// TODO Auto-generated method stub
return 3*60*1000;
}
});
使用上面的代碼,我設法在3分鍾到期時關閉連接,並在天藍色殺死它之前將其關閉。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.