Dubbo + Zookeeper
2022-09-22 22:43:16

Dubbo

[TOC]


本文可能有点长,之后可能会像其他文章一样做个拆分

1. 背景

网站应用的演进:随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进。

image

单一应用架构

当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。

垂直应用架构

当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,提升效率的方法之一是将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键。

分布式服务架构

当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。

流动计算架构

当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键。

2. Zookeeper安装

介绍的是Windows系统的安装。

2.1 下载

Dubbo官网推荐使用Zookeeper来作为注册中心。先来安装一下Zookeeper

zk01

点击完 Download后,选择稳定并且最新的版本下载

zk02

2.2 解压

下载的是一个压缩包,解压后。在该目录下新建一个data文件夹

zk04

然后在conf目录下,将zoo_sample.cfg,复制一份命名为zoo.cfg

zk03

修改zoo.cfgdataDir的内容为刚刚建立的文件夹

1
2
3
4
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
dataDir=../data

2.3 运行

  1. 在解压目录中的bin目录下运行zkServer.cmd,Zookeeper服务
1
E:\apache-zookeeper-3.6.2-bin\apache-zookeeper-3.6.2-bin\bin>zkServer.cmd

需要安装JDK并且环境变量里有JAVA_HOME,不然会提示Java home is not set

  1. 在解压目录中的bin目录下运行zkCli.cmd,Zookeeper客户端

输入点命令测试一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
[zk: localhost:2181(CONNECTED) 0] get /
[zk: localhost:2181(CONNECTED) 1] ls /
[zookeeper]
[zk: localhost:2181(CONNECTED) 2] create -e /hope 123456
Created /hope
[zk: localhost:2181(CONNECTED) 3] ls
ls [-s] [-w] [-R] path
[zk: localhost:2181(CONNECTED) 4] ls /
[hope, zookeeper]
[zk: localhost:2181(CONNECTED) 5] get /hope
123456

3. HelloWorld

编写一个Dubbo的服务调用例子,此处参考了此篇文章

pom

1
2
3
4
5
6
7
8
9
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</dependency>

3.1 服务提供者

3.1.1 提供者 Service

提供的service方法

  • DemoService 接口
  • DemoServiceImpl 实现类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//实现类
package com.md.demo.service;
public class DemoServiceImpl implements DemoService{

public String sayHello(String name) {
return "Welcome to Minbo's Dubbo demo, Hello " + name;
}
}

//接口
package com.md.demo.service;
public interface DemoService {
String sayHello(String name);
}

3.1.2 提供者 Xml

编写src\main\resources\providers.xml文件

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
<?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">

<!-- 配置可参考 http://dubbo.io/User+Guide-zh.htm -->
<!-- 服务提供方应用名,用于计算依赖关系 -->
<dubbo:application name="spring-boot2-dubbo-provider" owner="spring-boot2-dubbo-provider" />

<!-- 定义 zookeeper 注册中心地址及协议 -->
<dubbo:registry protocol="zookeeper" address="localhost:2181" client="zkclient" />

<!-- 定义 Dubbo 协议名称及使用的端口,dubbo 协议缺省端口为 20880,如果配置为 -1 或者没有配置 port,则会分配一个没有被占用的端口 -->
<dubbo:protocol name="dubbo" port="-1" />

<!-- 声明需要暴露的服务接口 -->
<dubbo:service interface="com.md.demo.service.DemoService" ref="demoService" timeout="10000" />

<!-- 和本地 bean 一样实现服务 -->
<bean id="demoService" class="com.md.demo.service.DemoServiceImpl" />

</beans>

3.2 服务消费者

3.2.1 调用提供者的Service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 测试用的 Controller 类;
*/
@Controller
public class TestController {

@Autowired
private DemoService demoService;

/**
* 测试 JSON 接口;
*
* @param name 名字
* @return
*/
@ResponseBody
@RequestMapping("/test")
public JsonResult testJson(String name) {
//调用 服务提供者的方法
String result = this.demoService.sayHello(name);
return new JsonResult(ResultCode.SUCCESS, result);
}
}

3.2.2 消费者 Xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?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">

<!-- 配置可参考 http://dubbo.io/User+Guide-zh.htm -->
<!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
<dubbo:application name="spring-boot2-dubbo-consumer" owner="spring-boot2-dubbo-consumer" />

<!-- 定义 zookeeper 注册中心地址及协议 -->
<dubbo:registry protocol="zookeeper" address="localhost:2181" client="zkclient" />

<!-- 生成远程服务代理,可以和本地 bean 一样使用 demoService -->
<dubbo:reference id="demoService" interface="com.md.demo.service.DemoService" />

</beans>

4. Dubbo-Admin

dubbo-admin 是 dubbo的控制台,具有服务查询、服务治理的功能。(感觉类似Eureka的功能界面)

假如上面的服务提供者还在运行,这里会显示那个服务

dz03

4.1 安装

这里是配合我们单机的Zookeeper来,认识一下Dubbo-admin的界面

4.1.1 下载

​ 在GitHub上直接搜dubbo admin即可,是Apache的项目,一般第一个就是。然后下载,解压

dz01

4.1.2 修改配置

用IDEA打开文件,先修改一些配置

dz02

  1. dubbo-admin-develop\dubbo-admin-server\src\main\resources\application.properties
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# centers in dubbo2.7
#-------- 确你的Zookeeper端口是否是2181(默认是的)---------
admin.registry.address=zookeeper://127.0.0.1:2181
admin.config-center=zookeeper://127.0.0.1:2181
admin.metadata-report.address=zookeeper://127.0.0.1:2181
# Dubbo-Admin控制台的登录账号和密码
admin.root.user.name=root
admin.root.user.password=root
...
...

#compress
server.compression.enabled=true
server.compression.mime-types=text/css,text/javascript,application/javascript
server.compression.min-response-size=10240
#-------增加这句话,来设置dubbo-admin后台的端口是8051
#(默认是8080容易端口占用,设置一个没有被使用的端口即可)
server.port=8051
  1. dubbo-admin-develop\dubbo-admin-ui\vue.config.js
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
const path = require('path');

module.exports = {
outputDir: "target/dist",
lintOnSave: "warning",
devServer: {
//-----这个端口应该是随便的,注意也不要使用被占用的端口就行----
port: 8050,
historyApiFallback: {
rewrites: [
{from: /.*/, to: path.posix.join('/', 'index.html')},
],
},
publicPath: '/',
proxy: {
'/': {
//-----代理目标:注意与application.properties的server.port保持一致----
target: 'http://localhost:8051/',
changeOrigin: true,
pathRewrite: {
'^/': '/'
}
}
}
},

4.2 运行

4.2.1 启动Zookeeper

在上面zookeeper的解压目录中的bin下运行:zkServer.cmd

1
E:\apache-zookeeper-3.6.2-bin\apache-zookeeper-3.6.2-bin\bin>zkServer.cmd

4.2.2 启动Dubbo-Admin

新版的是前后端分离架构的。所以前端后端需要分开来运行

  1. 运行前端:在IDEA的命令行或者本地命令行输入 npm run dev

    1
    2
    #注意是在 dubbo-admin-ui 目录下运行
    E:\dubbo-admin-develop (1)\dubbo-admin-develop\dubbo-admin-ui>npm run dev
  2. 运行后端
    dubbo-admin-develop\dubbo-admin-server\src\main\java\org\apache\dubbo\admin\DubboAdminApplication.java

    1
    2
    3
    4
    5
    6
    7
    #错误结果:
    #1.注意先启动Zookeeper,不然会报错 KeeperErrorCode = ConnectionLoss
    org.apache.curator.CuratorConnectionLossException: KeeperErrorCode = ConnectionLoss
    #2.端口占用 主要端口占用问题

    #正常结果
    Tomcat started on port(s): 8051 (http) with context path ''

4.2.3 访问 http://localhost:8050/

dz04

5. Dubbo-Monitor

参考自:https://blog.csdn.net/yz357823669/article/details/80505642

Dubbo-Monitor 主要用来统计服务的调用次数和调用时间,服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心,监控中心则使用数据绘制图表来显示。
服务消费方和提供方需要显示开启 Monitor。
如果使用 Spring 配置的服务消费方和提供方,则需要在对应 XML 添加下面配置:

1
<dubbo:monitor protocol="registry"/>

其中 protocol 为“registry”,表示服务提供方和消费方从服务注册中心发现监控中心(Monitor)地址。
如果使用的 Dubbo API 方式需要首先创建一个 MonitorConfig 对象。

1
2
MonitorConfig monitorConfig = new MonitorConfig();
monitorConfig.setProtocol("registry");

然后调用 reference.setMonitor(monitorConfig); 设置到消费配置对象里面。
同样我们下载dubbo源码之后解压打包,这里提供下载点我下载
解压 dubbo-monitor-simple-2.5.10-assembly.tar.gz,进入 dubbo-monitor-simple-2.5.10/conf/ 目录修改 dubbo.properties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
dubbo.container=log4j,spring,registry,jetty
dubbo.application.name=simple-monitor
dubbo.application.owner=
#dubbo.registry.address=multicast://224.5.6.7:1234
dubbo.registry.address=zookeeper://127.0.0.1:2181
#dubbo.registry.address=redis://127.0.0.1:6379
#dubbo.registry.address=dubbo://127.0.0.1:9090
dubbo.protocol.port=7070
dubbo.jetty.port=8081
dubbo.jetty.directory=${user.home}/monitor
dubbo.charts.directory=${dubbo.jetty.directory}/charts
dubbo.statistics.directory=${user.home}/monitor/statistics
dubbo.log4j.file=logs/dubbo-monitor-simple.log
dubbo.log4j.level=WARN

其中 dubbo.registry.address=zookeeper://127.0.0.1:2181 设置注册中心地址,这里设置为 ZooKeeper 的地址。
dubbo.protocol.port=7070,是 Monitor 提供的远程服务监听端口,服务提供者和消费者会调用这个端口提供的服务,发送统计信息到 Monitor。
dubbo.charts.directory 和 dubbo.statistics.directory 为 Monitor 本地存放的监控数据文件的位置。
dubbo.jetty.port=8081,设置 Jetty 容器的监听地址,类似于 Tomcat 的8080端口,这里设置为8081。
然后进入 dubbo-monitor-simple-2.5.10/bin,执行 start.bat 启动 Monitor:
这里写图片描述
至此 Monitor 启动了,访问 http://127.0.0.1:8081/ 会出现下面界面:
这里写图片描述

然后我们就可以使用监控平台做一些事情了。比如服务查看,应用程序查看,调用情况统计
: Dubbo-Monitor 也不是使用 Dubbo 搭建分布式系统必须的组件,但是它用来统计服务的调用次调和调用时间的监控中心,这些数据有助于系统运维和调优。

6. 常用配置

参考自:https://blog.csdn.net/yz357823669/article/details/83591507

之前我们简单介绍了dubbo配置服务提供者、消费者以及管理平台监控平台,接下来我们再说一下dubbo的其他配置。

6.1 配置策略

6.1.1 属性配置

dubbo可以在JVM 启动参数dubboXMLdubbo.properties 三个地方配置相关属性,这里我们以端口为例.

  • JVM 启动参数
    我们可以在启动项目时配置VM参数
    在这里插入图片描述
1
-Ddubbo.protocol.port=20883
  • dubboXML
1
<dubbo:protocol name="dubbo" port="20882"></dubbo:protocol>
  • dubbo.properties
1
dubbo.protocol.port=20881

覆盖策略
在这里插入图片描述

JVM 启动 -D 参数优先,这样可以使用户在部署和启动时进行参数重写,比如在启动时需改变协议的端口。

XML 次之,如果在 XML 中有配置,则 dubbo.properties 中的相应配置项无效。

Properties 最后,相当于缺省值,只有 XML 没有配置时,dubbo.properties 的相应配置项才会生效,通常用于共享公共配置,比如应用名。

6.1.2 XML配置

前面介绍了服务提供者接口的配置

1
<dubbo:service interface="com.yz.dubbo.api.IUserService" ref="userService1" timeout="1000"></dubbo:service>

当然我们只是把timeout 属性配置在了接口上,而接口中有那么多方法,所以我们还可以具体配置到方法

1
2
3
4
<dubbo:service interface="com.yz.dubbo.api.IUserService" ref="userService1" >
<dubbo:method name="getUser" timeout="2000"></dubbo:method>
</dubbo:service>
123

这样我们就指定了getUser 方法timeout 属性

但是服务提供者和消费者有那么多的接口一个一个配置岂不是太麻烦,所以我们可以将一样的配置抽取出来作为服务提供者以及消费者的缺省配置

1
<dubbo:provider timeout="3000"></dubbo:provider>

以 timeout 为例,显示了配置的查找顺序,其它 retries, loadbalance, actives 等类似

  • 方法级优先,接口级次之,全局配置再次之。
  • 如果级别一样,则消费方优先,提供方次之。

在这里插入图片描述
其中,服务提供方配置,通过 URL 经由注册中心传递给消费方。
建议由服务提供方设置超时,因为一个方法需要执行多长时间,服务提供方更清楚,如果一个消费方同时引用多个服务,就不需要关心每个服务的超时设置。

6.2.启动检查

Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题,默认 check=“true”。

可以通过 check=“false” 关闭检查,比如,测试时,有些服务不关心,或者出现了循环依赖,必须有一方先启动。
例子
当我们没有配置检查属性的时候.只启动消费者,可以看到控制台输出错误信息 No provider

在这里插入图片描述

我们配置启动检查checkfalse

1
<dubbo:reference id="userService" interface="com.yz.dubbo.api.IUserService" check="false"></dubbo:reference>

当我们设置check="false" 时,在没有提供者的情况下,消费者启动是不会报错的

1
2
3
4
5
6
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
new String[] { "classpath:customer.xml" });
final IUserService demoService = (IUserService) context.getBean("userService");
// System.out.println(demoService.getUser());
System.out.println("程序运行......");
System.in.read();

在这里插入图片描述
,只有在显示调用提供者服务的时候才会报错

1
2
3
4
5
6
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
new String[] { "classpath:customer.xml" });
final IUserService demoService = (IUserService) context.getBean("userService");
System.out.println(demoService.getUser());
System.out.println("程序运行......");
System.in.read();

在这里插入图片描述

可以看到程序报错,就是显示调用 demoService.getUser()这段代码导致的
注册中心检查

1
dubbo.registry.check=false

我们可以通过以上配置关闭服务提供者消费者对注册中心的检查,这样注册中心挂掉了,我们服务提供者消费者启动也不会报错,只是不能注册服务而已…

例子
当我们的注册中心挂掉的时候,我们启动服务消费者以及提供者是会报错的
在这里插入图片描述
在这里插入图片描述

我们只需要配置注册中心不检查,启动就不会报错而且当注册中心重新恢复的时候他们会自动的订阅服务以及注册服务

1
<dubbo:registry address="zookeeper://127.0.0.1:2181" check="false"></dubbo:registry>

6.3 超时时间

由于网络或服务端不可靠,会导致调用出现一种不确定的中间状态(超时)。为了避免超时导致客户端资源(线程)挂起耗尽,必须设置超时时间。
在现实的开发中我们往往一些服务提供者的调用比较耗时,而dubbo的timeout缺省配置为1000 毫秒,也就是说当消费者调用服务提供者一秒钟还没有返回结果,则消费者会报错,如下

提供者

1
2
3
4
5
6
7
8
9
10
11
12
13
public class UserServiceImpl implements IUserService{
@Override
public List<User> getUser() throws UserException {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return Arrays.asList(new User[]{new User(1,"yz","china","hlj")
,new User(2,"zwl","china","ah")
,new User(3,"hhj","china","zj")});
}
}

我们模拟耗时,让提供者睡眠三秒钟,接下来启动提供者消费者
在这里插入图片描述

可以看到消费者在等待一秒后还没有结果就报错,所以在线上我们可以通过设置timeout来避免提供者服务耗时带来的问题

1
<dubbo:service interface="com.yz.dubbo.api.IUserService" ref="userService1" timeout="4000"></dubbo:service>

在这里插入图片描述

消费者在等待三秒后,提供者服务已经被正常调用

6.4 重试次数

当我们服务消费者消费出现失败,可通过 retries="2" 来设置重试次数(不含第一次),多次调用服务提供者
例子
首先我们去掉之前的timeout 属性来模拟出错,并加上retries 来实现重试

1
<dubbo:reference id="userService" interface="com.yz.dubbo.api.IUserService" check="false" retries="3"></dubbo:reference>

提供者

1
2
3
4
5
6
7
8
9
10
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("被调用了............");

return Arrays.asList(new User[]{new User(1,"yz","china","hlj")
,new User(2,"zwl","china","ah")
,new User(3,"hhj","china","zj")});

接下来我们分别启动服务提供者与消费者,可以看到,我们消费者在调用失败后提供者被调用了四次
在这里插入图片描述
在这里插入图片描述

6.5 多版本

当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。

可以按照以下的步骤进行版本迁移:

  1. 在低压力时间段,先升级一半提供者为新版本
  2. 再将所有消费者升级为新版本
  3. 然后将剩下的一半提供者升级为新版本

也就实现了dubbo所说的灰度发布

例子
首先我们创建提供者接口两个不同实现

UserServiceImpl

1
2
3
4
5
6
7
8
9
@Override
public List<User> getUser() throws UserException {
System.out.println("被调用了1............");

return Arrays.asList(new User[]{new User(1,"yz","china","hlj")
,new User(2,"zwl","china","ah")
,new User(3,"hhj","china","zj")});
}
12345678

UserServiceImpl2

1
2
3
4
5
6
7
8
9
@Override
public List<User> getUser() throws UserException {
System.out.println("被调用了2............");

return Arrays.asList(new User[]{new User(1,"yz","china","hlj")
,new User(2,"zwl","china","ah")
,new User(3,"hhj","china","zj")});
}
12345678

接下来在provider.xml配置文件中提供者接口实现指向不同的两个类,并指定不同的版本

1
2
3
4
5
6
7
8
    <bean id="userService1" class="com.yz.dubbo.impl.UserServiceImpl"></bean>

<bean id="userService2" class="com.yz.dubbo.impl.UserServiceImpl2"></bean>

<dubbo:service interface="com.yz.dubbo.api.IUserService" ref="userService1" version="1.0.0"></dubbo:service>

<dubbo:service interface="com.yz.dubbo.api.IUserService" ref="userService2" version="2.0.0"></dubbo:service>
1234567

customer.xml 中指定消费者的版本号为1.0.0

1
2
<dubbo:reference id="userService" interface="com.yz.dubbo.api.IUserService" check="false" version="1.0.0"></dubbo:reference>
1

在这里插入图片描述

customer.xml 中指定消费者的版本号为2.0.0

1
2
<dubbo:reference id="userService" interface="com.yz.dubbo.api.IUserService" check="false" version="2.0.0"></dubbo:reference>
1

在这里插入图片描述

可以看到了通过指定版本号分别调用了不同的接口实现,这样在实际开发中就能够实现新旧功能的过度

7. 高可用

之前我们说了dubbo超时重试启动检查等配置,接下来我们说一下dubbo高可用的一些配置

7.1 zookeeper宕机

我们接下来讨论一下如果zookeeper宕机对我们的服务提供者消费者有什么影响

现象:zookeeper注册中心宕机,还可以消费dubbo暴露的服务

原因

  • 监控中心宕掉不影响使用,只是丢失部分采样数据
  • 数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务
  • 注册中心对等集群,任意一台宕掉后,将自动切换到另一台
  • 注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯
  • 服务提供者无状态,任意一台宕掉后,不影响使用
  • 服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复

高可用:通过设计,减少系统不能提供服务的时间

例子:

我们在消费者中睡眠20秒,然后我们在这20秒时间内停掉注册中心,看看第二次消费能否成功

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class App 
{

public static void main( String[] args ) throws Exception
{
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
new String[] { "classpath:customer.xml" });
final IUserService demoService = (IUserService) context.getBean("userService");
System.out.println(demoService.getUser());
System.out.println("程序运行第一次......");
Thread.sleep(1000*20);
System.out.println(demoService.getUser());
System.out.println("程序运行第二次......");
System.in.read();

}
}
1234567891011121314151617

接下来我们一次启动服务提供者 消费者 然后停掉服务注册中心

在这里插入图片描述

可以看到

在这里插入图片描述
在这里插入图片描述

我们的服务消费者在注册中心宕机后让然可以调用服务提供者提供的服务。但是注册中心宕机后我们不能再注册新的服务。

7.2 Dubbo直连

在开发及测试环境下,经常需要绕过注册中心,只测试指定服务提供者,这时候可能需要点对点直连,点对点直连方式,将以服务接口为单位,忽略注册中心的提供者列表,A 接口配置点对点,不影响 B 接口从注册中心获取列表。

注意 为了避免复杂化线上环境,不要在线上使用这个功能,只应在测试阶段使用。我们可以在开发的时候使用此方式进行调试

1
<dubbo:reference id="userService" interface="com.yz.dubbo.api.IUserService" check="false" version="1.0.0" url="dubbo://127.0.0.1:20882"></dubbo:reference>

基于注解方式可以在 @Reference(url = ..)里来配置

我们启动我们的服务注册中心与服务提供者消费

发现我们的消费者并没有注册到服务注册中心,但是我们仍然可以调用服务提供者提供的服务

在这里插入图片描述

我们实现了跨注册中心 直连服务提供者

7.3 负载均衡

在集群负载均衡时,Dubbo 提供了多种均衡策略,默认为 Random 随机调用

负载均衡策略

  • Random LoadBalance

    随机,按权重设置随机概率。

  • RoundRobin LoadBalance

    轮询,按公约后的权重设置轮询比率。

  • LeastActive LoadBalance

    最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。

  • ConsistentHash LoadBalance

    一致性 Hash,相同参数的请求总是发到同一提供者。

接下来我们测试一下默认的随机方式

1
2
消费者的xml
<dubbo:service interface="com.yz.dubbo.api.IUserService" ref="userService1" version="1.0.0" loadbalance="random"></dubbo:service>

基于注解方式可以在 @Reference()里的参数来配置

我们启动多个服务提供者,并指定不同的端口号,在实现中通过来区分不同的提供者

1
2
System.out.println("被调用了1............");
System.out.println("被调用了2............");

接下来我们启动多个服务提供者来模拟,并通过Admin控制台中的 倍权 半权 来调节权重 ,结果如下

在这里插入图片描述

接下来我们启动服务消费者模拟消费者多次消费

我们模拟了六次可以看到控制台输出

1
2
3
4
5
6
被调用了0............
被调用了0............
被调用了1............
被调用了0............
被调用了0............
被调用了0............

实现了调用多个服务提供者,并实现了负载均衡

7.4 服务降级

当我服务器的压力比较大的时候,我们可以通过服务降级功能 临时屏蔽某个出错的非关键服务,并定义降级后的返回策略,屏蔽掉不重要的服务如广告服务等,来降低核心业务的压力

  • mock=force:return+null 表示消费方对该服务的方法调用都直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。
  • 还可以改为 mock=fail:return+null 表示消费方对该服务的方法调用在失败后,再返回 null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。

我们可以直接在Admin控制台来操作服务降级和容错

在Dubbo-Admin中

降级:对应服务消费者中的屏蔽功能,相当于不发起远程调用

容错:对应服务消费者中的容错功能,正常调用服务提供者,但在提供者报错等调用失败时,返回null值

屏蔽

我们屏蔽我们的应用yzcustomer

在这里插入图片描述

发现提供者并没有调用且返回null
在这里插入图片描述

容错

我们容错我们的应用yzcustomer,并手动使我们的提供者出错,启动服务提供者和消费者

在这里插入图片描述

发现在调用服务提供者出错时,返回null

在这里插入图片描述

7.5 集群容错

7.5.1 Dubbo容错方案

在集群调用失败时,Dubbo 提供了多种容错方案,默认为 Failover 重试

Failover Cluster

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

Failfast Cluster

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

Failsafe Cluster

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

Failback Cluster

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

Forking Cluster

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

Broadcast Cluster

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

集群模式配置

按照以下示例在服务提供方和消费方配置集群模式

1
<dubbo:service cluster="failsafe" />

1
<dubbo:reference cluster="failsafe" />

7.5.2 整合Hystrix实现容错

简介:Hystrix旨在通过控制那些访问远程系统、服务和第三方库的节点从而对延迟和故障提供更强大的容错能力,Hystrix具备拥有回退机制和断路器功能的线程和信号隔离、请求缓存和请求打包以及监控和配置等功能。

1)、在pom文件中导入依赖(服务提供者和服务消费者都需要导入)

1
2
3
4
5
1         <dependency>
2 <groupId>org.springframework.cloud</groupId>
3 <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
4 <version>1.4.4.RELEASE</version>
5 </dependency>

2)、在主程序启动类上添加@EnableHystrix注解开启服务容错(服务提供者和服务消费者都需要添加)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 1 package cn.coreqi;
2
3 import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;
4 import org.springframework.boot.SpringApplication;
5 import org.springframework.boot.autoconfigure.SpringBootApplication;
6 import org.springframework.cloud.netflix.hystrix.EnableHystrix;
7
8 @SpringBootApplication
9 @EnableDubbo
10 @EnableHystrix //开启服务容错
11 public class SpringbootdubboserviceproviderApplication {
12
13 public static void main(String[] args) {
14 SpringApplication.run(SpringbootdubboserviceproviderApplication.class, args);
15 }
16
17 }

3)、在服务提供者实现类中方法上添加@HystrixCommand注解

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
 1 package cn.coreqi.service.impl;
2
3 import cn.coreqi.entities.User;
4 import cn.coreqi.service.UserService;
5 import com.alibaba.dubbo.config.annotation.Service;
6 import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
7 import org.springframework.stereotype.Component;
8
9 import java.util.ArrayList;
10 import java.util.List;
11
12 @Component //org.springframework.stereotype.Component
13 @Service //com.alibaba.dubbo.config.annotation.Service
14 public class UserServiceImpl implements UserService {
15
16 private static List<User> users = new ArrayList<>();
17
18 static {
19 users.add(new User(1,"fanqi","123456",1));
20 users.add(new User(2,"zhangsan","123456",1));
21 users.add(new User(3,"lisi","123456",1));
22 users.add(new User(4,"wangwu","123456",1));
23 }
24
25 //容错注解
26 @HystrixCommand
27 @Override
28 public List<User> getList() {
29 return users;
30 }
31 }

4)、在服务消费者调用服务提供者的方法上添加@HystrixCommand注解并指定fallbackMethod属性,重写fallbackMethod指定的方法。

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
 1 package cn.coreqi.controller;
2
3 import cn.coreqi.entities.User;
4 import cn.coreqi.service.UserService;
5 import com.alibaba.dubbo.config.annotation.Reference;
6 import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
7 import org.springframework.stereotype.Controller;
8 import org.springframework.web.bind.annotation.RequestMapping;
9 import org.springframework.web.bind.annotation.ResponseBody;
10
11 import java.util.List;
12
13 @Controller
14 public class UserController {
15
16 @Reference()
17 private UserService userService;
18 //添加此注解
19 @HystrixCommand(fallbackMethod = "test1")
20 @ResponseBody
21 @RequestMapping("/users")
22 public List<User> getUsers(){
23 return userService.getList();
24 }
25
26 public List<User> test1(){
27 return null;
28 }
29 }

7. 总结

7.1 配置Dubbo的三种方式

  • dubbo.xml来配置属性,使用@ImportResource导入xml配置文件

  • application.properties配置属性,使用@Service注解暴露服务,使用@Reference注解来引用服务

    需要在启动类加注解@EnableDubbo来开启Dubbo注解的功能

  • 使用@Configuration 注解来编写配置类,@Bean 编写对应方法

7.2 回顾

本文主要介绍了

  • Dubbo的背景
  • Dubbo和Zookeeper安装
  • Dubbo和Zookeeper的整合,创建生成者消费者
  • Dubbo-Admin、Dubbo-Monitor界面
  • Dubbo的常用配置、高可用配置

在Dubbo的配置这一块中,其实还有很多。更多的东西可以在官网上进行查看学习

官网是自带中文版的,还是挺舒服的:http://dubbo.apache.org/zh/docs/

dz05

8. Peace

8.1 参考文献

8.2 其他

还有一些不理解的地方,以后再做补充:

  • dubbo的本地存根
  • Dubbo-admin这个容错按钮和,dubbo整合Hystrix实现容错有什么区别
Prev
2022-09-22 22:43:16
Next