K神吹水系列之RPC

我们知道,有几十种的RPC技术,corba啊,com+,soap啊,xml-rpc,rmi、.net remoting,hessian,pb,thrift,avro,,,甚至dwr,blazeds
远程过程调用,肯定走网络的,所以可以按协议分成走tcp的,走http的,
少量的是走http的,比如hessian,soap一般也走http
大部分直接走tcp
对了,soap可以走非http方式的哦,一般人不知道。以前的axis和cxf源码里会带有webservice走jms协议的例子。
远程调用,肯定是两个系统间的调用,按这两个系统的平台环境,又可分为跨平台的rpc和某个平台特有的rpc
比如rmi只能java里用
.net remoting只能.net里用
blazeds只能flex调用java后台
dwr只能javascript调用java后台
至于DCOM,只能windows上用
两个系统间的调用,肯定要传递数据,所以,按照传递数据的方式,又可以分为文本的和二进制的rpc
xml-rpc,dwr,soap,都是文本的。
即,中间传输的数据,都是xml文本,或是js文本(js片段)。
其他协议一般都是二进制的。hessian是走http的二进制。
哦,json不太适合做rpc,虽然也能做,有点不爽。dwr可以看做json的。但是包了一些js代码进去。json最大的问题是,描述能力有限。最简单的例子就是:无法表示类型。{ a: 100 }请问as是什么类型的
除了部分脚本语言,大部分语言都是有严格的类型系统的
简单的讲就是,json没有schema所以难以处理复杂的应用场景,没有xml下的ws-*家族有那么成体系的生态链
现在有一波互联网先驱在致力于改进json规范,可以通过扩展加强呗,不一定改变现有的东西。wenshao的fastjson里,就做了点简单的加强。比如可以用开关 把 Long(10)序列化成 10L
jsonp是ajax跨域的技术 我们知道ajax不能跨域调用,但是js引用可以。jsonp就是利用

我们说说一个rpc的通用结构是啥,然后说说怎么做一个跨平台的rpc
rpc的目标是实现像调用本地方法一样调用远程方法。
调用方 A系统 Service.call() -> 请求-> 网络 -> 服务提供方 B系统 Service.call() -> 响应 -> 网络 -> A系统
所以,rpc需要在A系统,也就是调用方生成一个跟实际业务方法一样的桩。
这个叫stub
就是Service.call这个方法,但是这个方法在A系统是没有业务实现代码的。
就是个占位符,所以叫桩。
A系统里调用这个Service.call的时候,rpc框架需要直接能接管这个调用方法得的处理,
也就是利用像java里的动态代理或AOP的技术,拦截方法调用,然后自己实现后续的动作。.net里叫远程代理(Remote Proxy)和emit技术。flex里可以直接使用动态属性来搞。好了,我们现在可以拦截到这个方法调用的处理了
我们的rpc框架把请求的方法签名(就是什么类、什么方法、什么参数,请求和响应的参数类型等等信息)封装成一个请求对象。然后通过网络发送给系统B,也就是业务的实现方。
B这边有个通用的接收处理的地方,比如http的rpc的话这里是一般是个servlet,pb之类走tcp的话这里是一个自定义的监听处理程序,对B系统所有不同的Servicexxx的调用都会网络访问这个地方。
我们一般把这个叫做 skeleton
可以叫骨架,有时候我们也叫服务器端的桩。
也就是这个serlvet是个通用的?是丫。servlet,filter的无所谓。
这个skeleton拿到请求对象,拆解开,然后就按照一定的规则在自己的系统内找Service的实际业务实现类了。
找到了,就匹配有没有call方法,参数类型和返回值类型是否一样
这些叫方法签名,如果签名一样,就直接调用方法,把返回值或是错误信息封装到一个响应对象,
然后通过网络返回给系统A,拿soap来说,返回的报文要么是result,要么是fault
A系统的rpc框架拿到响应对象以后,从中分析是返回值还是错误,
是返回值就直接给方法调用者,是错误就异常
所以整个过程对于系统A里调用Service.call的代码来说,跟调用本地方法是一样的。虽然背后走了很复杂的一趟漂洋过海。

也就是利用像java里的动态代理或AOP的技术,
拦截方法调用,然后自己实现后续的动作。
.net里叫远程代理(Remote Proxy)和emit技术
.net里可以用spring.net,castle等框架来做AOP
也可以直接自己写个remote proxy类就能远程访问。
但是性能不如AOP。

spring是不是支持rpc网络调用?
配置个httpinvoker就可以了。
就是走http访问另外系统的bean,
spring把细节都屏蔽了。
也可以简单配置下,就能用cxf/xfire之类的把spring的bean暴露成webservice

https://code.google.com/p/rpcfx/
再说下跨平台的rpc,这里有个我当年学rpc时自己做的个demo,实现了java和.net的相互调用,client端也可以用js,,,还有一个副产品就是这个项目里自己实现了xml、json的序列化。
需要*。

我们知道,每个平台运行时自己内部表示对象的数据结构都是不同的。
所以,java里一个对象直接用内置的api序列化到文件或byte[]丢给.net肯定是不认的。
但是两个java系统间相互调用,就简单的多,可以用上面的办法。
怎么做一个通用的跨平台rpc呢?
关键是,要为交互定制一套平台无关的中间类型。
然后在两头把不同语言的数据对象都转换成这一套标准的数据类型。
就像你说中文,你老师说俄语,直接说母语你们无法交流,同时说英语就好了。
有个奇葩的东西,blazeds不是这么搞得。它在java端搞了个flash数据结构的编解码器,可以把java对象直接转换成flash用得amf数据格式。所以,它只能跨指定的平台java服务端+flex客户端。

所以,大家可以看到webservice里,需要用xsd定义一套中间类型(xsd:int xsd:string之类的),然后用嵌套可以表示复杂对象。protocol buffer、thrift、avro,都是自己定义了一套二进制的数据类型表示方式。然后调用方和提供方,都用这套类型做编解码,即可。
当然这些有很多优势,也有缺点。
优势是,自定义的格式,可以使得数据量更小,编解码更快。
webservice最大的问题就是,xml文本的,冗余多,编解码效率低。
xml处理领域有个神器,效率很nb,api非常不友好,有兴趣的可以去研究下(VTD-XML)貌似也需要*。支持namespace和xpath哦
标准一旦定义好了,就可以搞生态链里的工具集合了。
所以我们看到webservice里有各种生成stub的工具,wsdl2java之类的,thrift -gen java xxx 之类的,用起来就非常方便了。
大家只需要学习接口怎么描述,其他的都可以用工具生成代码了。其他的都可以用工具生成代码了。
所有的细节对rpc使用者都是透明的,大家不需要考虑上面讲到的那些细节就可以实现rpc功能了。
over

rpc就是个同步调用,对于业务处理的超时和网络超时它没有什么办法
挺多就是就是retry的策略。
这样又会带来额外的复杂度。
比如,业务方处理成功,返回的响应超时了;和调用业务方,请求就超时了。这是两个决然不同的场景。
rpc的业务,最好做成幂等的。就是多次调用,结果一样。
如果预期这个业务处理就非常非常慢。。。直接涉及成异步的就好。比如用mq,或者请求和响应分开来做。

每个请求一个thread,开销得多大啊,有线程池的吧
看下线程池的参数设置
客户端有超时,服务器端有超时。
客户端超时保障业务能正常处理,服务器保障系统安全。
系统健壮。
服务端超时的话,应当考虑适当的中断执行线程么?应该考虑。
rpc是同步直连,不终止掉一直卡那儿。
主要是突发情况下的SERVER繁忙,等等其他原因导致的超时。
哦,这样啊,你可以考虑做流控,做异步开关,做服务降级

异步开关:系统里做两个处理逻辑,监控系统里做个按钮,一按这个按钮,就切换成异步处理,再按这个钮,就切换成了同步处理。很简单的。
就是业务层代码本身就要支持这种转换,是这个意思吧
比如之前return的是一个明确的结果
现在return的是一个类似future的结果
大概是这个意思,具体用future还是两步处理,随便
超时了future也没办法。
还是建议拆解掉请求和响应。
两个办法
1、分两次请求,第一次发处理请求,然后直接返回一个标识token不管。过一定时间,再请求一次,拿这个token去要结果。
2、如果两个喜欢相互调用,就简单了。A调用B请求处理,B处理完了,调用A的回调函数。
无所谓超时不超时,哪怕今天请求的,明天回调呢。

K神吹水系列之RPC