rpc(Remote Procedure Call)
概念:rpc(远程过程调用)是一个通信协议,该协议允许运行于一台计算机的程序调用另一台计算机的程序。
角色:
消费方:
客户端(client) 负责发起服务的调用。
客户端存根(client stub) 负责与提供方通信:
1>存放着提供方的地址等信息。
2>将客户端的请求参数打包成网络消息,然后(通过socket)发送给提供方。
3>(通过socket)接收提供方返回的消息,并将消息解包然后将执行的结果返回给客户端。
提供方:
服务器(server) 服务提供者、执行者,将执行的结果返回给服务端存根。
服务端存根(server stub) (通过socket)接收消费方发送过来的消息,将消息解包,并调用服务器上的服务,最后将执行结果打包成消息(通过socket)发送给消费方。
RPC请求调用的流程:
请求:消费方 =======> 提供方
客户端 --> 客户端存根 --> 服务端存根 --> 服务器:执行请求。
响应:提供方 =======> 消费方
服务器 --> 服务端存根 --> 客户端存根 --> 客户端:得到请求的结果。
和http请求的对比:
性能:
http协议传输的报文比较臃肿,rpc传输的报文相对较小,故rpc的传输效率更高一些。
rpc框架一般都支持多种高效的序列化机制,故rpc通信时序列化和反序列化的消耗更小一些。
灵活性:
RPC协议要求服务提供方和服务调用方都需要使用相同的rpc框架。http协议不需要关注语言,通用性更好。
dubbo:
服务调用过程:
同 RPC请求调用的流程 一样。
文档:http://dubbo.apache.org/zh-cn/docs/source_code_guide/service-invoking-process.html
发布服务(服务导出):
ServiceBean实现了spring的ApplicationListener接口,并且监听着spring的上下文刷新事件(ContextRefreshedEvent),当监听到spring的上下文刷新事件时,执行服务导出操作。
文档:http://dubbo.apache.org/zh-cn/docs/source_code_guide/export-service.html
调用服务(服务导入):
文档:http://dubbo.apache.org/zh-cn/docs/source_code_guide/refer-service.html
服务目录(Directory):
文档:http://dubbo.apache.org/zh-cn/docs/source_code_guide/directory.html
概念:服务目录中存储了提供者的相关信息(ip、端口等),消费者可以从服务目录中获取到提供者的信息,然后可以通过Netty等客户端进行远程调用。
说明:服务目录在获取注册中心的服务配置信息后,会为每条配置信息生成一个Invoker对象并添加到一个集合中,这个集合中的元素会随注册中心中服务配置信息的变化而动态调整。
线程模式:
概念:服务端存根将解码后的请求发送给服务器中的分发器(Dispatcher),分发器根据不同的派发策略将请求分发到指定的线程池来执行或直接在IO线程上执行。
说明:Dubbo将底层通信框架中接收请求的线程称为IO线程,如果IO线程被占满,将会导致不能接收新的请求。
原则:
如果一些事件处理的逻辑能迅速完成,比如只在内存打一个标记,则直接在IO线程上执行该段逻辑即可。
如果事件的处理逻辑比较耗时,比如该段逻辑会发起数据库查询或HTTP请求,则将请求派发到线程池中执行,而不是直接在IO线程上执行。
派发策略:
all 所有消息都派发到线程池,包括请求,响应,连接事件,断开事件等,默认策略。
direct 所有消息都不派发到线程池,全部在 IO 线程上直接执行
message 只有请求和响应消息派发到线程池,其它消息均在 IO 线程上执行
execution 只有请求消息派发到线程池,不含响应。其它消息均在 IO 线程上执行
connection IO线程上,将连接断开事件放入队列,有序逐个执行,其它消息派发到线程池
文档:http://dubbo.apache.org/zh-cn/docs/user/demos/thread-model.html
集群容错模式:
文档:http://dubbo.apache.org/zh-cn/docs/user/demos/fault-tolerent-strategy.html
Failover 失败自动切换(即重试),默认选项,默认重试3次。 通常需要服务接口支持幂等性。
Failfast 快速失败,只发起一次调用,失败立即报错。 通常用于非幂等性的写操作,比如新增记录。
Failsafe 失败安全,出现异常时,直接忽略。
Forking 并行调用多个服务器,只要一个成功即返回。 通常用于实时性要求较高的读操作。
Broadcast 广播调用所有提供者,逐个调用,任意一台报错则报错 [2]。通常用于通知所有提供者更新缓存或日志等本地资源信息。
负载均衡:
文档:http://dubbo.apache.org/zh-cn/docs/user/demos/loadbalance.html
Random 随机,默认选项。
RoundRobin 轮询,会出现慢的提供者导致请求堆积的问题。 说明:若某台机器很慢但是并没有挂掉,当请求打到这台机器时请求就会卡住,久而久之,所有请求都卡在这台机器上了。
LeastActive 最小活跃调用数,调用数=两次调用的时间差。使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
ConsistentHash 一致性Hash,相同参数的请求总是发到同一提供者。 说明:当某一台提供者宕机后,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
注册中心挂掉:
消费者本地缓存着提供者的地址列表,故若只是注册中心挂掉,则消费者依然可以访问提供者暴露的服务。
服务宕机:
根据集群容错的策略做相应的处理。
其它:
服务调用是阻塞的吗?默认是阻塞的,也支持异步调用。
服务提供者能实现失效踢出的原理:基于zk临时节点实现的。
RMI(Remote Method Invoke):
概念:RMI是jdk提供的rpc框架,它封装了所有底层通信的细节,并且解决了编组、分布式垃圾收集、安全检查以及并发性等通用问题。
说明:RMI框架要求客户端和服务端都是Java应用。
原理:RMI采用客户端/服务端通信的方式,在服务器上部署提供服务的远程对象,客户端请求访问服务器上远程对象的方法。
详解:
1)RMI框架使用代理来负责客户端与远程对象之间通过Socket进行通信的细节。
2)RMI框架为远程对象分别生成了客户端代理和服务端代理。客户端的代理称为存根(Stub),服务端的代理称为骨架(Skeleton)
3)当客户端调用远程对象的一个方法时,实际上调用的是本地存根对象中相应的方法,存根对象与远程对象具有同样的接口。
4)服务端接收到客户端的请求信息,然后由相应的骨架对象来处理这一请求信息;注:骨架对象虽然起着代理的作用,但是与存根对象不同,骨架对象没有实现远程接口。
说明:
1)存根对象把:被访问的远程对象的名字、被调用的方法的描述、编组后的参数的字节序列等请求信息发送给服务器。
注:存根采用一种与平台无关的编码方式,把方法的参数编码为字节序列,这个编码过程称为参数编组。RMI主要采用java的序列化机制进行参数编组。
2)骨架对象执行以下操作:
反编组参数:把参数的字节序列反编码为参数
定位要访问的远程对象
调用远程对象的相应方法
获取方法调用产生的返回值或者异常,然后对它进行编组
把编组后的返回值或者异常发送给客户。
3)存根对象接收到服务器发送过来的编组后的返回值或异常,通过反编组获取到调用远程方法的返回结果。
4)从java.rmi.server.codebase属性指定的位置动态加载相关的类文件:
1>当客户端试图调用远程对象的方法时,如果在客户端还不存在远程对象所依赖的类文件(如:远程方法的参数和返回值对应的类文件),客户端会从java.rmi.server.codebase系统属性指定的位置去动态加载该类文件。
2>同样,当服务器端访问客户端的远程对象时,如果服务器端不存在相关的类文件,服务器端就会从java.rmi.server.codebase属性指定的位置动态加载相关的类文件。
3>当服务器向rmiregistry注册远程对象时,rmiregistry也会从java.rmi.server.codebase属性指定的位置动态加载相关的远程接口的类文件。
相关概念:
1)把分布在不同节点上的对象之间发送的消息转换为字节序列,这一过程称为编组(marshalling)