一、概述
Apache Dubbo™ (incubating)是一款高性能Java RPC框架。官网:http://dubbo.apache.org/zh-cn/index.html
DUBBO是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,是阿里巴巴SOA服务化治理方案的核心框架
1.1 架构

| 节点 | 角色说明 |
|---|---|
| Provider | 暴露服务的服务提供方 |
| Consumer | 调用远程服务的服务消费方 |
| Registry | 服务注册与发现的注册中心 |
| Monitor | 统计服务的调用次数和调用时间的监控中心 |
| Container | 服务运行容器 |
调用关系说明
- 服务容器负责启动,加载,运行服务提供者。
- 服务提供者在启动时,向注册中心注册自己提供的服务。
- 服务消费者在启动时,向注册中心订阅自己所需的服务。
- 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
- 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另 一台调用。
- 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。 Dubbo 架构具有以下几个特点,分别是连通性、健壮性、伸缩性、以及向未来架构的升级性。
1.2 特点
连通性、健壮性、伸缩性、以及向未来架构的升级性。
连通性
l 注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小
l 监控中心负责统计各服务调用次数,调用时间等,统计先在内存汇总后每分钟一次发送到监控中心服务器,并以报表展示
l 服务提供者向注册中心注册其提供的服务,并汇报调用时间到监控中心,此时间不包含网络开销
l 服务消费者向注册中心获取服务提供者地址列表,并根据负载算法直接调用提供者,同时汇报调用时间到监控中心,此时间包含网络开销
l 注册中心,服务提供者,服务消费者三者之间均为长连接,监控中心除外
l 注册中心通过长连接感知服务提供者的存在,服务提供者宕机,注册中心将立即推送事件通知消费者
l 注册中心和监控中心全部宕机,不影响已运行的提供者和消费者,消费者在本地缓存了提供者列表
注册中心和监控中心都是可选的,服务消费者可以直连服务提供者
健壮性
l 监控中心宕掉不影响使用,只是丢失部分采样数据
l 数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务
l 注册中心对等集群,任意一台宕掉后,将自动切换到另一台
l 注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯
l 服务提供者无状态,任意一台宕掉后,不影响使用
l 服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复
伸缩性
l 注册中心为对等集群,可动态增加机器部署实例,所有客户端将自动发现新的注册中心
l 服务提供者无状态,可动态增加机器部署实例,注册中心将推送新的服务提供者信息给消费者.
升级性
1.3 注册中心
dubbo支持多种注册中心,推荐使用ZooKeeper。(l Multicast 注册中心,l Redis注册中心**,l Simple注册中心**)
Zookeeper 是 Apacahe Hadoop 的子项目,是一个树型的目录服务,支持变更推送,适合作为 Dubbo 服务的注册中心,工业强度较高,可用于生产环境,并推荐使用。

流程说明:
- 服务提供者启动时: 向 /dubbo/com.foo.BarService/providers 目录下写入自己的 URL 地址
- 服务消费者启动时: 订阅 /dubbo/com.foo.BarService/providers 目录下的提供者 URL 地址。并向/dubbo/com.foo.BarService/consumers 目录下
- 写入自己的 URL 地址 监控中心启动时: 订阅 /dubbo/com.foo.BarService 目录下的所有提供者和消费者 URL 地址。
支持以下功能:
- 当提供者出现断电等异常停机时,注册中心能自动删除提供者信息
- 当注册中心重启时,能自动恢复注册数据,以及订阅请求
- 当会话过期时,能自动恢复注册数据,以及订阅请求
- 当设置 <dubbo:registry check="false" /> 时,记录失败注册和订阅请求,后台定时重试
- 可通过 <dubbo:registry username="admin" password="1234" /> 设置 zookeeper 登录信息
- 可通过 <dubbo:registry group="dubbo" /> 设置 zookeeper 的根节点,不设置将使用无根树
- 支持 * 号通配符 <dubbo:reference group="" version="" /> ,可订阅服务的所有分组和所有版本的提供者
1.4 负载均衡
在集群负载均衡时,Dubbo 提供了多种均衡策略,缺省为 random 随机调用。可通过消费端的@Reference注解中的loadbalance属性配置。
1.5 dubbo源码分层
| 分层名称 | 作用 |
|---|---|
| config 配置层 | 对外配置接口,以 ServiceConfig, ReferenceConfig 为中心,可以直接初始化配置类,也可以通过 spring 解析配置生成配置类 |
| proxy 服务代理层 | 服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton, 以ServiceProxy 为中心,扩展接口为 ProxyFactory |
| registry 注册中心层 | 封装服务地址的注册与发现,以服务 URL 为中心,扩展接口为 RegistryFactory,Registry, RegistryService |
| cluster 路由层 | 封装多个提供者的路由及负载均衡,并桥接注册中心,以 Invoker 为中心,扩展接口为 Cluster, Directory, Router, LoadBalance |
| monitor 监控层 | RPC 调用次数和调用时间监控,以 Statistics 为中心,扩展接口为 MonitorFactory,Monitor, MonitorService |
| protocol 远程调用层 | 封装 RPC 调用,以 Invocation, Result 为中心,扩展接口为 Protocol, Invoker,Exporter |
| exchange 信息交换层 | 封装请求响应模式,同步转异步,以 Request, Response 为中心,扩展接口为 Exchanger, ExchangeChannel, ExchangeClient, ExchangeServer |
| transport 网络传输层 | 抽象 mina 和 netty 为统一接口,以 Message 为中心,扩展接口为 Channel,Transporter, Client, Server, Codec |
| serialize 数据序列化层 | 可复用的一些工具,扩展接口为 Serialization, ObjectInput, ObjectOutput,ThreadPool |
二、集成示例
2.1 springboot集成示例
提供者
依赖
<!--添加SpringBoot测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--添加dubbo的springboot依赖-->
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>0.2.0</version>
</dependency>
<!--添加dubbo依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.4</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.13</version>
</dependency>
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>0.1</version>
</dependency>service接口
@Service(version = "${dubbo.service.version}") //声明这是一个dubbo服务 dubbo提供的注解@Service,而不是Spring的
public class UserServiceImpl implements UserService {
/**
* 实现查询,这里做模拟实现,不做具体的数据库查询
*/
public List<User> queryAll() {
List<User> list = new ArrayList<User>();
for (int i = 0; i < 10; i++) {
User user = new User();
user.setAge(10 + i);
user.setId(Long.valueOf(i + 1));
user.setPassword("123456");
user.setUsername("username_" + i);
list.add(user);
}
return list;
}
}配置
# Spring boot application
<NolebasePageProperties />
spring.application.name = itcast-dubbo-service
server.port = 9090
# Service version
dubbo.service.version = 1.0.0
# 服务的扫描包
dubbo.scan.basePackages = cn.itcast.dubbo.service
# 应用名称
dubbo.application.name = dubbo-provider-demo
# 协议以及端口
dubbo.protocol.name = dubbo
dubbo.protocol.port = 20880
# zk注册中心
dubbo.registry.address = zookeeper://172.16.55.185:2181
dubbo.registry.client = zkclient
spring.application.name=dubbo-demo-provider
spring.dubbo.application.id=dubbo-demo-provider
spring.dubbo.application.name=dubbo-demo-provider
spring.dubbo.registry.address=zookeeper://192.168.25.140:2181;zookeeper://192.168.25.140:2182;zookeeper://192.168.25.140:2183
spring.dubbo.server=true
spring.dubbo.protocol.name=dubbo
spring.dubbo.protocol.port=20880启动类
// 需添加@EnableDubboConfiguration注解
new SpringApplicationBuilder(DubboProvider.class)
.web(WebApplicationType.NONE) // 非 Web 应用
.run(args);消费者
依赖
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.13</version>
</dependency>
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>0.1</version>
</dependency>
<!--引入service的依赖-->
<dependency>
<groupId>cn.itcast.dubbo</groupId>
<artifactId>itcast-dubbo-service</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestUserService {
// loadbalance 负载均衡策略
@Reference(version = "1.0.0" , loadbalance = "roundrobin")
private UserService userService;
@Test
public void testQueryAll(){
List<User> users = this.userService.queryAll();
for (User user : users) {
System.out.println(user);
}
}
}配置
# Spring boot application
spring.application.name = itcast-dubbo-consumer
server.port = 9091
# 应用名称
dubbo.application.name = dubbo-consumer-demo
spring.dubbo.application.id=dubbo-demo-consumer
# zk注册中心
dubbo.registry.address = zookeeper://172.16.55.185:2181
dubbo.registry.client = zkclient三、dobbo特性
3.1 地址缓存
dubbo服务消费者在第一次调用时,会将服务提供方地址缓存到本地,以后在调用则不会访问注册中心。
当服务提供者地址发生变化时,注册中心会通知服务消费者。
3.2 超时与重试
dubbo 利用超时机制来解决大量请求阻塞的问题,•设置一个超时时间,在这个时间段内,无法完成服务访问,则自动断开连接。
使用timeout属性配置超时时间,默认值1000,单位毫秒。
如果出现网络抖动,则这一次请求就会失败。
Dubbo 提供重试机制来避免网络抖动使请求失败的问题。
通过 retries 属性来设置重试次数。默认为 2 次。
3.3 多版本
灰度发布:当出现新功能时,会让一部分用户先使用新功能,用户反馈没问题时,再将所有用户迁移到新功能。
dubbo 中使用version 属性来设置和调用同一个接口的不同版本
3.4 负载均衡
负载均衡策略(4种) :
Random :按权重随机,默认值。按权重设置随机概率。
RoundRobin :按权重轮询
LeastActive:最少活跃调用数,相同活跃数的随机。
ConsistentHash:一致性 Hash,相同参数的请求总是发到同一提供者。
3.5 集群容错
服务路由
服务路由包含一条路由规则,路由规则决定了服务消费者的调用目标,即规定了服务消费者可调用哪些服务提供者,dubbo提供三种服务路由实现,分别为条件路由ConditionRouter、脚本路由ScriptRouter、标签路由TagRouter
条件路由
条件路由规则的格式:[服务消费者匹配条件] => [服务提供者匹配条件]
host = 10.20.153.10 => host = 10.20.153.11
该条规则表示 IP 为 10.20.153.10 的服务消费者只可调用 IP 为 10.20.153.11 机器上的服务,不可调用其他机器上的服务。
如果服务消费者匹配条件为空,表示不对服务消费者进行限制。如果服务提供者匹配条件为空,表示对某些服务消费者禁用服务
常见路由配置
| 配置类型 | 配置示例 |
|---|---|
| 白名单 | host!=10.20.153.10,10.20.153.11=> |
| 黑名单 | host=10.20.153.10,10.20.153.11=> |
| 读写分离 | method=find,list,get,is=>host=172.22.3.94,172.22.3.95,172.22.3.96 method!=find,list,get,is=>host=172.22.3.97,172.22.3.98 |
| 前后台分离 | application=front=>host=172.22.3.91,172.22.3.92,172.22.3.93 application!=front=>host=172.22.3.94,172.22.3.95,172.22.3.96 |
集群容错模式
| 模式名称 | 描述 |
|---|---|
| Failover Cluster | 失败重试。默认值。当出现失败,重试其它服务器 ,默认重试2次,使用 retries 配置。一般用于读操作 |
| Failfast Cluster | 快速失败,只发起一次调用,失败立即报错。通常用于写操作 |
| Failsafe Cluster | 失败安全,出现异常时,直接忽略。返回一个空结果。 |
| Failback Cluster | 失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。 |
| Forking Cluster | 并行调用多个服务器,只要一个成功即返回。 |
| Broadcast Cluster | 广播调用所有提供者,逐个调用,任意一台报错则报错 |
3.6 服务降级
使用dubbo在进行服务调用时,可能由于各种原因(服务器宕机/网络超时/并发数太高等),调用中就会出现RpcException,调用失败。
可以通过服务降级功能临时屏蔽某个出错的非关键服务,并定义降级后的返回策略
服务降级就是指在由于非业务异常导致的服务不可用时,可以返回默认值,避免异常影响主业务的处理。
可以向注册中心写入动态配置覆盖规则:
mock=force:return+null 表示消费方对该服务的方法调用都直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。
还可以改为 mock=fail:return+null 表示消费方对该服务的方法调用在失败后,再返回null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。
mock配置方式
dubbo官方文档上使用一个mock配置,实现服务降级。mock只在出现非业务异常(比如超时,网络异常等)时执行。mock的配置支持两种,一种为boolean值,默认的为false。如果配置为true,则缺省使用mock类名,即类名+Mock后缀;另外一种则是配置”return null”,可以很简单的忽略掉异常。
mock配置在调用方,服务降级不需要对服务方配置产生修改。下面举个例子说明,mock的配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
<dubbo:application name="dubbo-consumer" />
<dubbo:registry address="zookeeper://127.0.0.1:2181" />
<!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
<dubbo:reference id="fooService" interface="com.test.service.FooService" timeout="10000" check="false" mock="return null">
</dubbo:reference>
</beans>测试在调用端调用服务两个方法,当服务端正常启动时,程序获得正常返回值;当服务提供方没有启动(模拟服务不可用状态),调用方依然正常运行,调用doSomething2获取返回值时null。
mock实现接口方式
mock配置方式,在服务降级时会对service中的所有方法做统一处理,即都返回null。但是有的时候需要一些方法在服务不可用时返回一些其他信息,以便做其他处理。如更新/删除等。
配置mock="true" ,同时实现mock接口,类名要注意命名规范:接口名+Mock后缀。此时如果调用失败会调用Mock实现。mock实现需要保证有无参的构造方法。
整合hystrix
Hystrix 旨在通过控制那些访问远程系统、服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。Hystrix具备拥有回退机制和断路器功能的线程和信号隔离,请求缓存和请求打包,以及监控和配置等功能
1、springboot官方提供了对hystrix的集成,直接在pom.xml里加入依赖
2、在主程序类上增加注解@EnableHystrix来开启hystrix功能
3、在服务提供者Provider上增加@HystrixCommand注解,这样调用就会经过Hystrix代理
4、在服务消费者增加@HystrixCommand(fallbackMethod= methodName))注解并指定出错时的回调方法。当调用出错时,会调用fallbackMethod = "hello"里面的指定的hello方法
3.7 dubbo线程IO模型
BIO
每个客户端连接过来后,服务端都会启动一个线程去处理该客户端的请求。阻塞I/O每个连接创建成功之后都需要一个线程来维护。
NIO
NIO,也叫做new-IO或者non-blocking-IO,可理解为非阻塞IO。NIO编程模型中,新来一个连接不再创建一个新的线程,而是可以把这条连接直接绑定到某个固定的线程,然后这条连接所有的读写都由这个线程来负责
IO模型中,一个连接都会创建一个线程,对应一个while死循环,死循环的目的就是不断监测这条连接上是否有数据可以读。但是在大多数情况下,1万个连接里面同一时刻只有少量的连接有数据可读,因此,很多个while死循环都白白浪费掉了,因为没有数据。 而在NIO模型中,可以把这么多的while死循环变成一个死循环,这个死循环由一个线程控制。这就是NIO模型中选择器(Selector)的作用,一条连接来了之后,现在不创建一个while死循环去监听是否有数据可读了,而是直接把这条连接注册到选择器上,通过检查这个选择器,就可以批量监测出有数据可读的连接,进而读取数据。
NIO模型解决线程资源受限的方案,实际开发过程中,我们会开多个线程,每个线程都管理着一批连接,相对于IO模型中一个线程管理一条连接,消耗的线程资源大幅减少。
四、SPI
SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。正因此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能。SPI 机制在第三方框架中也有所应用,比如 Dubbo 就是通过 SPI 机制加载所有的组件。不过,Dubbo 并未使用 Java 原生的SPI 机制,而是对其进行了增强,使其能够更好的满足需求。在 Dubbo 中,SPI 是一个非常重要的模块。
java原生spi
在resources中新建/META-INF/services目录,在新建的目录中新建文件,注意文件名必须是接口的全类名,文件中写入接口的实现类,每一个实现类占一行,即可使起加载类。
dubbo SPI机制
Dubbo 重新实现了一套功能更强的 SPI 机制,Dubbo SPI 的相关逻辑被封装在了 ExtensionLoader 类中,通过 ExtensionLoader,我们可以加载指定的实现类。Dubbo SPI 所需的配置文件需放置在META-INF/dubbo 路径下,配置内容如下
college=cn.itheima.api.impl.Czxy
shortTrain=cn.itheima.api.impl.Itheima与 Java SPI 实现类配置不同,Dubbo SPI 是通过键值对的方式进行配置,这样我们可以按需加载指定的实现类。另外,在测试 Dubbo SPI 时,需要在 Robot 接口上标注 @SPI 注解。
五、安装
5.1 dubbo-admin安装
dubbo-admin 是一个前后端分离的项目。前端使用vue,后端使用springboot,安装 dubbo-admin 其实就是部署该项目。我们将dubbo-admin安装到开发环境上。要保证开发环境有jdk,maven,nodejs
安装node
(如果当前机器已经安装请忽略)
因为前端工程是用vue开发的,所以需要安装node.js,node.js中自带了npm,后面我们会通过npm启动
下载地址
https://nodejs.org/en/Dubbo-Admin
进入github,搜索dubbo-admin
https://github.com/apache/dubbo-admin解压后修改配置文件
properties# application.properties # centers in dubbo2.7 # 修改zookeeper地址 # 注册中心 admin.registry.address=zookeeper://192.168.149.135:2181 # 配置中心 admin.config-center=zookeeper://192.168.149.135:2181 # 元数据中心 admin.metadata-report.address=zookeeper://192.168.149.135:2181打包
在 dubbo-admin-develop 目录执行打包命令
mvn clean package启动
sh# 在dubbo-Admin-develop\dubbo-admin-distribution\target>目录 java -jar .\dubbo-admin-0.1.jar # dubbo-admin-ui目录 npm run dev访问
html# 用户名密码都是root http://localhost:8081/