TinyRMI---RMI的封装、扩展及踩到的坑的解决

简介:

在Tiny的并行计算中,引用了远程方法调用工程,就是这里说的TinyRMI,当时在写测试用例的时候,只是在单机进行了测试,一切安好,但是Dawn在使用时,在多机进行试用,结果就出现了问题,最后花了不下一人周,才解决了Dawn发现的问题,最终解决了问题,也发现了RMI中的一些坑。可能有的人已经走过了,有的人如果没有碰到,也可能会掉同样的坑,因此把它成文,以飨读者,避免上同样的当。

此文的形成离不开Dawn的深入测试与分析,在此表示深深感谢!

功能需求

期望对Jdk中的Rmi进行一定的封装提供以下特性:

  1. 支持本地对象注册与注销
  2. 支持远程对象注册与注销
  3. 支持断线重连
    由于网络故障,导致连接断开,在网络故障恢复之后,可以继续正常访问
    由于RmiServerLocal停止,重新启动后,客户端注册的对象要继续可以正常访问
    由于RmiServerRemote停止 ,重新启动后,客户端注册的对象需要重新注册
  4. 支持对象校验
    如果某些对象已经失效,服务器端可以把它从注册表去除,以避免别人拿到失效的对象

接口设计

 

可以看到RmiServer是继承了Serializable和Remote接口的一个接口,它提供了注册对象及取消注册对象的多个方法。当然也提供了一些辅助方法,看起来还是非常简单的。

另外还有一个辅助接口Verifiable,对于加入的远程对象,如果实现了此接口,则可以对其有效性进行验证,如果已经失效,将被自动从注册无中去除。

?
1
2
3
4
5
6
7
8
9
10
11
/**
  * 是否可验证,实现了此接口的类,可以进行校验
  */
public interface Verifiable {
     /**
      * 校验,如果校验时不出现异常,就表示是OK的
      *
      * @throws RemoteException
      */
     void verify() throws RemoteException;
}

当然,要加入到RmiServer中,也要有一定的约束,因此设定了接口RemoteObject

?
1
2
public interface RemoteObject extends Serializable, Remote {
}

好的,至此为止,接口就算设计完了。

代码实现

第一版代码实现,偶预想的非常简单,如何获取Registry作为一个抽象方法由子类实现,其它都是针对Registry进行的操作,就放在抽象类中实现,分分钟写好,然后本地测试通过,洋洋自得中,却蒙蒙然不知犯下了严重的错误.....

在单机环境下,测试都是好的,不管是RMI自己的测试用例还是复用它的并行计算工程。

但是Dawn在使用的时候,采用了Linux机器两个物理机进行测试,问题出现了,错误信息如下:

?
1
java.rmi.AccessException: Registry.Registry.bind disallowed; origin / 192.168 .xxx.xxx is non-local host

我们用物理的两台计算机进行测试,也有同样的问题。

于是Baidu、谷歌都用上了,查找了终于找到原因:

Registry只有本地的才可以对注册表进行修改,远程的只能用来查看。

于是把RmiServerLocal作为一个远程对象提供出来,让RmiServerRemote来调用,心想这样总可以了吧??但是还是不行,还是同样的问题。

仔细阅读JavaDoc文档和找到的一些材料,才理解了,这个bind过程只能在RmiServerLocal所在的机器中执行,即使是通过远程服务调用,它还是认为是在RmiServerRemote中调用的......这尼玛的坑爹了,这个RMI这么难用,设计者知道么?

这次改进,远程机注册的时候,只是添加到RmiServerLocal中的一个Queue中,然后在RmiServerLocal所在机器开一个扫描线程,来进行bind,unbind操作,这样总保证是在Local中执行了。

测试一下,确实OK了......还没有高兴半下,Dawn报过来,又出问题了。

Windows作Server,Linux做客户端的时候是OK的,但是Linux只要做服务器端就还是不行。

继续查找原因,看到资料说是因为Linux查找到IP与外部访问的IP不对应导致,需要修改/etc/hosts文件中的127.0.0.1到外部方面的IP地址。

修改之后,问题得到解决。

测试过程

初步跑通之后,接下来就是各种各样的测试及各种各样的问题。

由于采用了异步注册的关系,导致注册时间过后一小段时间才可以访问注册到的对象。

有对象访问冲突问题,等等等等,总之就是测了改,改了测,最后终于修正完毕,终于有一个稳定的可用的版本出现了。

最后,由于原来的设想的代码共用基本上没有复用的价值了,因此RmiServerLocal和RmiServerRemote都是各自的实现,不再有共同的基类。

经验总结

  1. 测试用例的编写一定要充分覆盖。
  2. 涉及到网络方面的测试,不能仅在本地测试通过就可以,一定要用真实的环境进行测试。
  3. 不同的操作系统处理还是有一些不同,不要太迷信Java的一次编写到处使用,事实是到处可以跑,在大多数情况下也是可以正确的跑的,但是有些外部条件不同,可能会导致故障的出现。

关于RMI:

  1. 如果是客户端仅调用服务器端提供的对象,那么是非常简单的。
  2. 如果是客户端也要向服务器注册远程对象,那么就需要采用异步的方式,搞一下注册或注销队列。

代码本身不复杂,需要的同学,请自行查看源代码。

https://git.oschina.net/tinyframework/tiny/tree/master/framework/org.tinygroup.rmi

相关文章
|
6月前
|
Prometheus Kubernetes Java
ChaosBlade注入问题之查看实现模块位置如何解决
ChaosBlade 是一个开源的混沌工程实验工具,旨在通过模拟各种常见的硬件、软件、网络、应用等故障,帮助开发者在测试环境中验证系统的容错和自动恢复能力。以下是关于ChaosBlade的一些常见问题合集:
|
6月前
|
中间件
NetCore通过中间件判断接口是否存在 AllowAnonymousAttribute 特性
特性来判断一个接口是否被标记为允许匿名访问。以下是一个简单的中间件示例,用于在请求管道中检查接口是否被。.NET Core中,可以通过检查接口上的。在应用程序中使用此中间件,将其添加到。
84 0
|
6月前
|
编译器 C++ 容器
【C++】vector容器接口要点的补充
【C++】vector容器接口要点的补充
|
开发框架 网络协议 Java
[J2EE规范]RMI简单实例
[J2EE规范]RMI简单实例
93 0
|
Java API 数据安全/隐私保护
翻阅必备,一看必知——Java -GUI界面设计(容器,布局,监听,事件 API大全
翻阅必备,一看必知——Java -GUI界面设计(容器,布局,监听,事件 API大全
234 0
|
编解码 分布式计算 Java
基于 netty 封装的超简单通俗易用 服务端客户端交互框架 《net-framework》原理,源码和使用说明,开箱即用,只需要开发业务逻辑,完全自定义无限扩充 [结尾附github源码]
基于 netty 封装的超简单通俗易用 服务端客户端交互框架 《net-framework》原理,源码和使用说明,开箱即用,只需要开发业务逻辑,完全自定义无限扩充 [结尾附github源码]
基于 netty 封装的超简单通俗易用 服务端客户端交互框架 《net-framework》原理,源码和使用说明,开箱即用,只需要开发业务逻辑,完全自定义无限扩充 [结尾附github源码]
|
消息中间件 缓存 JavaScript
这16个有用的 SpringBoot 扩展接口,居然还有人不知道?
这16个有用的 SpringBoot 扩展接口,居然还有人不知道?
|
运维 Dubbo 中间件
Dubbo3 源码解读-宋小生-6:Dubbo的SPI扩展机制之普通扩展对象的创建与Wrapper机制的源码解析
> Dubbo3 已经全面取代 HSF2 成为阿里的下一代服务框架,2022 双十一基于 Dubbo3 首次实现了关键业务不停推、不降级的全面用户体验提升,从技术上,大幅提高研发与运维效率的同时地址推送等关键资源利用率提升超 40%,基于三位一体的开源中间件体系打造了阿里在云上的单元化最佳实践和统一标准,同时将规模化实践经验与技术创新贡献开源社区,极大的推动了开源技术与标准的发展。 > 本文是
206 0
|
负载均衡 程序员 Go
Go RPC入门指南:RPC的使用边界在哪里?如何实现跨语言调用?
就是因为无法在同一个进程内,或者无法在同一个服务器上通过本地调用的方式实现我们的需求。 HTTP能满足需求但是不够高效,所以我们需要使用RPC。
176 0
Go RPC入门指南:RPC的使用边界在哪里?如何实现跨语言调用?
|
XML 编解码 缓存
100 行代码搞定了 RPC 原理,大家随便问。。(1)
100 行代码搞定了 RPC 原理,大家随便问。。(1)
173 0
100 行代码搞定了 RPC 原理,大家随便问。。(1)