3

クライアントがサーバーに対してRPCのような要求を行うクライアントサーバー階層アーキテクチャがあります。Tomcatを使用してサーブレットをホストし、ApacheHttpClientを使用してサーブレットにリクエストを送信しています。

私のコードは次のようになります。

    private static final HttpConnectionManager CONN_MGR = new MultiThreadedHttpConnectionManager();
    final GetMethod get = new GetMethod();
    final HttpClient httpClient = new HttpClient(CONN_MGR);
    get.getParams().setCookiePolicy(CookiePolicy.IGNORE_COOKIES);
    get.getParams().setParameter(HttpMethodParams.USER_AGENT, USER_AGENT);

    get.setQueryString(encodedParams);
    int responseCode;
    try {
        responseCode = httpClient.executeMethod(get);
    } catch (final IOException e) {
        ...
    }
    if (responseCode != 200)
        throw new Exception(...);

    String responseHTML;
    try {
        responseHTML = get.getResponseBodyAsString(100*1024*1024);
    } catch (final IOException e) {
        ...
    }
    return responseHTML;

負荷の軽い環境ではうまく機能しますが、1秒間に何百ものリクエストを行うと、これがわかり始めます-

Caused by: java.net.BindException: Address already in use
    at java.net.PlainSocketImpl.socketBind(Native Method)
    at java.net.AbstractPlainSocketImpl.bind(AbstractPlainSocketImpl.java:336)
    at java.net.Socket.bind(Socket.java:588)
    at java.net.Socket.<init>(Socket.java:387)
    at java.net.Socket.<init>(Socket.java:263)
    at org.apache.commons.httpclient.protocol.DefaultProtocolSocketFactory.createSocket(DefaultProtocolSocketFactory.java:80)
    at org.apache.commons.httpclient.protocol.DefaultProtocolSocketFactory.createSocket(DefaultProtocolSocketFactory.java:122)
    at org.apache.commons.httpclient.HttpConnection.open(HttpConnection.java:707)
    at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:387)
    at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171)
    at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397)
    at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323)

これを修正する方法について何か考えはありますか?クライアントがエフェメラルクライアントポートを再利用しようとしていることと関係があると思いますが、なぜこれが発生するのですか/どうすれば修正できますか?ありがとう!

4

5 に答える 5

2

あなたが直面している問題についての非常に良い議論はここで見つけることができます。Tomcat側では、デフォルトでSO_REUSEADDRオプションを使用します。これにより、サーバーはTIME_WAITにあるソケットを再利用できます。さらに、Apache httpクライアントはデフォルトでキープアライブを使用し、接続の再利用を試みます。

問題は、HttpClientでreleaseConnectionを呼び出さなかったことが原因のようです。これは、接続を再利用するために必要です。それ以外の場合、接続はガベージコレクターが来て閉じるか、サーバーがキープアライブを切断するまで開いたままになります。どちらの場合も、プールには戻されません。

于 2009-12-30T03:37:17.890 に答える
1

1秒間に数百の接続があり、接続がどれだけ長く開いて、実行して、閉じて、リサイクルされるかわからないので、これはあなたが抱える問題にすぎないと思います。実行できることの1つはBindException、tryブロックでをキャッチし、それを使用してバインドに失敗した場合に必要なことをすべて実行し、バインドがwhile成功したかどうかを示すフラグに依存するループで呼び出し全体をラップすることです。頭のてっぺんから:

boolean hasBound = false;
while (!hasBound) {
    try {
        hasBound = true;
        responseCode = httpClient.executeMethod(get);
    } catch (BindException e) {
        // do anything you want in the bound-unsuccessful case
    } catch (final IOException e) {
        ...
    }
}

質問で更新: 1つの興味深い質問:あなたが許可する接続の最大総数とホストあたりの数はいくつMultiThreadedHttpConnectionManagerですか?あなたのコードでは、それは次のようになります:

CONN_MGR.getParams().getDefaultMaxConnectionsPerHost();
CONN_MGR.getParams().getMaxTotalConnections();
于 2009-12-30T02:23:01.660 に答える
0

したがって、TCP/IPポートを開くことが許可されているよりも多くのリクエストを実行しました。私はHttpClientを使用していないため、これについて詳しく説明することはできませんが、理論的には、この特定の問題には3つの解決策があります。

  1. ハードウェアベース:別のNIC(ネットワークインターフェイスカード)を追加します。
  2. ソフトウェアベース:使用直後に接続を閉じるか、接続タイムアウトを増やします。
  3. プラットフォームベース:開くことができるTCP/IPポートの数を増やします。OS固有および/またはNICドライバー固有の場合があります。絶対最大値は65535であり、そのうちのいくつかはすでに予約/使用されている可能性があります(ポート80など)。
于 2009-12-30T02:24:08.193 に答える
0

したがって、問題は、他のHttpClientインスタンスの1つが、インスタンス化したMultiThreadedHttpConnectionManagerを誤って使用していなかったことでした。そのため、実質的にレート制限はまったくありませんでした。この問題を修正すると、スローされる例外が修正されました。

しかし、すべての提案に感謝します!

于 2009-12-31T01:29:31.790 に答える
0

HttpClientUtils.closeQuietly(client);を呼び出しても ただし、InputStream contentStream = HttpResponse.getEntity()。getContent()のようなHttpResponseエンティティからコンテンツを読み取ろうとする場合のコードでは、inputstreamも閉じる必要があります。そうすると、HttpClient接続のみが適切に閉じられます。

于 2017-06-29T16:24:38.493 に答える