在当今微服务盛行的大环境下,设计一套高并发、高可扩展、自动容错和高可用的分布式系统无疑是离不开一套优秀的RPC
框架的。Dubbo
是阿里开源的RPC
框架,在市场上众多RPC
框架中是最受欢迎的,各大公司也都争相拥抱和使用。
Dubbo的架构图
图片来自Dubbo官网,各个角色说明如下:
角色 | 角色说明 |
---|---|
Provider | 暴露服务的服务提供方 |
Container | 服务运行容器 |
Consumer | 调用远程服务的服务消费方 |
Registry | 服务注册与发现的注册中心 |
Monitor | 统计服务的调用次数和调用时间的监控中心 |
Provider | 在启动的时候会向注册中心注册元数据(IP/端口之类的),Consumer在启动的时候会向注册中心订阅(第一次订阅会拉去全部数据)服务方的元数据,注册中心发生数据变化的时候,会推送给订阅的Consumer。在获取到服务方的元数据之后可以进行RPC 调用,在RPC 调用后会向监控中心上报统计信息(比如并发数和调用的接口),开发人员可以在监控中心看到服务的调用情况。 |
Dubbo的分层和核心组件
核心组件如下:
层次明 | 作用 |
---|---|
Service | 业务层。即我们实现的业务代码。 |
config | 配置层。主要围绕ServiceConfig(暴露服务的位置)和ReferenceConfig(引用的服务位置)两个实现类展开,初始化配置信息。可以理解为该层管理了整个Dubbo的配置。 |
proxy | 服务代理层。在Dubbo中,无论生产者还是消费者,框架都会生成一个代理类,整个过程对上层是透明的。当调用一个远程接口时就像是调用本地的接口一样,代理程会自动发起远程调用并返回结果,让业务层对远程调用完全无感。 |
registry | 注册层。负责Dubbo框架的服务注册和发现。当有新的服务加入或者旧的服务下线时,注册中心都会感知并通知给所有的订阅方,整个过程不需要人工参与。 |
cluster | 集群容错层。主要负责远程调用失败时的容错策略(重试失败,快速失败等)。选择具体节点调用时的负载均衡策略(如随机,一致性Hash等),特殊调用路径的路由策略(如某个消费者只会条用某个IP的生产者)。 |
monitor | 控制层。主要负责监控统计调用次数和时间等。 |
protocol | 远程调用层。封装RPC调用具体过程,Protocol是Invoker暴露(发布一个服务让别人可以调用)和引用(应用一个远程服务到本地)的主功能入口,它负责管理Invoker的整个生命周期。Invoker是Dubbo的核心模型,框架中所有的其他模型都向它靠拢,或者转换成它,它代表一个可执行体。允许向他发起invoke调动,它可能是执行一个本地的接口实现,也可能是一个远程调用,还可能是一个集群实现。 |
exchange | 信息交换层。建立Request-Response模型,封装请求响应模式,如把同步请求转换成异步请求。 |
transport | 网络传输层。把网络传输抽象为同一接口,如Mina和Netty虽然接口不一样,但是Dubbo在它们上面又封装了同一接口。用户也可以根据其他扩展接口添加更多的网络传输方式。 |
Serialize | 序列化层。如果数据通过网络进行发送,则需要先做序列化,编程二进制流。序列化层负责管理整个框架网络传输时的序列化/反序列化工作。 |
Dubbo总体调用过程
- 首先是从一个Proxy开始,Proxy持有一个Invoker对象,然后触发invoke调用。
- 在invoke调用过程中,需要使用Cluster,Cluster负责容错,如果调用失败的则重试。Cluster在调用之前会通过Directory获取所有可以调用的远程服务Invoker列表。
- 然后通过负载均衡选取一个可以调用的Invoker。这个Invoker在调用之前会经过一个过滤链,通常为处理上下文、限流、计数等。
- 接下来通过client做数据传输,如常见的Netty Client。在传输之前需要做一些私有协议的构造,此时就会用到Codec接口。构造完成之后,就对数据包做序列化然后传送到服务提供者端。服务提供者收到数据包,也会使用Codec处理协议头及一些半包、粘包等。处理好之后再对完整的数据报文做反序列化处理。
- 随后这个Request被分配到线程池中进行处理。Server会处理这些Request,根据请求找到对应的Exporter(它内部持有了Invoker)。
- 最后得到了接口的真实实现并调用,再原路返回调用结果。
以上就是一个完整的远程调用过程。