rpc,消息中间件算是分布式系统中既基础,又重要的组件。本文总结一下自己对rpc的理解,以及公司要进行rpc技术迁移时需要考虑的问题。
rpc(Remote Procedure Call Protocol)远程过程调用协议,首先rpc只是一个协议,使得客户端调用远程的服务,像调用本地服务一样,而不需要详细了解通讯过程,编解码过程。其中,传输协议,编解码协议的实现,就是rpc具体实现框架要解决的了。
那么一个rpc框架具体应该考虑哪些问题,才能使得调用者完成一次远程过程调用呢?
1,代理
首先,客户端调用者需要对远程服务调用的过程透明,那么在java程序中,使用代理,是一个特别好的选择,我研读了我们公司内部的rpc组件,以及dubbo,他们都是通过代理,实现客户端对远程服务的调用,其中,服务发现,通讯细节,则是在代理内部实现,
我们内部的客户端调用过程比较简单,客户端,调用getService,获取所需服务,由CuratorDiscoveryAvroServiceFactory提供“远程”
service对象,其中CuratorDiscoveryAvroServiceFactory封装了zk的服务发现功能,通过proxy.getInstance()返回远程service服务对象。
其中,我们都知道,每一个Proxy都需要实现一个InvocationHandler,用来对代理对象进行包装,我们内部实现了InvocationHandler的
ServiceInvocationHandler用来封装服务发现,以及请求通讯的细节,我们还提供了静态发现,既写死ip,从来不需要再去zk上发现服务,而是通过配置中的远程服务的相关信息,直接构建service对象,并返回。
dubbo的设计比我们的要复杂的多,dubbo的代理使用Javassist实现,以及协议Protocol的概念,invoker由协议实现类返回,不同的协议实现类返回各自协议实现的invoker,用户调用远端方法时,是invoker发送远端请求,但是invoker发送请求所使用的协议,又是protocol的实现类决定的。 用于在不同情况下,并根据不同策略,选择不同的代理方式,提供服务,但原理都是代理内部实现路由,服务发现,权重等策略,以及通讯细节。稍后进行部分源码解读。
服务发现:
代理如何知道远端服务所在的位置,如何与之取得通信,这就需要服务发现,服务发现算是目前很常用的功能概念了。
我们公司内部,大致是如此的调用链:
通过zk地址,以及所需接口名字,构造serviceProvider最后发现服务,很常规的服务发现流程。
使用服务发现,可以使得调用者与远端服务进行解耦,即便远端服务挂了,或者新增了机器,zk也可实时通知客户端更新远端服务可用列表。
这里顺便多说一句。其实zk的地址,大多都是直接配置在客户端的,客户端发现zk,这也是个服务发现,那么直接写死客户端是不是不太好。
其实这个问题,目前没法解,就像一个先有鸡还是先有蛋的过程。阿里的vipserver是通过安装一个客户端域名发现,来解决这个问题。
通讯
大多rpc通讯协议不使用http,如果使用http协议通讯,那就更像是restapi调用了,系统依赖复杂,调用繁多的情况下,基于tcp层通讯
比基于http层通讯优势大很多,少了每次请求的3次握手,因为tcp可建立长连接,并且http基于7层协议,发包收包都要一层一层解析。
在基于tcp层协议下通讯,可选用bio,nio方式,nio方式优点我就不多说了,查看netty实现原理就知道了。
我们内部的rpc框架,以及dubbo,以及hsf都是基于netty做nio通讯,并用netty做 rpcserver的。
序列化反序列化
序列化反序列化又叫做编解码。通讯过程传输的是二进制流,客户端调用远端服务,发送的传输请求,需要编码为二进制流,才能在网络上传输。我们公司内部使用avro进行编解码。rpc框架也是基于avro的ipc包实现的。
常用的序列化方式有hession,thrift,avro,等。dubbo默认使用hession进行编码
公司内部之前选型rpc框架为avro,由于接下来要切入hsf,所以我对自己的关系服务rpc进行了改造,改造后,客户端只要简单的重启,即可。
不需要做太多代码的修改。
一个正常的rpc服务调用端实现,在保证可正常调用远端rpc服务外,还需要考虑一下几点:
降级,熔断:接口调用量多,错误率频升,为保证核心服务可用,弃用边缘服务,降级策略,熔断策略应在客户端也即调用处使用。
限流:服务预估可承载流量,当线上实际流量过大或剧增时,为了保证服务可用,当弃掉部分请求。
路由:客户端可根据接口,参数等,进行路由,也可优先选择同机房的服务进行路由。
为了迁移,本人先调研了一下HSF是如何实现上述功能的,这样,才能知道如何改造自己的rpc服务。
hsf为阿里内部rpc中间件,本身软负载,限流,服务治理等功能。而hsf的上述功能,都是在配置中心进行配置,而不是在代码中进行实现。
而hsf目前还没有熔断机制。我与阿里的hsf开发负责人进行了咨询,他们正在开发自动熔断功能。
由此,我抽象了rpc的客户端的技术实现。限流,路由功能放在具体rpc技术实现中,熔断功能则放在客户端处。
businessClass调用FansRPCApiService接口,
FansRPCServiceClient本身不做rpc技术细节的事,由FansRPCService的实现类去做。
如果切了其他rpc框架,则新建个FansRPCService实现类,即可。
同时,因为hsf不提供熔断机制。所以我在FansRPCServiceClient处做熔断工作。
熔断工作在client端做,这样及时内部rpc实现方式改变,也不影响。
由于hsf已做了路由,限流等工作,所以此部分工作由FansRPCService实现类做。
未完待续............