几次成功请求后,Thrift TSimpleServer无响应

时间:2013-01-24 23:04:37

标签: java .net tcp thrift

我有一个在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();

请注意,我们使用的是带有显式主机名或IP地址的TServerTransport构造函数。我怀疑我应该更改它以获取仅指定端口的构造函数(最终绑定到InetAddress.anyLocalAddress())。或者,我想我可以将服务配置为绑定到“通配符”地址(“0.0.0.0”)。

我应该提一下,该服务并非托管在开放的互联网上。它托管在一个私人网络中,我正在使用SSH隧道来实现它。因此,服务绑定的主机名在我的本地网络中无法解析(尽管我可以通过隧道建立初始连接)。我想知道这是否与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()上时拒绝连接?

4 个答案:

答案 0 :(得分:4)

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

  

用于测试的简单单线程服务器。

您可能想要使用的是TThreadPoolServer

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

答案 1 :(得分:3)

我有一些建议。您提到前几次调用服务器工作,然后有挂起。这是一个线索。发生这种情况的一种情况是客户端未将字节完全发送到服务器。我不熟悉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机器上运行java客户端(运行TSimpleServer的同一台机器)。看看它是否会导致同样的问题。如果是,您可以调试您的Java客户端并找到根本原因。如果没有,您可以在.net客户端运行的位置运行它,看看是否有任何问题并从那里开始。

编辑:c)我能够在 Java 中看到示例旧货客户端代码:https://chamibuddhika.wordpress.com/2011/10/02/apache-thrift-quickstart-tutorial/ 我注意到 transport.open(); //做一些代码 transport.close(); 正如a)中所建议的,您可以通过.net客户端代码查看是否在完成时调用传输方法flush()和close()

答案 2 :(得分:0)

将Thrift服务绑定到通配符地址(“0.0.0.0”)解决了问题,不再挂起。

使用多线程服务器会使应用程序响应更快,但仍会导致挂起/未完成的请求。

如果有人偶然发现了这个问题,并且可以提供更完整的解释以及它与Java RMI TCP回调问题的关系(我在我的问题中链接到了这个问题),请为您提供支持。

答案 3 :(得分:0)

我有一个类似的c ++服务器/客户端环境。

c ++客户端调用方法(attributeDefinitionsAliases)并等待响应。

c ++服务器开始写入套接字,但会锁定。 Wireshark捕获:

enter image description here 在c ++服务器上关闭c ++客户端后,出现异常:

  

节省内部消息:TSocket :: write_partial()send():errno = 10054

     

节俭内部消息:TConnectedClient死亡:write()send():errno = 10054

编辑1: 这不是一个节俭的问题。服务器启动/启动方式似乎有问题。我有一个应用程序(launcher-app),它使用popen 工作正常来启动/启动QProcess(https://doc.qt.io/archives/qt-4.8/qprocess.html)服务器。