Apache Dubbo 是一款微服务开发框架,它提供了 RPC通信 与 微服务治理 两大关键能力。这意味着,使用 Dubbo 开发的微服务,将具备相互之间的远程发现与通信能力, 同时利用 Dubbo 提供的丰富服务治理能力,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。同时 Dubbo 是高度可扩展的,用户几乎可以在任意功能点去定制自己的实现,以改变框架的默认行为来满足自己的业务需求。

1.简介

Apache Dubbo 是一款微服务开发框架,它提供了 RPC通信 与 微服务治理 两大关键能力。这意味着,使用 Dubbo 开发的微服务,将具备相互之间的远程发现与通信能力, 同时利用 Dubbo 提供的丰富服务治理能力,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。同时 Dubbo 是高度可扩展的,用户几乎可以在任意功能点去定制自己的实现,以改变框架的默认行为来满足自己的业务需求。

Dubbo3 基于 Dubbo2 演进而来,在保持原有核心功能特性的同时, Dubbo3 在易用性、超大规模微服务实践、云原生基础设施适配、安全设计等几大方向上进行了全面升级。 以下文档都将基于 Dubbo3 展开。

2.ZooKeeper

安装 ZooKeeper

https://archive.apache.org/dist/zookeeper/stable/ 下载 ZooKeeper,目前的最新版是 3.5.6。

把 apache-zookeeper-3.5.6-bin.tar.gz 解压到一个本地目录 (目录名最好不要包含空格和中文)。我使用 /usr/local 目录。

1
tar -zxvf apache-zookeeper-3.8.4-bin.tar.gz

把 conf 目录下的 zoo_sample.cfg 重命名为 zoo.cfg,然后修改配置。

cp zoo_sample.cfg zoo.cfg

mkdir zoodata

1
2
3
4
5
6
7
8
9
10
# 心跳检查的时间 2秒
tickTime=2000dd
# 初始化时 连接到服务器端的间隔次数,总时间10*2=20秒
initLimit=10
# ZK Leader 和follower 之间通讯的次数,总时间5*2=10秒
syncLimit=5
# 存储内存中数据快照的位置,如果不设置参数,更新事务日志将被存储到默认位置。
dataDir=/root//root/apache-zookeeper-3.8.4-bin/zoodata
# ZK 服务器端的监听端口
clientPort=2181

配置以下环境变量 vim /etc/profile

1
2
export ZOOKEEPER_HOME=/root/apache-zookeeper-3.8.4-bin
export PATH=$PATH:$ZOOKEEPER_HOME/bin:$ZOOKEEPER_HOME/conf

source /etc/profile

启动 Zookeeper

再安装配置完成后,就可以启动 Zookeeper,使用 zkServer.sh start 启动 ZooKeeper 服务:

1
2
3
4
5
[root@wupx apache-zookeeper-3.5.6-bin]# ./zkServer.sh start
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /usr/local/apache-zookeeper-3.5.6-bin/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED

查看状态 ./zkServer.sh status

停止zookeeper ./zkServer.sh stop

3.使用Dubbo

新建Spring和SpringMvc的项目

1.引入依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<properties>
<spring.version>5.1.9.RELEASE</spring.version>
<dubbo.version>2.7.4.1</dubbo.version>
<zookeeper.version>4.0.0</zookeeper.version>
</properties>

<dependencies>
<!--servlet 3.0规范-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.33</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.33</version>
</dependency>

<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>${dubbo.version}</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>${zookeeper.version}</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>${zookeeper.version}</version>
</dependency>
</dependencies>

2.配置文件

在resources目录下创建Soring.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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://dubbo.apache.org/schema/dubbo" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">


<!-- <context:component-scan base-package="com.richu.service"/> -->
<!--项目名-->
<dubbo:application name="dubbo-server"/>
<!--注册中心地址-->
<dubbo:registry address="zookeeper://192.168.3.133:2181"/>
<!--包扫描-->
<dubbo:annotation package="com.richu.service"/>
</beans>

在webapp/WEB-INF/创建web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">

<!-- 配置spring -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:Spring.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>

添加注解

1
2
3
4
5
6
7
@DubboService
public class UserServiceImpl implements UserService {
@Override
public String login(String username, String password) {
return "test login";
}
}

3.SpringBoot使用Dubbo

User类:

1
2
3
4
5
6
7
@Data
public class User implements Serializable {
private static final long serialVersionUID = -9206514891359830486L;
private int id;
private String name;
private String sex;
}

UserService:

1
2
3
public interface UserService {
User getUser(int id);
}

新建两个SpringBoot项目,一个是服务提供者,一个是服务消费者,引入dubbo的核心依赖

这里排除一下zookeeper的依赖,使用自己的zookeeper版本选择自己的依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.4.1</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.0.0</version>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.14</version>
</dependency>

这里的配置都写在application.yaml中,首先是服务提供者:

1
2
3
4
5
6
7
8
dubbo:
application:
name: dubbo-provider
registry:
address: zookeeper://127.0.0.1:2181
protocol:
name: dubbo
port: 20880

服务提供者需要写服务的实现类,这里需要注意@Service注解采用的是dubbo包下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import org.apache.dubbo.config.annotation.Service;
import org.springframework.stereotype.Component;

@Service
@Component
public class UserServiceImpl implements UserService {
@Override
public User getUser(int id) {
User user=new User();
user.setId(id);
user.setName("javayz");
user.setSex("man");
return user;
}
}

接着在启动类上添加一个@EnableDubbo注解即可。

服务的消费者同样是先写一下配置文件:

1
2
3
4
5
dubbo:
application:
name: dubbo-consumer
registry:
address: zookeeper://127.0.0.1:2181

UserController

接着通过@Reference注解将service对象引进来

1
2
3
4
5
6
7
8
9
10
11
12
@RestController
@RequestMapping("/user")
public class UserController {

@Reference
private UserService userService;

@GetMapping("/{id}")
public User getUser(@PathVariable int id) {
return userService.getUser(id);
}
}

http://localhost:8081/user/1

返回结果

1
2
3
4
5
{
"id": 1,
"name": "hello",
"sex": "man"
}

4.安装dubbo-admin

https://github.com/apache/dubbo-admin 下载并解压,需要有maven和node

mvn clean package 打包 或使用mvn clean package -Dmaven.test.skip=true

打包后用java -jar执行 (推荐使用java8或11)

在ui模块 node可以使用ui模块node文件夹下的node.exe

npm run dev 启动前端 http://localhost:38082/

4.Dubbo高级特性

重试和超时

1
@Service(timeout = 5000,retries = 10)

多版本

1
2
3
4
5
6
@Service(timeout = 5000,retries = 10,version = "v2.0")
@Service(timeout = 5000,retries = 10,version = "v1.0")

//注入的时候指定版本
@Reference(version = "v1.0")
private UserService userService;

dubbo负载均衡策略

https://cn.dubbo.apache.org/zh-cn/overview/what/core-features/load-balance/

1
2
3
4
//配置权重
@Service(weight = 100)
@Service(weight = 200)
@Service(weight = 300)

全局配置

Dubbo 框架的默认策略是 random 加权随机负载均衡。如果要调整策略,只需要设置 loadbalance 相应取值即可,每种负载均衡策略取值请参见文档最上方表格。

为所有服务调用指定全局配置:

1
2
3
dubbo:
consumer:
loadbalance: roundrobin

接口级配置

可以为每个服务指定不同的负载均衡策略。

在 provider 端设置,作为 consumer 侧默认值

1
2
@DubboService(loadbalance = "roundrobin")
public class DemoServiceImpl implements DemoService {}

在 consumer 端设置,具备更高优先级

1
2
@DubboReference(loadbalance = "roundrobin")
private DemoService demoService;

方法级配置

也可以指定方法(method)级别的负载均衡策略。

在 Spring Boot 开发模式下,配置 method 级别参数有以下几种方式:

JavaConfig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Configuration
public class DubboConfiguration {
@Bean
public ServiceBean demoService() {
MethodConfig method = new MethodConfig();
method.setName("sayHello");
method.setLoadbalance("roundrobin");

ServiceBean service = new ServiceBean();
service.setInterface(DemoService.class);
service.setRef(new DemoServiceImpl());
service.addMethod(method)
return service;
}
}
@Autowire
private DemoService demoService;

@Configuration
public class DubboConfiguration {
@Bean
public ReferenceBean demoService() {
MethodConfig method = new MethodConfig();
method.setName("sayHello");
method.setLoadbalance("roundrobin");

ReferenceBean<DemoService> reference = new ReferenceBean<>();
reference.setInterface(DemoService.class);
reference.addMethod(method);
return reference;
}
}

dubbo.properties

1
dubbo.reference.org.apache.dubbo.samples.api.DemoService.sayHello.loadbalance=roundrobin

一致性哈希配置

默认采用第一个参数作为哈希 key,如果需要切换参数,可以指定 hash.arguments 属性

1
2
3
4
5
6
7
8
ReferenceConfig<DemoService> referenceConfig = new ReferenceConfig<DemoService>();
// ... init
Map<String, String> parameters = new HashMap<String, String>();
parameters.put("hash.arguments", "1");
parameters.put("sayHello.hash.arguments", "0,1");
referenceConfig.setParameters(parameters);
referenceConfig.setLoadBalance("consistenthash");
referenceConfig.get();

dubbo限流策略配置

限制服务器端并发执行数(服务粒度)

限制 com.richuff.UserService 的每个方法,服务器端并发执行(或占用线程池线程数)不能超过 10 个

1
2
@DubboService(executes=10)
private UserServiceImpl implements UserService{}

限制服务器端并发执行数(方法粒度)

限制 com.richuff.UserServicesayHello 方法,服务器端并发执行(或占用线程池线程数)不能超过 10 个

1
2
@DubboService(executes=10, methods = {@Method(name="sayHello",executes=10)})
private UserServiceImpl implements UserService{}

限制消费端并发调用数(服务粒度)

限制 com.richuff.UserService 的每个方法,每客户端并发执行(或占用连接的请求数)不能超过 10 个

1
2
@DubboReference(actives=10)
private UserService userService;

限制消费端并发调用数(方法粒度)

限制 com.richuff.UserServicesayHello 方法,每客户端并发执行(或占用连接的请求数)不能超过 10 个

1
2
@DubboReference(actives=10, methods = {@Method(name="sayHello",executes=10)})
private UserService userService;

集群容错模式

可以自行扩展集群容错策略,参见:集群扩展

Failover Cluster

失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过 retries="2" 来设置重试次数(不含第一次)。

重试次数配置如下:

1
<dubbo:service retries="2" />

1
<dubbo:reference retries="2" />

1
2
3
<dubbo:reference>
<dubbo:method name="findFoo" retries="2" />
</dubbo:reference>

Failfast Cluster

快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。

Failsafe Cluster

失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。

Failback Cluster

失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。

Forking Cluster

并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数。

Broadcast Cluster

广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息。

现在广播调用中,可以通过 broadcast.fail.percent 配置节点调用失败的比例,当达到这个比例后,BroadcastClusterInvoker 将不再调用其他节点,直接抛出异常。 broadcast.fail.percent 取值在 0~100 范围内。默认情况下当全部调用失败后,才会抛出异常。 broadcast.fail.percent 只是控制的当失败后是否继续调用其他节点,并不改变结果(任意一台报错则报错)。broadcast.fail.percent 参数 在 dubbo2.7.10 及以上版本生效。

Broadcast Cluster 配置 broadcast.fail.percent。

broadcast.fail.percent=20 代表了当 20% 的节点调用失败就抛出异常,不再调用其他节点。

1
@reference(cluster = "broadcast", parameters = {"broadcast.fail.percent", "20"})