java - 什么会导致 RMI 方法调用间歇性失败?

标签 java networking rmi

长话短说,我有一个 RMI 服务器和客户端。服务器和客户端能够相互进行 RMI 调用。 Client 连接到 Server 后,Server 可能会快速连续调用 Client 上的数百个方法。

问题是这样的——在大量的服务器到客户端方法调用结束时,有些会失败,因为 RMI 声称它无法建立从服务器到客户端的连接,尽管之前有数百次调用会失败成功。我无法发布任何实际代码,因为这个项目相当大(大约 50k 行),但这是抛出的异常的完整堆栈跟踪:

java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:
java.rmi.ConnectIOException: error during JRMP connection establishment; nested exception is:
java.net.SocketException: Connection reset
at sun.rmi.server.UnicastServerRef.dispatch(Unknown Source)
at sun.rmi.transport.Transport$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(Unknown Source)
at sun.rmi.transport.StreamRemoteCall.executeCall(Unknown Source)
at sun.rmi.server.UnicastRef.invoke(Unknown Source)
at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(Unknown Source)
at java.rmi.server.RemoteObjectInvocationHandler.invoke(Unknown Source)
at $Proxy0.findClassDefinition(Unknown Source)
at com.fabric.network.NetworkClassLoader.findClass(NetworkClassLoader.java:111)
at java.lang.ClassLoader.loadClass(Unknown Source)
at com.fabric.network.NetworkClassLoader.loadClass(NetworkClassLoader.java:131)
at java.lang.ClassLoader.loadClass(Unknown Source)
at com.fabric.network.MessageSocket$CustomObjectInputStream.resolveClass(MessageSocket.java:171)
at java.io.ObjectInputStream.readNonProxyDesc(Unknown Source)
at java.io.ObjectInputStream.readClassDesc(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.defaultReadFields(Unknown Source)
at java.io.ObjectInputStream.readSerialData(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.defaultReadFields(Unknown Source)
at java.io.ObjectInputStream.readSerialData(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at com.fabric.network.MessageSocket.receive(MessageSocket.java:118)
at com.fabric.application.driver.NodeRemoteDriver$IncomingMessageThread.run(NodeRemoteDriver.java:205)
Caused by: java.rmi.ConnectIOException: error during JRMP connection establishment; nested exception is:
java.net.SocketException: Connection reset
at sun.rmi.transport.tcp.TCPChannel.createConnection(Unknown Source)
at sun.rmi.transport.tcp.TCPChannel.newConnection(Unknown Source)
at sun.rmi.server.UnicastRef.invoke(Unknown Source)
at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(Unknown Source)
at java.rmi.server.RemoteObjectInvocationHandler.invoke(Unknown Source)
at $Proxy2.findClassDefinition(Unknown Source)
at com.fabric.network.ClassDefinitionCache.findClassDefinition(ClassDefinitionCache.java:78)
at com.fabric.management.host.NodeManagementServices.findClassDefinition(NodeManagementServices.java:231)
at sun.reflect.GeneratedMethodAccessor16.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at sun.rmi.server.UnicastServerRef.dispatch(Unknown Source)
at sun.rmi.transport.Transport$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: java.net.SocketException: Connection reset
at java.net.SocketInputStream.read(Unknown Source)
at java.io.BufferedInputStream.fill(Unknown Source)
at java.io.BufferedInputStream.read(Unknown Source)
at java.io.DataInputStream.readByte(Unknown Source)
... 21 more

再次抱歉,我无法提供太多代码,但我不一定要求修复代码 - 我只是想弄清楚为什么会发生这种情况。

编辑

添加了完整的堆栈跟踪。

最佳答案

好吧,在几乎把我所有的头发都拔出来之后,事实证明 RMI 试图打开太多的端口。我在后台使用自定义 RMISocketFactory 实现。这个自定义实现是单例的,所以我认为没有必要实现hashCode()equals()。确实是一个非常痛苦的错误......

事实证明,如果 RMI 确定它需要创建的套接字是由 RMISocketFactory 创建的,而该工厂不等同于创建它想要的套接字的工厂,那么 RMI 将不会重用套接字重用。 RMI 依靠 equals()hashCode() 来执行此检查。在我的自定义套接字工厂中正确实现这两种方法后,这些间歇性问题就消失了。

这个问题的描述可以在这里找到:

http://docs.oracle.com/javase/7/docs/technotes/guides/rmi/faq.html

无论如何,感谢大家的关注,非常感谢您的宝贵时间!

附加信息

我之前没有注意到的一个次要问题是我使用的 ServerSocket 正在用完传入连接请求的队列空间,这也导致连接被丢弃。使用具有较大 newConnectionQueueSize 的构造函数 new ServerSocket(port, newConnectionQueueSize, bindAddress) 也有助于解决此问题。

关于java - 什么会导致 RMI 方法调用间歇性失败?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10422603/

相关文章:

java - 从 JList 中获取多个选定项

java - 如何在Java中同一网络上的服务器和客户端之间创建同时连接?

java - RMI 和引用/代理类

Java RMI - 从接口(interface)/$Proxy 转换为原始类?

java - 遇到 "Exception in thread "main“java.util.NoSuchElementException”分配错误

java - 如何将 int 转换为字符串?

networking - 游戏服务器应该如何以定义的滴答率接收 udp 数据包?

java - 运行单元测试时 RmiRegistry 创建多次

java - JTextField 无法正常工作

java - 异步套接字延迟