web-dev-qa-db-ja.com

Apache HttpClient暫定エラー:NoHttpResponseException

XMLでPOSTメソッドを受け入れているWebサービスがあります。それが正常に動作している場合、ランダムに、メッセージ_The target server failed to respond_でIOExceptionをスローするサーバーと通信できません。後続の呼び出しは正常に機能します。

ほとんどの場合、いくつかの呼び出しを行った後、アプリケーションを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);をセットアップしようとしましたが、使用しませんでした。誰かが私に今できることを提案できますか?

[〜#〜] important [〜#〜]例外が発生する理由を理解することに加えて、私が抱える重要な懸念の1つは、retryhandlerがここで動作しない理由です。

62
Em Ae

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

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

96
ok2c

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

15
Jehy

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

この問題は、HttpClient 4.4.1で解決されました。 このJIRA および リリースノート を参照してください

6
Brian Agnew

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

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

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

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();
}
3

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

1
Amalgovinus

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

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

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

どうして?

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

OkHttpに切り替えたとき、NoHttpResponseExceptionの問題は永久になくなりました。

1
G. Demecki

Apache HTTPクライアント4.5.5でのデフォルトヘッダーの追加に関する同じ問題

接続:閉じる

問題を解決する

1