Java RMI 在租约到期后不关闭套接字
我启用 RMI 的应用程序似乎正在泄漏套接字。 我有一个通过 RMI 提供服务的 Java 应用程序。 它使用在 Linux 上运行的 Java SE 1.6 RMI 实现。 我观察到的问题是,如果客户端使用注册表获取对我的远程对象的引用,然后连接突然断开(断电、电缆拔出等...),服务器将保持套接字打开。 我希望 RMI 实现能够在客户端的租约到期后进行清理,但这并没有发生。 在服务器上,当租约到期时,将调用我的远程对象的 unreferenced()
方法,但套接字在 netstat 中无限期地保持可见,处于“ESTABLISHED”状态。
由于我们无法强制客户端执行任何特定行为,几天后,我们的 Linux 发行版中的打开文件描述符就达到了默认限制 1024,导致服务器无法打开任何新的套接字或文件。 我考虑过 TCP keepalive,但由于 RMI 抽象了网络层,因此在建立连接后我无法访问实际的服务器套接字。
有没有办法强制 RMI 层清理与租约过期的客户端连接相关的套接字?
更新:我使用的解决方案与所选答案类似,但使用不同的方法。 我使用了一个自定义套接字工厂,然后将 createServerSocket() 返回的 ServerSocket 实例包装在一个透明包装器中,该包装器传递除accept() 之外的所有方法。 在 accpet 方法中,在返回套接字之前启用 keepalive。
public class KeepaliveSocketWrapper extends ServerSocket
{
private ServerSocket _delegate = null;
public KeepaliveSocketWrapper(ServerSocket delegate)
{
this._delegate = delegate;
}
public Socket accept() throws IOException
{
Socket s = _delegate.accept();
s.setKeepAlive(true);
return s;
}
.
.
.
}
My RMI enabled application seems to be leaking sockets. I have a Java application providing a service over RMI. It is using the Java SE 1.6 RMI implementation running on Linux. The problem I am observing is that if a client obtains a reference to my Remote object, using the Registry, and then the connection is severed abruptly (power loss, cable unplugged, etc...), the server keeps the socket open. I would expect the RMI implementation to clean up after the client's lease expires, but that is not happening. On the server, my Remote object's unreferenced()
method is called when the lease expires, but the socket remains visible in netstat in the "ESTABLISHED" state indefinitely.
Since we are not able to force the clients into any specific behavior, after several days we are hitting the default limit, 1024 in our Linux distro, for open file descriptors, causing the server to become unable to open any new sockets or files. I thought about TCP keepalives, but since RMI is abstracting away the network layer, I don't have access to the actual server socket after the connection has been established.
Is there any way to force the RMI layer to cleanup sockets tied to client connections with expired leases?
Update: The solution I used is similar to the chosen answer, but uses a different approach. I used a custom socket factory, and then wrapped the ServerSocket instance returned by createServerSocket() in a transparent wrapper that passed all methods through, except for accept(). In the accpet method, keepalives are enabled before the socket is returned.
public class KeepaliveSocketWrapper extends ServerSocket
{
private ServerSocket _delegate = null;
public KeepaliveSocketWrapper(ServerSocket delegate)
{
this._delegate = delegate;
}
public Socket accept() throws IOException
{
Socket s = _delegate.accept();
s.setKeepAlive(true);
return s;
}
.
.
.
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
然后就可以使用RMI了。
如果我没记错的话,诀窍是在系统打开第一个套接字之前设法设置工厂——Java 不允许覆盖工厂; 在最坏的情况下,使用反射来覆盖它:)))(开玩笑;这将是一个黑客)
此外,您可能需要允许 SecurityManager 中的特定操作。
and then just use RMI.
The trick, if I remember right, is to manage to set the factory before the system opens first socket -- Java disallows to overwrite the factory; in worst case, use reflection to overwrite it :))) (kidding; would be a hack)
Also, you may need to allow specific operation in SecurityManager.