?
Dubbo
前言
在介绍Dubbo之前先了解一下基本概念:
Dubbo是一个RPC 框架,RPC ,即Remote Procedure Call (远程过程调用),相对的就是本地过程调用,在分布式架构之前的单体应用架构和垂直应该架构运用的都是本地过程调用。它允许程序调用另外一个地址空间(通常是网络共享的另外一台机器)的过程或函数,并且不用程序员显式编码这个远程调用的细节。
而分布式架构应用与应用之间的远程调用就需要RPC 框架来做,目的就是为了让远程调用像本地调用一样简单。
Dubbo框架有以下部件:
Consumer
即调用远程服务的服务消费方,消费者需要面向接口编程,知道了哪些接口可以调用了,具体实现需要框架提供一个代理类来为接口提供具体实现,让消费者只管调用什么接口,而具体实现的获取由代理类来处理。
消费者还需要提供调用方法名以及方法的参数值。
但是代理类此时还不知道需要调用哪个服务器上的远程方法,此时需要一个注册中心,通过注册中心获取可以调用的远程服务列表。
远程服务器一般都是集群部署,那么调用哪个服务器则需要通过负载均衡来选择一个最合适的服务器来调用。
同时还需要有集群容错机制,因为各种原因,可能远程调用会失败,此时需要容错机制来重试调用,保证远程调用的稳定性。
同时与服务提供方约定好通信协议和序列化格式,方便通信以及数据传输。
Provider
即暴露服务的服务提供方,服务提供方内部实现具体的接口,然后将接口暴露出去,再将服务注册到注册中心,服务消费方调用服务,提供者接收到调用请求后,通过约定好的通信协议来处理该请求,然后做反序列化,完成后,将请求放入线程池中处理,某个线程接收到这个请求然后找到对应的接口实现进行调用,然后将调用结果原路返回。
Registry
即服务注册与发现的注册中心,注册中心负责服务地址的注册与查找,相当于服务目录,服务提供者和消费者只会再启动时与注册中心交互,注册中心不转发请求,压力小。
注册中心还可以集中化处理配置以及动态地将变更通知订阅方。
但是为什么需要注册中心呢?没有注册中心不可以吗?
在没有注册中心,各服务之间的调用关系是这样的:
当服务越来越多时,服务URL配置管理变得非常困难,硬件负载均衡器的单点压力也越来越大,而有了注册中心之后,就可以实现服务的统一管理,并且实现软负载均衡,降低硬件成本,以下为注册中心示意图:
Monitor
即统计服务调用次数和调用时间的监控中心,面对众多服务,精细化的监控和方便的运维是不可或缺的,对后期维护相当重要。
Container
即服务运行的容器。
架构
图中的各个节点充当的角色已经介绍过了,以下是各节点之间调用关系:
Container 服务容器负责启动,加载以及运行Provider 服务提供者Provider 服务提供者启动时,需要将自身暴露出去让远程服务器可以发现,同时向Registry 注册中心注册自己提供的服务Consumer 服务消费者启动时,向Registry 注册中心订阅所需要的服务Registry 注册中心返回服务提供者列表给消费者,同时如果发生变更,注册中心将基于长连接推送实时数据给消费者- 服务消费者需要调用远程服务时,会从提供者的地址列表中,基于负载均衡算法选出一台提供者服务器进行调用,如果调用失败,会基于集群容错策略进行调用重试
- 服务消费者与提供者会在内存中统计调用次数和调用时间,然后通过定时任务将数据发送给
Monitor 监控中心
高可用性
- 监控中心宕机后不会对服务造成影响,只是丢失部分统计数据
- 注册中心集群后,任意一台宕机后,将自动切换到其他注册中心
- 当所有注册中心均宕机后,服务提供者和消费者之间仍然能通过本地记录了彼此信息的缓存进行通讯,但是如果一方产生变更,另外一方无法感知
- 服务提供者无状态,任意一台服务器宕机后不影响使用,会有其他服务提供者提供服务
- 当所有服务提供者宕机后,服务消费者无法正常使用,将进行无限次重连等待服务提供者重新连线恢复
框架设计
大的分层为Business(业务逻辑层)、RPC层和Remoting层。
再细分下来,Dubbo一共有十层架构,作用分别如下:
Service ,业务层,即日常开发中的业务逻辑层Config ,配置层,对外配置接口,以ServiceConfig 和ReferenceConfig 为中心,可以直接初始化配置类,也可以通过Spring解析配置生成配置类Proxy ,服务代理层,服务接口透明代理,生成服务的客户端Stub 和客户端Skeleton ,负责远程调用和返回结果Registry ,注册中心层,封装服务地址的注册与发现,以服务URL为中心,拓展接口为RegistryFactory ,Registry ,RegistryService Cluster 路由和集群容错层,封装了多个提供者的路由、负载均衡以及集群容错,并桥接注册中心,负责通过负载均衡选取调用具体的节点,处理特殊调用请求和负责远程调用失败的容错措施Monitor ,监控层,负责监控统计RPC调用次数和调用时间Portocol ,远程调用层,主要封装RPC远程调用方法Exchange ,信息交换层,用于封装请求响应模型Transport ,网络传输层,抽象化网络传输统一接口,有Mina 和Netty 可供使用Serialize ,序列化层,将数据序列化成二进制流进行传输,也可以反序列化接收数据
服务暴露过程
首先Provider启动,Protocal通过Proxy代理将需要暴露的接口封装成Invoker,是一个可执行体,然后通过Exporter包装并发送到注册中心完成注册,至此服务就暴露完成。
服务消费过程
注:上图中蓝色部分为服务消费者,绿色部分为服务提供者。
服务消费者启动时会向注册中心订阅并拉取所需服务提供者的信息,并保存到本地缓存,由此即使所有注册中心宕机后,服务提供者和服务消费者也可以通过本地缓存进行通讯,只是一方出现了信息变更,另一方无法感知,但并不影响服务的进行。
之后整个服务消费流程从图中的Proxy开始,由代理类完成处理,以此到达透明无感知。
ProxyFactory生成一个Proxy代理类,Proxy持有一个Invoker可执行对象,调用invoke 之后需要通过Cluster从Directory中获取所有可调用的远程服务Invoker列表,如果配置了某些路由规则,还需要再过滤一遍Invoker列表。
剩下的Invoker再通过LoadBalance做负载均衡选取一个,还需要再通过Filter进行一些数据统计,之后将这些数据保存下来,定时发送给Monitor。
接下来用Client做数据传输,一般用Netty进行传输。
传输需要通过Codec接口进行协议构造,然后再通过Serialization进行序列化,最后将序列化后的二进制流发送至给对应的服务提供者。
<=分割线=>
服务提供者接收到二进制流后也会进行Codec协议处理,然后进行反序列化(此处的处理与传输之前的处理是呈对称的)后将请求放入线程池中处理,某个线程会根据请求找到对应的Exporter,然后再通过Filter进行层层过滤得到Invoker,最终调用对应的实现类然后将结果原路返回。
以上。
整理不易,如果对你有帮助请留个三连吧!
如有错误或不足欢迎评论指正。
|