Linux で実行されている Java アプリケーションから提供される Thrift API があります。私は .NET クライアントを使用して API に接続し、操作を実行しています。
サービスへの最初の数回の呼び出しはエラーなく正常に機能しますが、その後 (一見ランダムに) 呼び出しが「ハング」します。クライアントを強制終了して再接続しようとすると、サービスが再びハングするか、クライアントで次のエラーが発生します。
Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.
at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
at Thrift.Transport.TStreamTransport.Read(Byte[] buf, Int32 off, Int32 len)
(etc.)
JConsole を使用してスレッド ダンプを取得すると、サーバーがオンになっているaccept()
"Thread-1" prio=10 tid=0x00002aaad457a800 nid=0x79c7 runnable [0x00000000434af000]
java.lang.Thread.State: RUNNABLE
at java.net.PlainSocketImpl.socketAccept(Native Method)
at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:408)
- locked <0x00000005c0fef470> (a java.net.SocksSocketImpl)
at java.net.ServerSocket.implAccept(ServerSocket.java:462)
at java.net.ServerSocket.accept(ServerSocket.java:430)
at org.apache.thrift.transport.TServerSocket.acceptImpl(TServerSocket.java:113)
at org.apache.thrift.transport.TServerSocket.acceptImpl(TServerSocket.java:35)
at org.apache.thrift.transport.TServerTransport.accept(TServerTransport.java:31)
at org.apache.thrift.server.TSimpleServer.serve(TSimpleServer.java:63)
netstat
サーバー上では、クライアントを強制終了した数分後に最終的に消えるサービスポートへの接続が表示TIME_WAIT
されます(予想どおり)。
Thrift サービスをセットアップするコードは次のとおりです。
int port = thriftServicePort;
String host = thriftServiceHost;
InetAddress adr = InetAddress.getByName(host);
InetSocketAddress address = new InetSocketAddress(adr, port);
TServerTransport serverTransport = new TServerSocket(address);
TServer server = new TSimpleServer(new TServer.Args(serverTransport).processor((org.apache.thrift.TProcessor)processor));
server.serve();
TServerTransport
明示的なホスト名または IP アドレスを取るコンストラクターを使用していることに注意してください。ポートのみを指定するコンストラクターを取るように変更する必要があると思います(最終的にはにバインドしInetAddress.anyLocalAddress()
ます)。あるいは、「ワイルドカード」アドレス (「0.0.0.0」) にバインドするようにサービスを構成できると思います。
このサービスはオープンなインターネット上でホストされていないことに注意してください。プライベート ネットワークでホストされており、SSH トンネリングを使用してアクセスしています。したがって、サービスがバインドされているホスト名は、ローカル ネットワークでは解決されません (ただし、トンネリング経由で最初の接続を行うことはできます)。これはRMI TCP コールバックの問題と似たようなものでしょうか?
何が起こっているのか (これが一般的な問題である場合) についての技術的な説明、または実行できる追加のトラブルシューティング手順はありますか?
アップデート
今日も同じ問題がありましたが、今回jstack
は、Thrift サーバーが入力ストリームからの読み取りを永久にブロックしていることを示しています。
"Thread-1" prio=10 tid=0x00002aaad43fc000 nid=0x60b3 runnable [0x0000000041741000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:129)
at org.apache.thrift.transport.TIOStreamTransport.read(TIOStreamTransport.java:127)
at org.apache.thrift.transport.TTransport.readAll(TTransport.java:84)
at org.apache.thrift.protocol.TBinaryProtocol.readAll(TBinaryProtocol.java:378)
at org.apache.thrift.protocol.TBinaryProtocol.readI32(TBinaryProtocol.java:297)
at org.apache.thrift.protocol.TBinaryProtocol.readMessageBegin(TBinaryProtocol.java:204)
at org.apache.thrift.TBaseProcessor.process(TBaseProcessor.java:22)
at org.apache.thrift.server.TSimpleServer.serve(TSimpleServer.java:70)
TServerSocket
そのため、コンストラクターで「クライアント タイムアウト」を設定する必要があります。しかし、それにより、ブロックしているときにアプリケーションも接続を拒否するのはなぜでしょうaccept()
か?