深入浅出jcr之16 该死的RMI,我们需要HTTP+简单RPC协议

        从这篇文章开始,ahuaxuan不再详细描述jackrabbit中的实现原理,而是把注意力放在jackrabbit中做的不好的地方,不敢说是批判,但是有些技术上的决策错误还是值得拿出来讨论讨论的。其中一个就是jackrabbit的客户端和jackrabbit server的通信方式--RMI。围绕这个问题我们可以展开一系列的讨论。

本文分为几个部分
1 为什么要抛弃RMI
2 为什么要选择基于HTTP的RPC协议
3 展望未来

RMI这个东西原理之前很多人搞不清楚,因为sun的RMI实现源码虽说开源了,但是却很难找到,很久之前,ahuaxuan经过一番折腾之后终于找到了它的源代码,翻了一下源代码也发现了一些问题,其中有一些概念上的定义和一般的场景相混淆,叫人很难理解那些配置的含义。但是这篇文章并不是想要批判sun的RMI的实现,如果有需要,ahuaxuan会写另外一篇文章来精要的描述一下sun的RMI的实现。

那么今天的主题是什么?是jackrabbit中RMI的使用方式。RMI可以作为有状态通信的手段。而jackrabbit中也是这样用的,这意味着什么呢?

当我们通过RMI拿到一个node时,然后遍历这个node的10个属性,会发生什么事情?
client会通过RMI发生11个remote call到jackrabbit中,虽然我们只拿了一个node,但是却发送了11个请求,如果我们拿10个node,需要上百次请求。这还只是一个线程发起的,如果客户端上多个线程同一个时间发起类似的请求,那么10个线程就会发生上千次操作。这个是他妈多夸张的事情啊。

在我的眼里,jcr其实和数据库的实现原理是类似的,在数据库操作中,我们也会遇到1+n的问题,我们要做的就是要避免这样的问题。我们需要的是争取在客户端指定需要返回的properties,然后一次返回,这样才能比较靠谱,客户端10个线程即使同时发生请求,那么也只会发生10次请求,和前面的1000次请求相比,减少了990次remote call,带来的性能提高是无可置疑的。

那么要实现这样的效果,我们还是需要选择一种通信方式,是RMI吗,不是?为什么不是,因为sun的RMI实现在客户端的connection pool和服务器端的thread pool都太简单了,不过本文不会详细介绍这个问题,我们跳过RMI,来选择其他的通信方式。

不用RMI,那么要不就是自定义协议,理论上是没有任何问题的,自定义协议的好处的可控性较强,而且可以根据业务的特性有效的降低字节数。减少带宽的消耗。它的问题是什么呢?开发周期较长,那么我们是否可以使用现成的协议呢?

当然没有问题,最便捷的方式就是使用http协议,在http协议之上构造自己简单的RPC协议,这个步骤很简单,虽然很简单,但是能不做最好不做,那么是否有现成的技术呢。有的,hessian.

这里的hessian不只是hessian的序列化算法,也包括hessian包中rpc框架,当然它的框架有很大的一个缺点,那就是短连接,任何一个rpc请求返回之后 URLConnection就关闭了,新的RPC请求必须重新打开一个Connection,所以作为一个高并发的客户端,我们必须支持connection pool, 有现成的吗?? 有的,我们可以使用HTTPClient,呵呵,怎么做呢,稍微看一下代码,我们会发现我们只需要重新写一个InvocationHandler接口的实现就行了,怎么实现呢,直接参考HessianProxy就可以了。整体的实现较为简单,ahuaxuan不一一赘述。

当然这个只是客户端的,服务器端需要重新封装一些接口,比如getNodesByIds(),可以通过Node的id集合批量的进行操作。接着服务器端的这些接口需要交给客户端,客户端利用动态代理技术创建这些接口的实现类,调用接口的方法的实现会生成RPC协议需要的参数,必然类名,方法名,参数,以及它们在binary中的位置,这样服务器拿到这个协议数据之后就可以寻找到对于的目标类,然后调用对应的方法(并传入客户端传来的参数),这一切都完美了。一切细节都被屏蔽了。客户端只需要拿到服务器端发布的接口就可以了,然后。。。。呵呵呵呵。


虽然在改造的过程中会遇到一些小小的问题(包括hessian包的一些小bug),但是这些小小的绊脚石并不能阻碍我们前进的脚步。

这样原来有状态的通信的方式改成了无状态的方式,而且HTTPClient的连接池配置更为丰富。更重要的是降低了很多很多很多的remote call. 性能提高也称为了必然。

当然这个是最好的方式吗,当然不是,最终,我们的目的是直接基于TCP打造我们的RPC协议,而不是基于HTTP的RPC协议,毕竟HTTP会传输很多我们不需要的字节,直接使用mina或者netty是毕竟合适的选择。当然这些又是后话了。

也许你会问jackrabbit为什么没有这样做的,我相信它会的,它最终会这样做的。

上一篇:如何编写一个全新的 Git 协议


下一篇:三星Exynos芯片组其它手机不能用?原来是高通在搞鬼