91

XMLでPOSTメソッドを受け入れるWebサービスがあります。正常に動作している場合、ランダムな機会にサーバーとの通信に失敗し、メッセージ付きのIOExceptionがスローされますThe target server failed to respond。以降の呼び出しは正常に機能します。

ほとんどの場合、電話をかけてからアプリケーションを10〜15分間アイドル状態にしておくと発生します。その後に最初に呼び出すと、このエラーが返されます。

私はいくつかのことを試しました...

私は次のような再試行ハンドラーを設定します

HttpRequestRetryHandler retryHandler = new HttpRequestRetryHandler() {

            public boolean retryRequest(IOException e, int retryCount, HttpContext httpCtx) {
                if (retryCount >= 3){
                    Logger.warn(CALLER, "Maximum tries reached, exception would be thrown to outer block");
                    return false;
                }
                if (e instanceof org.apache.http.NoHttpResponseException){
                    Logger.warn(CALLER, "No response from server on "+retryCount+" call");
                    return true;
                }
                return false;
            }
        };

        httpPost.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, retryHandler);

しかし、この再試行は呼び出されませんでした。(はい、私は正しいinstanceof句を使用しています)。デバッグ中、このクラスは呼び出されません。

セットアップもしてみましたHttpProtocolParams.setUseExpectContinue(httpClient.getParams(), false);が、ダメ。誰かが私が今できることを提案できますか?

重要 なぜ例外が発生するのかを理解することに加えて、私が抱えている重要な懸念の1つは、なぜリトライハンドラーがここで機能しないのかということです。

4

10 に答える 10

133

ほとんどの場合、接続マネージャーによって存続している永続的な接続は古くなります。つまり、ターゲットサーバーは、接続がアイドル状態のときにHttpClientがそのイベントに応答できずに接続をシャットダウンするため、接続が半分閉じられるか、「失効」します。通常、これは問題ではありません。HttpClientは、プールからのリース時に接続の有効性を検証するためにいくつかの手法を採用しています。失効した接続チェックが無効になっていて、失効した接続を使用して要求メッセージを送信した場合でも、要求の実行は通常、SocketExceptionを使用した書き込み操作で失敗し、自動的に再試行されます。ただし、状況によっては、書き込み操作が例外なく終了し、後続の読み取り操作が-1(ストリームの終わり)を返す場合があります。

状況を改善する最も簡単な方法は、期限切れの接続と、たとえば、非アクティブな期間が経過した後、プールから1分より長くアイドル状態になっている接続を削除することです。詳細については、HttpClientチュートリアルのこのセクションを参照してください。

于 2012-05-15T12:34:35.973 に答える
22

受け入れられた答えは正しいですが、解決策がありません。このエラーを回避するには、この回答のように、HTTPクライアントにsetHttpRequestRetryHandler(またはapacheコンポーネント4.4の場合はsetRetryHandler)を追加できます。

于 2016-06-07T12:55:30.587 に答える
7

HttpClient 4.4は、リクエスターに戻る前に、古くなっている可能性のある接続の検証に関連するこの領域のバグに悩まされていました。接続が古くなっているかどうかは検証されなかったため、すぐにになりNoHttpResponseExceptionます。

この問題は、HttpClient4.4.1で解決されました。このJIRAリリースノートを参照してください

于 2017-12-12T09:48:17.977 に答える
5

受け入れられた答えは正しいですが、IMHOは単なる回避策です。

明確にするために:持続的接続が古くなる可能性があるのは完全に正常な状況です。しかし、残念ながら、HTTPクライアントライブラリがそれを適切に処理できない場合、それは非常に悪いことです。

Apache HttpClientでのこの誤った動作は長年修正されていなかったため、古い接続の問題から簡単に回復できるライブラリ(OkHttpなど)に切り替えることを強くお勧めします。

なんで?

  1. OkHttpはデフォルトでhttp接続をプールします。
  2. http接続が古くなり、べき等ではないためにリクエストを再試行できない状況(POSTなど)から正常に回復します。Apache HttpClient(前述)については言えませんNoHttpResponseException
  3. 初期ドラフトおよびベータバージョンからのHTTP/2.0をサポートします。

OkHttpに切り替えたとき、私の問題はNoHttpResponseException永遠に消えました。

于 2019-06-03T10:05:04.643 に答える
5

解決策:ReuseStrategyを決して変更しない

この問題は非常に複雑で、失敗する可能性のあるさまざまな要因が非常に多いため、別の投稿でこの解決策を見つけてうれしく思いました:org.apache.http.NoHttpResponseExceptionを解決する方法

接続を再利用しないでください:org.apache.http.impl.client.AbstractHttpClientで設定してください:

httpClient.setReuseStrategy(new NoConnectionReuseStrategy());

同じことがorg.apache.http.impl.client.HttpClientBuilderビルダーで構成できます。

builder.setConnectionReuseStrategy(new NoConnectionReuseStrategy());
于 2019-11-07T09:11:55.287 に答える
4

現在、ほとんどのHTTP接続は、特に宣言されていない限り、永続的であると見なされます。ただし、サーバーリソースを保存するために、接続が永久に開いたままになることはめったにありません。多くのサーバーのデフォルトの接続タイムアウトはかなり短く、たとえばApachehttpd2.2以降では5秒です。

エラーは、org.apache.http.NoHttpResponseExceptionサーバーによって閉じられた1つの永続的な接続が原因である可能性があります。

ApacheHttpクライアントプールで未使用の接続を開いたままにする最大時間をミリ秒単位で設定できます。

Spring Bootを使用して、これを実現する1つの方法:

public class RestTemplateCustomizers {
    static public class MaxConnectionTimeCustomizer implements RestTemplateCustomizer {

        @Override
        public void customize(RestTemplate restTemplate) {
            HttpClient httpClient = HttpClientBuilder
                .create()
                .setConnectionTimeToLive(1000, TimeUnit.MILLISECONDS)
                .build();

            restTemplate.setRequestFactory(
                new HttpComponentsClientHttpRequestFactory(httpClient));
        }
    }
}

// In your service that uses a RestTemplate
public MyRestService(RestTemplateBuilder builder ) {
    restTemplate = builder
         .customizers(new RestTemplateCustomizers.MaxConnectionTimeCustomizer())
         .build();
}
于 2017-12-16T16:48:43.263 に答える
2

これはdisableContentCompression()、HttpClientに割り当てられたプーリングマネージャーにが設定されていて、ターゲットサーバーがgzip圧縮を使用しようとしている場合に発生する可能性があります。

于 2019-04-12T19:24:09.550 に答える
1

apachehttpクライアント4.5.5でデフォルトヘッダーを追加する場合の同じ問題

接続:閉じる

問題を解決する

于 2018-04-13T16:07:37.213 に答える
0

PoolingHttpClientConnectionManagerの代わりに使用BasicHttpClientConnectionManager

BasicHttpClientConnectionManager同じルートで後続のリクエストに接続を再利用するように努めます。ただし、既存の接続を閉じて、指定されたルートに対して再度開きます。

于 2022-02-16T08:48:10.217 に答える
-2

同じ問題に直面しました。拡張機能として「connection:close」を追加することで解決しました。

ステップ1:新しいクラスConnectionCloseExtensionを作成します

import com.github.tomakehurst.wiremock.common.FileSource;
import com.github.tomakehurst.wiremock.extension.Parameters;
import com.github.tomakehurst.wiremock.extension.ResponseTransformer;
import com.github.tomakehurst.wiremock.http.HttpHeader;
import com.github.tomakehurst.wiremock.http.HttpHeaders;
import com.github.tomakehurst.wiremock.http.Request;
import com.github.tomakehurst.wiremock.http.Response;

public class ConnectionCloseExtension extends ResponseTransformer {
  @Override
  public Response transform(Request request, Response response, FileSource files, Parameters parameters) {
    return Response.Builder
        .like(response)
        .headers(HttpHeaders.copyOf(response.getHeaders())
            .plus(new HttpHeader("Connection", "Close")))
        .build();
  }

  @Override
  public String getName() {
    return "ConnectionCloseExtension";
  }
}

ステップ2:以下のようにwireMockServerに拡張クラスを設定します。

final WireMockServer wireMockServer = new WireMockServer(options()
                .extensions(ConnectionCloseExtension.class)
                .port(httpPort));
于 2019-10-01T10:33:54.030 に答える