目录
1.简介
Apache Dubbo |ˈdʌbəʊ| 是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的RPC远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。官方文档
类似与Spring mvc 是http层的框架,使用http协议进行通信并调用http web服务 @requestMapping暴露的接口,Dubbo可以基于TCP+自定义协议调用服务暴露出来的远程方法。
1.1 背景知识
- 远程调用 : RMI、hassian、webservice、thrift
- 通信交互 : HTTP、mina、netty
- 序列化 : hessian2、 java、json
- 容器 : jetty、spring
- 多线程 : 异步、线程池
- 服务注册发现 : zookeeper、redis
1.2 应用场景
- 作为对内提供服务应用的容器
- 拆分复杂Web应用到服务容器
- 应用负载均衡协调
- 应用服务治理
3.流程
0.start 容器启动 1.register:服务提供者启动-注册 2.subscribe:服务消费者启动-订阅注册中心
3.notify:注册中心通知消费者 有新的服务提供者
4.invoke:RPC远程调用
5.count:监控
3.1服务提供者服务导出
服务提供者配置如下
<beans>
<!-- 服务提供者的应用程序名称,用于跟踪依赖关系 -->
<dubbo:application name="demo-provider"/>
<!-- 利用注册中心导出服务 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<!-- 使用dubbo协议在端口20880上导出服务 -->
<dubbo:protocol name="dubbo" port="20880"/>
<!-- 服务实现,方式与常规bean一样 -->
<bean id="demoService" class="demo.DemoServiceImpl"/>
<!-- 声明要导出的服务接口 -->
<dubbo:service interface="demo.DemoService" ref="demoService"/>
</beans>
首先ServiceConfig类拿到对外提供服务的实际类ref(如上配置:DemoServiceImpl),然后通过ProxyFactory类的getInvoker方法使用ref生成一个AbstractProxyInvoker实例,到这一步就完成具体服务到Invoker的转化。
接下来就是Invoker转换到Exporter的过程,如下图,通过Protocol的export方法导出服务,并注册到注册中心
如DubboProtocol,以上的暴露的服务
url=dubbo://xx.xx.xx.xxx:20880/demo.DemoService?anyhost=true&application=demo-provider&bind.ip=xx.xx.xx.xxx&bind.port=20880&channel.readonly.sent=true&codec=dubbo&dubbo=2.6.2&generic=false&interface=demo.DemoService&methods=sayHello&pid=30185&side=provider×tamp=1575978129988
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
URL url = invoker.getUrl();
// export service.
String key = serviceKey(url);
DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
exporterMap.put(key, exporter);
...
openServer(url);
optimizeSerialization(url);
return exporter;
}
3.2 服务消费者调用服务
首先ReferenceConfig的init方法调用Protocol的refer方法 生成Invoker实例(如上图中的红色部分), 这是服务消费的关键。 接下来把Invoker转换为客户端所需接口(如:DemoService)。 每种协议如RMI/Dubbo/Web service等它们在调用refer方法 生成Invoker实例的细节。 |
核心方法refer、subscribe()
4.源码模块解析
4.1 包组织结构
- dubbo-common 公共逻辑模块:包括 Util 类和通用模型。
- dubbo-remoting 远程通讯模块:相当于 Dubbo 协议的实现,如果 RPC 用 RMI协议则不需要使用此包。
- dubbo-rpc 远程调用模块:抽象各种协议,以及动态代理,只包含一对一的调用,不关心集群的管理。
- dubbo-cluster 集群模块:多个provider包成一个,负载均衡, 容错,路由等,集群地址列表可静态配置,也可由注册中心下发。
- dubbo-registry 注册中心模块:基于注册中心下发地址的集群方式,以及对各种注册中心的抽象。
- dubbo-monitor 监控模块:统计服务调用次数,调用时间的,调用链跟踪的服务。
- dubbo-config 配置模块:是 Dubbo 对外的 API,用户通过 Config 使用Dubbo,隐藏 Dubbo 所有细节。
- dubbo-container 容器模块:是一个 Standlone 的容器,以简单的 Main 加载 Spring 启动,因为服务通常不需要 Tomcat/JBoss 等 Web 容器的特性,没必要用 Web 容器去加载服务。
4.2 层次结构
- Service()服务层:业务实现,包含服务提供者和消费者的实现类和接口类
- Config配置层(dubbo-config): 服务代理层,主要结合SPI机制,动态选取不通的配置类,参见SPI机制
- Registry注册层(dubbo-registry) :负责注册和发现Dubbo服务,以及对Dubbo服务的监听
- Cluster路由层(dubbo-cluster):服务的路由、负载和失败重试策略
- Protocol协议层(dubbo-rpc):协议的转换和过滤
- Exchange信息交换层 (dubbo-remoting)
- Transport传输层(dubbo-remoting):抽象Netty、Mina为统一接口,数据通信传输
- Serialize序列化层(dubbo-common):根据不同协议对数据进行序列化
4.2.1 config配置
负责所有dubbo相关的xml配置和注释配置转换为config对象 ( dubbo.xsd )
api配置的对象类,用于生成对应的register,protocol等对象
核心类有
- ServiceBean(对应provider端的<dubbo:service interface="demo.DemoService" ref="demoService"/>)
- ReferenceBean (consumer端的<dubbo:reference id="demoService" check="false" interface="demo.DemoService"/>)
- ProtocolConfig(provider端的<dubbo:protocol name="dubbo" port="20880"/>)
- RegisterConfig (<dubbo:registry address="zookeeper://127.0.0.1:2181"/>)
4.2.2 *Proxy 服务代理层
负责生成消费者的代理对象,以及服务提供方Provider的Invoker
ProxyFactory接口的2种实现 JdkProxyFactory、JavassistProxyFactory,原理请见动态代理
Proxy层封装了所有接口的透明化代理,而在其它层都以Invoker为中心,只有到了暴露给用户使用时,才用Proxy将Invoker转成接口,或将接口实现转成Invoker,也就是去掉Proxy层RPC是可以Run的,只是不那么透明,不那么看起来像调本地服务一样调远程服务。
4.2.3 Registry注册中心层
负责服务注册与查询服务,以及注册服务的本地缓存<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
支持多种协议注册发现服务,例如redis、zookeeper、Multicast
核心类
接口 RegistryFactory、Registry
AbstractRegistry 以及 ZookeeperRegistry、ZookeeperRegistryFactory
以zk为例
支持以下功能:
流程说明:
- 提供者启动时向/dubbo/com.foo.BarService/providers目录下写入自己的URL地址。
- 消费者启动时订阅/dubbo/com.foo.BarService/providers的提供者URL地址。并向/dubbo/com.foo.BarService/consumers下写入自己的URL地址。
- 监控中心启动时订阅/dubbo/com.foo.BarService目录下的所有提供者和消费者URL地址。
4.2.4 Cluster 路由层
主要负责负载均衡的策略,以及失败策略,缺省设置:RandomLoadBalance,FailoverCluster
支持轮询、随机、最少活跃、一致性哈希负载均衡策略
核心类
接口 LoadBalance、Cluster
RandomLoadBalance以及 RoundRobinLoadBalance等
各节点关系:
Invoker是Provider的一个可调用Service的抽象,Invoker封装了Provider地址及Service接口信息。
Directory代表多个Invoker,可以把它看成List<Invoker>,但与List不同的是,它的值可能是动态变化的,比如注册中心推送变更。
Cluster将Directory中的多个Invoker伪装成一个Invoker,对上层透明,伪装过程包含了容错逻辑,调用失败后,重试另一个。
Router负责从多个Invoker中按路由规则选出子集,比如读写分离,应用隔离等。
LoadBalance负责从多个Invoker中选出具体的一个用于本次调用,选的过程包含了负载均衡算法,调用失败后,需要重选。
4.2.5 Protocol远程调用层
封装RPC调用、支持多种RPC协议,不包含IO通信部分
支持Dubbo(默认)、RMI 、Hessian、Http、WebService、thrift等9种RPC调用方式
接口 Protocol、Exporter、Invoker
DubboProtocol、DubboInvoker、DubboExporter、DubboCodec
- DubboProtocol采用单一长连接和NIO异步通讯,适用于小数据量大并发的服务调用,及consumer cnt >> provider cnt。通过单一连接,保证单一消费者不会压死提供者,长连接,减少连接握手验证等,并使用异步IO,复用线程池,防止C10K问题。
- dubbo协议底层直接使用了Netty框架,在dubbo协议中,分为三种调用方式:同步(默认)、异步和OneWay,同步就是阻塞等拿到被调用方的结果再返回;异步不等待被调用者的处理结果就直接返回,但需要等到被调用者接收到异步请求的应答;OneWay(单向调用)在很多MQ和RPC框架中都有出现,即调用方只负责调用一次,不管被调用方是否接收到该请求,更不会去理会被调用方的任何应答,OneWay一般只会在无需保证调用结果的时候使用。
4.2.6 Exchange信息交换层
主要功能:封装请求响应模式,同步转异步,处理各种协议的通信请求,支持netty、mina、http等,默认采用Netty进行通信
接口 Server、Channel、Client
NettyClient、NettyServer
4.2.7 Serialize数据序列化层
数据序列化层和可复用的一些工具,包括序列化线程池等。dubbo协议缺省为hessian2,rmi协议缺省为java,http协议缺省为json
接口 ThreadPool、Serialization
FixedThreadPool、 Hessian2Serialization