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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 00:59:02  来源:igfitidea点击:

What could cause RMI method calls to fail intermittently?

javanetworkingrmi

提问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 RMISocketFactoryimplementation 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 RMISocketFactorythat 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 ServerSocketI 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 newConnectionQueueSizecontributed to solving this problem as well.

我之前没有注意到的第二个问题是,ServerSocket我使用的连接请求的队列空间不足,这也导致连接被丢弃。使用new ServerSocket(port, newConnectionQueueSize, bindAddress)更大的构造函数也newConnectionQueueSize有助于解决这个问题。