7

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()か?

4

4 に答える 4

4

スタック トレースから、TSimpleServer を使用しているようです。その javadoc に

テスト用の単純なシングルスレッド サーバー。

おそらくあなたが使いたいのはTThreadPoolServerです。

最も可能性が高いのは、TSimpleServer の単一スレッドがブロックされ、停止したクライアントが応答するかタイムアウトするのを待っていることです。また、TSimpleServer はシングル スレッドであるため、他の要求を処理するために使用できるスレッドはありません。

于 2013-01-27T21:44:44.867 に答える
3

いくつか提案があります。サーバーへの最初の数回の呼​​び出しが機能し、その後ハングが発生するとおっしゃいました。それは手がかりです。これが発生するシナリオの 1 つは、クライアントがバイトをサーバーに完全に送信していない場合です。私は TSimpleServer に精通していませんが、TSimpleServer はポートでリッスンし、何らかのバイナリ プロトコルを持ち、クライアントがそのプロトコルで通信することを期待していると思います。.net クライアントは、バイトを送信してこのサーバーと通信しています。出力バッファが正しくフラッシュされていない場合、すべてのバイトがサーバーに送信されず、サーバーがハングする可能性があります。

Java では、これは次のようにクライアント側で発生する可能性があります。

BufferedOutputStream stream = new BufferedOutputStream(socket.getOutputstream()) //get the socket stream to write 
stream.write(content);//write everything that needs to be written 
stream.flush();//if flush() is not called, could result in server getting incomplete packets resulting in hangs!!!

提案:

a) .net クライアント コードを調べます。サーバーと実際に通信するコードの一部が、同等の flush() または cleanup メソッドを適切に呼び出しているかどうかを確認してください。注:彼らのドキュメントから、トランスポート層がflush()を定義していることがわかりました。.net コードをスキャンして、トランスポート メソッドを使用しているかどうかを確認する必要があります。http://thrift.apache.org/docs/concepts/

b) さらにデバッグするために、.net クライアントをシミュレートする小さな Java クライアントを作成してみることができます。Linux マシン (TSimpleServer が実行されているマシンと同じマシン) で Java クライアントを実行します。同じ問題が発生するかどうかを確認してください。その場合は、Java クライアントをデバッグして、根本原因を見つけることができます。そうでない場合は、.net クライアントが実行されている場所で実行し、問題があるかどうかを確認して、そこから取得することができます。

編集 :c)ここでJavaのサンプルのリサイクルクライアントコードを確認できました 。// コードを実行します transport.close(); a) で提案されているように、.net クライアント コードを調べて、完了時にトランスポート メソッド flush() および close() を呼び出しているかどうかを確認できます。

于 2013-01-31T07:51:34.503 に答える
0

同様のc ++サーバー/クライアント環境があります。

C ++ クライアントはメソッド (attributeDefinitionsAliases) を呼び出し、応答を待ちます。

C ++ サーバーはソケットへの書き込みを開始しますが、ロックします。Wireshark キャプチャ:

ここに画像の説明を入力 C ++ サーバーで C ++ クライアントを閉じると、例外が表示されます。

Thrift 内部メッセージ: TSocket::write_partial() send() : errno = 10054

Thrift 内部メッセージ: TConnectedClient が死亡しました: write() send(): errno = 10054

編集 1: 倹約の問題ではありません。サーバーの起動/起動方法に問題があるようです。popen を使用して QProcess ( https://doc.qt.io/archives/qt-4.8/qprocess.html ) でサーバーを起動/起動するアプリケーション (launcher-app) が正常に動作します

于 2019-09-16T07:00:20.143 に答える
0

Thrift サービスをワイルドカード アドレス (「0.0.0.0」) にバインドすると、問題が解決し、ハングすることはなくなりました。

マルチスレッド サーバーを使用すると、アプリケーションの応答性が向上しますが、それでもハングしたり不完全な要求が発生したりします。

誰かがこの質問に出くわし、より完全な説明と、それが Java RMI TCP コールバックの問題 (質問でリンクしたもの) とどのように関連しているかを提供できる場合は、賛成票を投じてください。

于 2013-02-05T20:36:12.273 に答える