同じ宛先へのオープン複数のJava HttpConnectionsを維持する方法
-
20-09-2019 - |
質問
私たちは、多くの場合、同じプロバイダ(集約ユースケースの一種)にREST APIを呼び出すためのHttpURLConnection APIを使用しています。我々は、プロバイダのホスト(常に同じIP)に常に開い5つの接続のプールを維持したい。
適切な解決策とは何ですか?ここでは試したものです。
System.setProperty("http.maxConnections", 5); // set globally only once
...
// everytime we need a connection, we use the following
HttpURLConnection conn = (HttpURLConnection) (new URL(url)).openConnection();
conn.setRequestMethod("GET");
conn.setDoInput(true);
conn.setDoOutput(false);
conn.setUseCaches(true);
...
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
...
BufferedReaderのは、これ以上のバイトを返すまで、は、この時点では、入力ストリームを読み込みます。我々は、プロバイダへの根本的な接続を再利用したい場合は、私たちは、その点の後に何をしますか?私たちは、入力ストリームが完全に読まれている場合、接続は、プールに戻って追加されたという印象の下にあった。
これは、数週間のためにこのように作業していて、今日では、この例外を生成する動作を停止されています:java.net.SocketException: Too many open files
私たちは(lsof
を実行することで)このようなCLOSE_WAIT状態で多数のソケットを見つけました:
java 1814 root 97u IPv6 844702 TCP colinux:58517->123.123.254.205:www (CLOSE_WAIT)
のいずれかconn.getInputStream()。近い()またはconn.disconnect()が完全に接続を閉じ、プールから削除していないのでしょうか?
解決
ここのから:
現在の実装では、応答本体をバッファリングしません。どのアプリケーションが応答の本体を読み終わるか、(近い呼び出す)再利用するために、その接続のためには、応答本体の残りの部分を放棄しなければならないことを意味します。全体のレスポンスボディが使用できない場合、接続が再利用されることはありませんつまり、接続をクリーンアップするときさらに、現在の実装では、ブロック読み取りをしようとしません。
私はあなたのソリューションが動作するはずかのようにこれを読んで、あなたも近い自由に呼び出すことで、接続が再利用されます。
ということ他のヒント
私たちは、Java 5の上にも、この問題を抱えていたし、私たちのソリューションは、プールされた接続マネージャでのApacheのHttpClientに切り替えることです。
HTTPのためのSunのURLハンドラのキープアライブの実装は非常にバグがあります。アイドル状態の接続を閉じるには、No保守スレッドはありません。
キープアライブのもう一つの大きな問題は、あなたが回答を削除する必要があるということです。そうしないと、接続も孤立することになります。ほとんどの人は正しくエラーストリームを処理しません。
、正しくエラー応答を読み取る方法の例については、この質問に対する私の答えをご覧ください。参照するは、引用されました勘当で本当に役立ったものだった。
私たちは、Apache HttpClientをよりよく知っているが、それは別のjarファイルを必要とし、我々はアプレットでこのコードを使用する場合があります。
の呼び出しHttpURLConnection.connect()
は不要でした。私はそれが接続の再利用を防ぐかはわからないが、我々はそれを取り出しました。ストリームをクローズしても安全ですが、接続にdisconnect()
を呼び出すと、再利用を防ぐことができます。また、sun.net.http.errorstream.enableBuffering=true
を設定することができます。
ここでは、私たちが使用して終了するものである。
System.setProperty("http.maxConnections", String.valueOf(CONST.CONNECTION_LIMIT));
System.setProperty("sun.net.http.errorstream.enableBuffering", "true");
...
int responseCode = -1;
HttpURLConnection conn = null;
BufferedReader reader = null;
try {
conn = (HttpURLConnection) (new URL(url)).openConnection();
conn.setRequestProperty("Accept-Encoding", "gzip");
// this blocks until the connection responds
InputStream in = new GZIPInputStream(conn.getInputStream());
reader = new BufferedReader(new InputStreamReader(in));
StringBuffer sb = new StringBuffer();
char[] buff = new char[CONST.HTTP_BUFFER_SIZE];
int cnt;
while((cnt = reader.read(buff)) > 0) sb.append(buff, 0, cnt);
reader.close();
responseCode = conn.getResponseCode();
if(responseCode != HttpURLConnection.HTTP_OK) throw new IOException("abnormal HTTP response code:"+responseCode);
return sb.toString();
} catch(IOException e) {
// consume error stream, otherwise, connection won't be reused
if(conn != null) {
try {
InputStream in = ((HttpURLConnection)conn).getErrorStream();
in.close();
if(reader != null) reader.close();
} catch(IOException ex) {
log.fine(ex);
}
}
// log exception
String rc = (responseCode == -1) ? "unknown" : ""+responseCode;
log.severe("Error for HttpUtil.httpGet("+url+")\nServer returned an HTTP response code of '"+rc+"'");
log.severe(e);
}