まず、いくつかの背景。一連の短い URL を展開/解決するワーカーがあります。
http://t.co/example -> http://example.com
したがって、リダイレクトに従うだけです。それでおしまい。接続からデータを読み取ることはありません。200 を取得した直後に、最終的な URL を返し、InputStream を閉じます。
さて、問題そのもの。運用サーバーでは、リゾルバー スレッドの 1 つがInputStream.close()
呼び出し内でハングします。
"ProcessShortUrlTask" prio=10 tid=0x00007f8810119000 nid=0x402b runnable [0x00007f882b044000]
java.lang.Thread.State: RUNNABLE
at java.io.BufferedInputStream.fill(BufferedInputStream.java:218)
at java.io.BufferedInputStream.skip(BufferedInputStream.java:352)
- locked <0x0000000561293aa0> (a java.io.BufferedInputStream)
at sun.net.www.MeteredStream.skip(MeteredStream.java:134)
- locked <0x0000000561293a70> (a sun.net.www.http.KeepAliveStream)
at sun.net.www.http.KeepAliveStream.close(KeepAliveStream.java:76)
at java.io.FilterInputStream.close(FilterInputStream.java:155)
at sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.close(HttpURLConnection.java:2735)
at ru.twitter.times.http.URLProcessor.resolve(URLProcessor.java:131)
at ru.twitter.times.http.URLProcessor.resolve(URLProcessor.java:55)
at ...
skip()
簡単な調査の後、接続プールに戻す前にストリームをクリーンアップするために呼び出されることを理解しました (キープアライブがオンに設定されている場合)。それでも、この状況を回避する方法がわかりません。さらに、私たちのコードに悪い設計があるのか、それとも JDK に問題があるのか疑問です。
したがって、質問は次のとおりです。
- ハングアップを避けることはでき
close()
ますか?たとえば、妥当なタイムアウトを保証します。 - 接続からのデータの読み取りをまったく回避することは可能ですか? 最終的な URL だけが必要であることを思い出してください。
skip()
本当は、絶対に呼ばれたくない…と思い ます。
アップデート:
KeepAliveStream、79 行目、close()
メソッド:
// Skip past the data that's left in the Inputstream because
// some sort of error may have occurred.
// Do this ONLY if the skip won't block. The stream may have
// been closed at the beginning of a big file and we don't want
// to hang around for nothing. So if we can't skip without blocking
// we just close the socket and, therefore, terminate the keepAlive
// NOTE: Don't close super class
try {
if (expected > count) {
long nskip = (long) (expected - count);
if (nskip <= available()) {
long n = 0;
while (n < nskip) {
nskip = nskip - n;
n = skip(nskip);} ...
JDK自体にバグがあるように思えます。残念ながら、これを再現するのは非常に困難です...