我有一个在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地址。我怀疑我应该将其更改为采用仅指定端口的构造函数(最终绑定(bind)到InetAddress.anyLocalAddress())。另外,我想我可以将服务配置为绑定(bind)到“通配符”地址(“0.0.0.0”)。

我应该提到,该服务不是托管在开放Internet上的。它托管在专用网络中,我正在使用SSH隧道访问它。因此,服务绑定(bind)到的主机名不会在我的本地网络中解析(尽管我可以通过隧道进行初始连接)。我想知道这是否类似于RMI TCP callback problem吗?

有什么技术上的解释(如果这是一个常见问题),还是我可以采取的其他疑难解答步骤?

更新

今天有同样的问题,但是这次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()时导致应用程序也拒绝连接?

最佳答案

从您的堆栈跟踪中看来,您正在使用TSimpleServer,其javadocs say



您可能想使用的是TThreadPoolServer

最有可能发生的是,TSimpleServer的单线程被阻塞,以等待死客户端响应或超时。并且由于TSimpleServer是单线程的,因此没有线程可用于处理其他请求。

09-11 18:33