### Dubbo 简介
Apache Dubbo 是一款微服务开发框架,它提供了 RPC通信 与 微服务治理 两大关键能力。这意味着,使用 Dubbo 开发的微服务,将具备相互之间的远程发现与通信能力, 同时利用 Dubbo 提供的丰富服务治理能力,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。同时 Dubbo 是高度可扩展的,用户几乎可以在任意功能点去定制自己的实现,以改变框架的默认行为来满足自己的业务需求。
作为一个分布式服务框架,以及SOA治理方案,Dubbo其功能主要包括:高性能NIO通讯及多协议集成,服务动态寻址与路由,软负载均衡与容错,依赖分析与服务降级等。Dubbo最大的特点是按照分层的方式来架构,使用这种方式可以使各个层之间解耦合(或者最大限度地松耦合)。从服务模型的角度来看,Dubbo采用的是一种非常简单的模型,要么是提供方提供服务,要么是消费方消费服务,所以基于这一点可以抽象出服务提供方(Provider)和服务消费方(Consumer)两个角色。
### Dubbo历史

* 诞生阿里
从2012年10月23日Dubbo2.5.3发布后,在Dubbo开源将满一周年之际,阿里基本停止了对Dubbo的主要升级。只在之后的2013年和2014年更新过2次对Dubbo2.4的维护版本,然后停止了所有维护工作。Dubbo对Srping的支持也停留在了Spring 2.5.6版本上。
* 当当续命
阿里停止维护和升级Dubbo期间,当当网开始维护自己的Dubbo分支版本Dubbox,支持了新版本的Spring,并对外开源了Dubbox。同时,网易考拉也维护了自己的独立分支Dubbok,可惜并未对外开源。
* 重启登顶apache
在2017年9月7日,阿里发布了Dubbo的2.5.4版本,距离上一个版本2.5.3发布已经接近快5年时间了。在随后的几个月中,阿里Dubbo开发团队以差不多每月一版本的速度开始快速升级迭代,修补了Dubbo老版本多年来存在的诸多bug,并对Spring等组件的支持进行了全面升级。并于2018年进入apache孵化,2019年成为apache顶级项目,同时也发布了dubbo.js,dubbo-go等多语言dubbo版本,2020年发布3.0往云原生项目发展的战略计划
### 服务架构

`服务提供者(Provider)`:暴露服务的服务提供方,服务提供者在启动时,向注册中心注册自己提供的服务。
`服务消费者(Consumer)`: 调用远程服务的服务消费方,服务消费者在启动时,向注册中心订阅自己所需的服务,服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
`注册中心(Registry)`:注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者
`监控中心(Monitor)`:服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心
* 调用关系说明
1.服务容器负责启动,加载,运行服务提供者。
2.服务提供者在启动时,向注册中心注册自己提供的服务。
3.服务消费者在启动时,向注册中心订阅自己所需的服务。
4.注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
5.服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
6.服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
### 服务发现
服务发现,即消费端自动发现服务地址列表的能力,是微服务框架需要具备的关键能力,借助于自动化的服务发现,微服务之间可以在无需感知对端部署位置与 IP 地址的情况下实现通信。
实现服务发现的方式有很多种,Dubbo 提供的是一种 Client-Based 的服务发现机制,通常还需要部署额外的第三方注册中心组件来协调服务发现过程,如常用的 Nacos、Consul、Zookeeper 等,Dubbo 自身也提供了对多种注册中心组件的对接,用户可以灵活选择。官网推荐`Zookeeper`作为注册中心
### 通信协议
Dubbo3 提供了 Triple(Dubbo3)、Dubbo2 协议,这是 Dubbo 框架的原生协议。除此之外,Dubbo3 也对众多第三方协议进行了集成,并将它们纳入 Dubbo 的编程与服务治理体系, 包括 gRPC、Thrift、JsonRPC、Hessian2、REST 等
* Dubbo 协议
Dubbo缺省协议采用单一长连接和NIO异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。Dubbo缺省协议不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低
* Hessian 协议
Hessian协议用于集成Hessian的服务,Hessian底层采用Http通讯,采用Servlet暴露服务,Dubbo缺省内嵌Jetty作为服务器实现基于Hessian的远程调用协议
* HTTP 协议
采用Spring的HttpInvoker实现
* RMI 协议
RMI协议采用JDK标准的java.rmi.*实现,采用阻塞式短连接和JDK标准序列化方式
* Triple 协议
Triple 协议是 Dubbo3 推出的主力协议。Triple 意为第三代,通过 Dubbo1.0/ Dubbo2.0 两代协议的演进,以及云原生带来的技术标准化浪潮,Dubbo3 新协议 Triple 应运而生。
### 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
#### 安装Dubbo-admin

```docker
docker run -it -d --name dubbo-admin \
-v /xxx/xxx/dubbo-admin:/data \
-p 8080:8080 \
-e dubbo.registry.address=zookeeper://ip:port \
-e dubbo.admin.root.password=root \
-e dubbo.admin.guest.password=root \
```
> -e dubbo.registry.address=zookeeper://ip:port 填写自己zookeeper ip和端口号
-e dubbo.admin.root.password 配置控制台root账号 密码
-e dubbo.admin.guest.password 配置控制台guest账号 密码
### SpringBoot整合Dubbo
* docker安装zk
```
docker run --privileged=true -d --name zookeeper --publish 2181:2181 -d zookeeper:latest
```
* user-service-provider 服务提供者
核心依赖
```
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper</artifactId>
<version>3.0.5</version>
<type>pom</type>
<exclusions>
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
<exclusion>
<artifactId>log4j</artifactId>
<groupId>log4j</groupId>
</exclusion>
</exclusions>
</dependency>
```
编写配置文件
```
# 应用名称
spring.application.name=user-service-provider
# Actuator Web 访问端口
management.server.port=8081
management.endpoints.jmx.exposure.include=*
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
# dubbo 协议
dubbo.protocol.id=dubbo
dubbo.protocol.name=dubbo
# dubbo 协议端口( -1 表示自增端口,从 20880 开始)
dubbo.protocol.port=-1
# Dubbo 消费端订阅服务端的应用名,多个服务提供者用逗号分隔
# 这里订阅"自己",会被忽略掉,请根据实际情况添加
dubbo.cloud.subscribed-services=spring-dubbo
dubbo.registry.address=zookeeper://192.168.11.140:2181
# dubbo 服务扫描基准包
dubbo.scan.base-packages=com.jackgreek.springdubbo
# 应用服务 WEB 访问端口
server.port=8081
```
通过`@DubboService`暴露服务
Dubbo3.0之前使用`Service`暴露服务的,之后是使用`@DubboService`
```
@DubboService
@Component
public class lUserServiceImpl implements UserService {
@Override
public List<UserAddress> getUserAddressList(String userId) {
// TODO Auto-generated method stub
UserAddress address1 = new UserAddress(1, "北京市昌平区宏福科技园综合楼3层", "1", "李老师", "010-56253825", "Y");
UserAddress address2 = new UserAddress(2, "深圳市宝安区西部硅谷大厦B座3层(深圳分校)", "1", "王老师", "010-56253825", "N");
// try {
// Thread.sleep(2000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// if(Math.random()>0.5) {
// throw new RuntimeException();
// }
return Arrays.asList(address1,address2);
}
}
```
* order-service-consumer 服务消费者
配置文件
```
server.port=8082
dubbo.application.name=order-service-consumer
dubbo.registry.address=zookeeper://192.168.11.140:2181
dubbo.consumer.check=false
```
通过`@DubboReference`调用 服务接口
```
@DubboReference(loadbalance="random",check = false,timeout=1000)
UserService userService;
@Override
public List<UserAddress> initOrder(String userId) {
// TODO Auto-generated method stub
System.out.println("用户id:"+userId);
//1、查询用户的收货地址
List<UserAddress> addressList = userService.getUserAddressList(userId);
return addressList;
}
```
* 在服务端和消费端的Application类加入`@EnableDubbo`将服务注入zk中。
启动服务端和消费端的启动器,通过Dubbo-admin查看各服务的状态

我们可以通过Dubbo-admin配置某个服务的规则
#### 番外
* `DubboReference`(消费端)与'DubboService'(服务端)注解介绍
```
id
服务引用bean的id,即当前dubbo:reference标签代表的服务的bean的id,其为string类型,必填属性(后续的属性说明中,没有专门指明是必填属性的,均为可选属性)。
interface
服务接口名,类型是class即接口完整类名,属于dubbo:reference标签的必填属性,其作用是用于服务发现。
version
服务版本,与服务提供者的版本一致。对应URL参数为version,类型为string。
group
服务分组,对应URL参数为group,类型为string。当一个接口有多个实现,可以用分组区分,必须和服务提供方一致。
timeout
服务方法调用超时时间,单位毫秒。long类型,对应URL的参数为timeout,缺省使用的timeout,是性能调优属性之一。
retries
远程服务调用重试次数,不包括第一次调用,不需要重试请设为0,缺省使用的retries。int类型的性能调优类属性,对应URL参数retries。
connections
对每个提供者的最大连接数,rmi、http、hessian等短连接协议表示限制连接数,dubbo等长连接协表示建立的长连接个数。int类型的性能调优类属性,URL对应的参数为connections,缺省使用的connections。
loadbalance
负载均衡策略,对应URL中的loadbalance参数,string类型,其取值范围为random,roundrobin,leastactive,分别表示随机,轮询,最少活跃调用。loadbalance是性能调优中非常重要的属性之一,缺省使用的loadbalance。
async
是否异步执行,不可靠异步,只是忽略返回值,不阻塞执行线程。boolean类型的性能调优类属性,URL中对应参数为async,缺省使用的async。
generic
是否缺省泛化接口,如果为泛化接口,将返回GenericService。属于服务治理类的属性,类型为boolean,其对应的URL参数为generic,缺省使用的generic。
check
启动时检查提供者是否存在,boolean类型。设置为true时,如果提供者即服务不存在注册中心,将报错,设置为false时,不做检查。这是一个很有用的服务治理类的属性,在出现服务循环引用时,可以设置为false解决。其对应的URL参数为check。缺省使用的check。
url
点对点直连服务提供者地址,将绕过注册中心。string类型,URL中对应的参数为url。
stub
服务接口客户端本地代理类名,用于在客户端执行本地逻辑,如本地缓存等,该本地代理类的构造函数必须允许传入远程代理对象,构造函数如:public XxxServiceLocal(XxxService xxxService)。其类型为class/boolean,URL中对应的参数为stub。
mock
服务接口调用失败Mock实现类名,该Mock类必须有一个无参构造函数,与stub的区别在于,Local总是被执行,而mock只在出现非业务异常(比如超时,网络异常等)时执行,stub在远程调用之前执行,Mock在远程调用后执行。mock是比较重要的服务治理的属性,其类型为class或boolean,缺省值为false。对应URL的参数是mock。
cache
以调用参数为key,缓存返回结果,其类型为string/boolean,取值范围lru, threadlocal, jcache等。在URL中对应的参数为cache。
validation
是否启用JSR303标准注解验证,如果启用,将对方法参数上的注解进行校验。boolean类型,在URL中对应的参数为validation。
proxy
选择动态代理实现策略,string类型的性能调优类属性,可选取值为jdk和javassist,缺省值为javassist。其在URL的参数为proxy。
client
客户端传输类型设置,如Dubbo协议的netty或mina。string类型的性能调优类属性,其在URL的参数为client。
registry
string类型,从指定注册中心注册获取服务列表,在多个注册中心时使用,值为的id属性,多个注册中心ID用逗号分隔。缺省将从所有注册中心获服务列表后合并结果。
owner
调用服务负责人,用于服务治理,请填写负责人公司邮箱前缀。string类型的属性,对应URL参数为owner。
actives
每服务消费者每服务每方法最大并发调用数。int类型,缺省值为0,对应URL参数为actives,属于性能调优类属性。
cluster
集群方式,string类型的性能调优类属性,可选取值为failover、failfast、failsafe、failback、forking,缺省值为failover。
filter
服务过滤器链,服务消费方远程调用过程拦截器名称,多个名称用逗号分隔。string类型性能调优属性,缺省值为default。其对应的URL参数为reference.filter。
listener
服务消费方引用服务监听器名称,多个名称用逗号分隔。string类型的性能调优类属性,其对应的URL参数为invoker.listener,缺省值为default。
layer
服务调用者所在的分层。如:biz、dao、intl:web、china:acton。string类型的服务治理类属性,其在URL上对应的参数为layer。
init
是否在afterPropertiesSet()时饥饿初始化引用,否则等到有人注入或引用该实例时再初始化。boolean类型的性能调优类属性,缺省值为false,其在URL上对应的参数为init。
protocol
string类型,只调用指定协议的服务提供方,其它协议忽略。其在URL上对应的参数为protocol。
```
* 配置原则
1、作服务的提供者,比服务使用方更清楚服务性能参数,如调用的超时时间,合理的重试次数,等等
2、在Provider配置后,Consumer不配置则会使用Provider的配置值,即Provider配置可以作为Consumer的缺省值。否则,Consumer会使用Consumer端的全局设置,这对于Provider不可控的,并且往往是不合理的
配置的覆盖规则:
1) 方法级配置别优于接口级别,即小Scope优先
2) Consumer端配置 优于 Provider配置 优于 全局配置,
3) 最后是Dubbo Hard Code的配置值([见配置文档](https://dubbo.apache.org/zh/docsv2.7/user/configuration/configuration-load-process/)

Dubbo初体验