java 什么会导致 RMI 方法调用间歇性失败?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10422603/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What could cause RMI method calls to fail intermittently?
提问by CodeBlind
Long story short, I have an RMI Server and Client. The Server and Client are capable of making RMI calls on each other. After the Client connects to the Server, the Server may make many hundreds of method calls in quick succession on the Client.
长话短说,我有一个 RMI 服务器和客户端。服务器和客户端能够相互进行 RMI 调用。Client 连接到 Server 后,Server 可能会在 Client 上快速连续地进行数百个方法调用。
The problem is this - towards the end of a huge batch of Server-to-Client method calls, some will fail because RMI claims it cannot establish a connection from Server-to-Client, even though hundreds of calls before it will succeed. I can't post any real code because this project is rather large (about 50k lines), but here's the full stack trace of the exception that gets thrown:
问题是这样的 - 在大量服务器到客户端方法调用结束时,有些会失败,因为 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.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.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
Again, sorry I can't provide much in the way of code, but I'm not necessarily asking for a code fix - I just want to wrap my head around why this might be happening.
再次,抱歉,我无法提供太多代码,但我不一定要求修复代码 - 我只是想弄清楚为什么会发生这种情况。
EDIT
编辑
Added the full stack trace.
添加了完整的堆栈跟踪。
回答by CodeBlind
Ok, so after nearly pulling all my hair out, it turns out that RMI was trying to open up way too many ports. I am using a custom RMISocketFactory
implementation under the hood. This custom implementation is a singleton, so I didn't think it was necessary to implement hashCode()
and equals()
. A very painful mistake indeed...
好吧,在几乎把我所有的头发都拔掉之后,结果证明 RMI 试图打开太多的端口。我在后台使用自定义RMISocketFactory
实现。这个自定义实现是单例的,所以我认为没有必要实现hashCode()
和equals()
。确实是一个非常痛苦的错误......
Turns out, RMI won't reuse sockets if RMI determines that the socket it needs to create is to be created by an RMISocketFactory
that isn't equivalent to the factory that created the socket it wants to reuse. RMI relies on equals()
and hashCode()
to perform this check. Once I correctly implemented these two methods in my custom socket factory, these intermittent problems went away.
事实证明,如果 RMI 确定它需要创建的套接字是由与RMISocketFactory
创建它想要重用的套接字的工厂不同的工厂创建的,则 RMI 不会重用套接字。RMI 依赖equals()
并hashCode()
执行此检查。一旦我在我的自定义套接字工厂中正确实现了这两种方法,这些间歇性问题就消失了。
The description of this issue can be found here:
可以在此处找到此问题的描述:
http://docs.oracle.com/javase/7/docs/technotes/guides/rmi/faq.html
http://docs.oracle.com/javase/7/docs/technotes/guides/rmi/faq.html
At any rate, thanks to all for taking a look at this, I sure appreciate your time!
无论如何,感谢所有看过这个的人,我非常感谢你的时间!
Additional Information
附加信息
A secondary issue that I didn't notice before was that the ServerSocket
I was using was running out of queue space for incoming connection requests, which also contributed to the connections being dropped. Using the constructor new ServerSocket(port, newConnectionQueueSize, bindAddress)
with a larger newConnectionQueueSize
contributed to solving this problem as well.
我之前没有注意到的第二个问题是,ServerSocket
我使用的连接请求的队列空间不足,这也导致连接被丢弃。使用new ServerSocket(port, newConnectionQueueSize, bindAddress)
更大的构造函数也newConnectionQueueSize
有助于解决这个问题。