Сброс соединения - Java (статус основного сокета остается установленным) Azure VM - PullRequest
0 голосов
/ 31 мая 2019

Я отлаживаю одну проблему сброса соединения и мне нужна помощь.

Вот фон

Используя java версии 8, apache httpClient 4.5.2

У меня есть следующая программа, которая успешно работает на Windows 10, 7, но в конечном итоге происходит сброс соединения на виртуальной машине Azure Windows Server 2016.

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, после того, как 1-й сокет запроса переходит в состояние CLOSE_WAIT, и следующий запрос выполняется с закрытием существующего соединения и созданием новогоподключение.

Фактически сервер закрывает соединение продолжительностью 5 минут.Но Windows 10 может обнаружить его и повторно инициировать соединение при следующем запросе.

Теперь на Windows Server 2016 я вижу, что netstat показывает состояние сокета ESTABLISHED.Означает, что соединение готово к использованию, и оно подключается к тому же соединению, и, наконец, сервер уже закрыло его, что приводит к ошибке сброса соединения.

Я подозреваю, что это экологическая проблема, когда сервер 2016 сохраняет сокет ESTABLISHED даже после того, как сервер завершил его, но в Windows 10 состояние сокета изменилось на CLOSE_WAIT.

Помощь по этому вопросу очень ценится

1 Ответ

1 голос
/ 06 июня 2019

Наконец-то получили основную причину,

Проблема с Microsoft Azure.Они используют SNAT и закрывают исходящие TCP-соединения после 4-минутного простоя.Это потратило впустую мои 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 минут и закрыть его до того, как Azure убьет его.

...