Dubbo
Dubbo
概念
-
简介
Dubbo是一个高性能的 RPC框架, 解决了分布式中的调用问腿 -
优点: 解决了分布式系统中互相调用的问题
-
缺点: 假设由
100台服务器 50台用户业务服务器, 50台订单业务服务器, 但是在上线后发现, 用户服务器使用率很小, 但是订单服务器压力很大, 最佳匹配比应该是 1:4,这时候就要求我们还有一个统一管理的调度中心 -
为什么
Dubbo说自己性能高 高性能要从底层的原理说起,
既然是一个 RPC框架, 主要干的就是远程过程 (方法) 调用, 那么提升性能就要从最关键, 最耗时的两个方面入手: 序列化和网络通信 - 序列化: 我们学习
Java网络开发的时候就知道, 本地的对象要在网络上传输, 必须要实现 Serializable接口, 也就是必须序列化,我们序列化的方案很多: xml,json,其中效率最高的就是二进制流二进制流 ( 因为计算机就是二进制的),然而 Dubbo采用的就是效率最高的二进制 - 网络通信: 不同于
HTTP需要进行 7步走 ( 三次握手和四次挥手),Dubbo采用 Socket通信机制, 一步到位, 提升了通信效率, 并且可以建立长连接, 不用反复连接, 直接传输数据
- 序列化: 我们学习
-
架构
摘自官网 
- 服务提供者
( provider):暴露服务的服务提供方,服务提供者在启动时,向注册中心注册自己提供的服务 - 服务消费者
( Consumer): 调用远程服务的服务消费方,服务消费者在启动时, 向注册中心订阅自己所需的服务, 服务消费者,从提供者地址列表中, 基于软负载均衡算法, 选一台提供者进行调用, 如果调用失败, 再选调另一台 - 注册中心
( Registry): 注册中心返回服务提供者地址列表给消费者,如果有变更, 注册中心将基于长连接推送变更变数据给消费者 - 监控中心
( Monitor):服务消费者和提供者,在内存中累计调用次数和调用时间, 定时每分钟发送一次统计数据到监控中心
- 服务提供者
-
调用关系说明
-
服务容器负责启动,
加载, 运行服务提供者 -
服务提供者在启动时,
向注册中心注册自己提供的服务 -
服务消费者在启动时,向注册中心订阅自己所需的服务
-
注册中心返回服务提供者地址列表给消费者,如果有变更,
注册中心将基于长连接推送变更数据给消费者 -
服务消费者,
从提供者地址列表中, 基于软负载均衡算法, 选一台提供者进行调用, 如果调用失败, 再选另一台调用 -
服务消费者和提供者,
在内存中累计调用次数和调用时间, 定时每分钟发送一次统计数据到监控中心
-
Dubbo 支持的协议
-
协议
支持多种协议:
dubbo,hessian,rmi,webservice,thrift,memcached,redis,dubbo,官方推荐 dubbo 协议 dubbo协议默认端口 20880使用
dubbo协议, spring配置文件加入: <dubbo:protocol name="dubbo" port="20880" /> -
优点
采用
NIO复用单一长连接, 并使用线程池并发处理请求, 减少握手和加大并发效率, 性能较好 ( 推荐使用) -
缺点
大文件上传时,
可能出现问题 ( 不使用Dubbo 实现文件传输)
日志配置
-
配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15# DEBUG < INFO < WARN < ERROR < FATAL
# Global logging configuration
log4j.rootLogger=info, stdout,file
# My logging configuration...
#log4j.logger.com.tocersoft.school=DEBUG
#log4j.logger.net.sf.hibernate.cache=debug
## Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p %d %C: %m%n
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=../logs/iask.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %m%n -
依赖
1
2
3
4
5
6
7
8
9
10
11<!--日志-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
Dubbo-Admin
-
前提环境:
Node、Maven,dubbo-admin(master分支), 使用 cmd 执行 -
简介
dubbo-admin管理平台, 是图形化的服务管理页面 - 从注册中心获取到所有的提供者
/ 消费者进行配置管理 - 路由规则,
动态配置、服务降级、访问控制、权重调整 dubbo-admin是一个前后端分离的项目, 前端使用 vue,后端使用springboot- 安装
dubbon-admin其实就是部署该项目
-
下载
dubbo-admin(master):https://github.com/apache/dubbo-admin/tree/master
-
配置注册中心
( dubbo-admin > dubbo-admin-server >resource>*.properties)1
2
3
4
5
6
7
8
9
10
11# zookeeper 修改 ip 地址后 通过 ./zkCli,sh -server ip:port 可以连接
(默认加载 2181)
# ip 地址是部署 zookeeper 环境的 ip 地址与端口
admin.registry.address=zookeeper://43.142.80.9:2181
admin.config-center=zookeeper://43.142.80.9:2181
admin.metadata-report.address=zookeeper://43.142.80.9:2181
# 设置最大等待时间为10 秒
dubbo.metadata-report.timeout=10000
# 配置中心连接时间改为10 秒
dubbo.config-center.timeout=10000 -
在
dubbo-admin/目录下执行打包 1
2
3
4
5
6# 执行打包
mvn clean package -Dmaven.test.skip=true
# 过程中出现错误
1. 代理问题(如果有可能, 在 maven settings.xml 添加代理配置)
2. node 插件下载问题,请直接删除项目中的 node 下载,在系统安装
https://npm.taobao.org/mirrors/node/v14.6.0/(镜像下载地址)-
问题一
1
2
3
4
5
6
7
8
9
10<!-- maven settings.xml -->
<proxies>
<proxy>
<id>optional</id>
<active>true</active>
<protocol>http</protocol>
<host>your proxy ip</host>
<port>your proxy port</port>
</proxy>
</proxies>proxy
-
问题二
删除 node下载插件 再次执行 mvn clean package -Dmaven.test.skip=true

-
成功编译信息
( 删除项目中 Node 插件,这个node的作用是在 dubbo-admin-ui执行下载, 可以直接安装在系统上, 手动执行 npm install)
-
-
在
dubbo-admin-master\dubbo-admin-ui执行 npm install -
在
dubbo-admin-master\dubbo-admin-distribution\target下执行如下 1
2# 启动
java -jar dubbo-admin-0.3.0.jar启动 
-
在
dubbo-admin-master\dubbo-admin-ui执行 npm run devnpm run dev启动 UI
-
访问
UI默认 roo成功登录 

RPC
分布式系统
-
什么是分布式系统
分布式系统是若干独立计算机
(服务器) 的集合, 这些计算机对于用户来说就像单个相关系统, 分布式系统是建立在网络之上的服务器端一种结构 分布式系统总计算机可以使用不同的操作系统,
可以云兄不同应用程序提供服务, 将服务分散部署到多个计算机服务器上
RPC
-
什么是
RPCRPC是指远程过程调用, 是一种进程间通信方式, 是一种技术思想,而不是规范,它允许程序调用另一个地址空间 (网络的另一台机器上) 的过程或函数, 而不用开发人员显示编码这个调用的细节,调用本地方法和调用远程方法一样 -
RPC特点 - 简单: 使用简单,
建立分布式应用更容易 - 高效: 调用过程看起来十分清晰,
效率高 - 通用: 进程间通讯的方式,
有通用的规则
- 简单: 使用简单,
-
RPC原理 RPC原理 
RPC调用过程 - 调用方
client要使用右侧 server的功能 (方法), 发起对方法的调用 client stub是 PRC中定义的存根, 看作是 client的助手, stub把要调用的方法参数进行序列化, 方法名称和其他数据包装起来 - 通过网络
socket(网络通信的技术),把方法调用的细节内容发送给右侧的 server server端通过 socket接受请求的方法名称, 参数等数据, 传给 stubserver端接到的数据由 server stub(server 的助手)处理,调用 server的真正方法, 处理业务 server方法处理完业务, 把处理的结果对象 Object交给了助手, 助手把 Object进行序列化, 对象转为二进制数据 server助手把二进制数据交给网络处理程序 - 通过网络将二进制数据发送给
client client接数据, 交给 client助手 client助手, 接受数据通过反序列化为 java对象 ( Object),作为远程方法调用的结果
- 调用方
直连方式
-
图解
Consumer -> Provider不使用注册中心 
-
实现目标
直连方式访问 
-
实现步骤
-
创建新项目
-
添加模块
( link-orderservice-provider: dubbo)的服务提供者, 提供接口, 让消费者调用 -
新建
maven -
加入依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<!-- Spring 依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.14.RELEASE</version>
</dependency>
<!-- dubbo 的依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency> -
新建保存数据的实体类
Order,实现序列化接口1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19package com.coderitl.dubbo.domain;
import lombok.Data;
import java.io.Serializable;
public class Order implements Serializable {
private static final long serialVersionUID = -4779766312604286029L;
// 订单 id
private String id;
// 商品名称
private String goodName;
// 单价
private float price;
// 购买数量
private int amout;
} -
新建业务接口
OrderServcice,定义方法createOrder -
新建业务接口的实现类,
实现 createOrder() -
新建
spring的配置文件, 通过容器创建和管理对象 - 声明
Dubbo的服务名称 - 声明服务的接口,
暴露接口 - 声明服务接口的实现类
- 声明
-
新建测试类,
测试 spring的配置文件 -
把服务提供者的
jar,安装到本地的maven仓库
-
-
link-main-web(quick)创建消费者项目, 调用服务提供者中接口的方法 - 实现步骤
- 新建
maven - 加入依赖
- 定义业务接口和实现类
- 新建
spring的配置文件 - 声明服务的名称
- 声明要使用的远程接口额
服务提供者) - 声明自定义的业务对象
- 新建测试通过消费者访问提供者
- 新建
- 实现步骤
-
-
Dubbo 服务化最佳实践
-
分包
建议将服务接口、服务模型、服务异常等均放在公共包中
-
粒度
服务接口尽可能最大粒度,
每个服务方法应代表一个功能, 而不是某功能的一个步骤, 否则将面临分布式事务问题, Dubbo暂未提供分布式事务支持 服务接口建议以业务场景为单位划分,
并对相近业务做抽离, 防止接口数量爆炸 不建议使用过于抽象的通用接口,
如 Map query(Map)这样的接口没有明确语义, 会给后期维护带来不便 -
版本
每个接口都应该定义版本号,
为后续不兼容升级提供可能, 如: <dubbo:service interface="" version="1.0">建议使用两位版本号,
要变更服务版本, 先升级一半提供者为新版本,再将消费者全部升级为新版本, 然后将剩下的一半提供者升为新版本 -
项目修改
-
实现目标
实现目标 
-
快速入门
-
抽取公共接口
dubbo-interface => jar模块包 1
2
3
4
5
6package com.coderitl.service;
public interface UserService {
public String sayHello();
} -
创建提供者模块
dubbo-service =>webapp(war)-
创建公共接口的实现类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16package com.coderitl.service.impl;
import com.coderitl.service.UserService;
import org.apache.dubbo.config.annotation.Service;
//@Service //将该类的对象创建出来,放到 Spring 的 IOC 容器中 bean 定义
// 将这个类提供的方法(服务)对外发布。将访问的地址 ip,端口,路径注册到注册中心中
public class UserServiceImpl implements UserService {
public String sayHello() {
return "hello dubbo hello!~";
}
} -
创建配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23<!-- resources/spring/applicationContext.xml -->
<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">
<!-- 被 dubbo 扫描注解代替 -->
<!--<context:component-scan base-package="com.coderitl.service" />-->
<dubbo:application name="dubbo-service"/>
<dubbo:registry address="zookeeper://43.142.80.9:2181"/>
<dubbo:annotation package="com.coderitl.service.impl" />
</beans> -
WEB.xml1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<!-- spring -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring/applicationContext*.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
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.coderitl</groupId>
<artifactId>dubbo-service</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<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>
<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.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</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>
<!--依赖公共的接口模块-->
<dependency>
<groupId>com.coderitl</groupId>
<artifactId>dubbo-interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>8000</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
-
-
创建消费者模块
dubbo-web => webapp(war)-
创建控制器
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
33package com.coderitl.controller;
import com.coderitl.service.UserService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
public class UserController {
// 注入 Service
// @Autowired // 本地注入
/*
1. 从 zookeeper 注册中心获取 userService 的访问 url
2. 进行远程调用 RPC
3. 将结果封装为一个代理对象。给变量赋值
*/
// 远程注入
private UserService userService;
public String sayHello(){
return userService.sayHello();
}
} -
创建配置文件
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<!-- resources/spring/springmvc.xml -->
<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:mvc="http://www.springframework.org/schema/mvc"
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://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.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">
<mvc:annotation-driven/>
<context:component-scan base-package="com.coderitl.controller"/>
<dubbo:application name="dubbo-web">
<dubbo:parameter key="qos.port" value="33333"/>
</dubbo:application>
<dubbo:registry address="zookeeper://43.142.80.9:2181"/>
<dubbo:annotation package="com.coderitl.controller"/>
</beans> -
web.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
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<!-- Springmvc -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app> -
依赖
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.coderitl</groupId>
<artifactId>dubbo-web</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<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>
<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.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</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>
<!--依赖公共的接口模块-->
<dependency>
<groupId>com.coderitl</groupId>
<artifactId>dubbo-interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>9000</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project> -
访问测试
访问测试 
-
注册中心
-
Zookeeper安装 -
端口问题
zookeeper3.5x 占用 8080 解决方案: 配置文件中添加: admin.serverPort=xxxx
序列化
-
问题: 两个机器传输数据,
如何传输 Java对象? 序列化 
-
实现
1
2
3
4
5
6
7
8
9
10import java.io.Serializable;
public class UserInfo implements Serializable {
private Integer id;
private String userName;
private String address;
} -
接口与实现
1
2
3
4
5
6
7
8
9
10
11// 实现
public UserInfo getUserById(int id) {
UserInfo userInfo = new UserInfo(1, "coder-itl", "BJ");
return userInfo;
}
// 控制器
public UserInfo getInfo() {
return userService.getUserById(1);
} -
访问
序列化实现 
地址缓存
- 注册中心挂了,
服务是否可以正常访问? - 可以,因为
Dubbo服务消费者在第一次调用时, 会将服务提供方地址缓存到本地, 以后在调用则不会访问注册中心 - 当服务提供者地址放生变化时,
注册中心会通知服务消费者
- 可以,因为
超时与重试
- 添加位置
@Service(timeout=1000,retries=0) 单位毫秒,默认 1000ms @Reference(timeout=1000)单位毫秒, 默认 1000ms - 超时建议添加在
服务提供方 - 重试默认
retries=2
多版本
-
灰度发布
部分更新,提供反馈 完全更新 

-
Version:Dubbo中使用 Version 属性来设置和调用同一个接口的不同版本 1
2
3
4
负载均衡
-
配置负载均衡
1
2
3
4
5
6// weight 配置权重
集群容错
-
集群容错模式
-
Failover Cluster: 失败重试,,默认值 当出现失败, 重试其他服务器, 默认重试 2次 ( retries=2),使用retries配置, 一般用于读操作 1
// 默认
-
Failfast Cluster: 快速失败,只发起一次调用, 失败立即报错, 通常用于写操作 -
Failsafe Cluster: 失败安全,出现异常时, 直接忽略, 返回一个空结果 -
Failback Cluster:失败自动恢复, 后台记录失败请求, 定时重发 -
Forking Cluster: 并行调用多个服务器,只要一个成功即返回 -
Broadcast Cluster:广播调用所有提供者,逐个调用, 任意一台报错则报错
-
服务降级
-
含义
对重要的服务降级 
-
官网
https://dubbo.apache.org/zh/docs3-v2/java-sdk/advanced-features-and-usage/service/service-downgrade/
1
- 配置
mock="[fail|force]return|throw xxx"fail或 force 关键字可选,表示调用失败或不调用强制执行 mock方法,如果不指定关键字默认为 failreturn表示指定返回结果, throw表示抛出指定异常 xxx根据接口的返回类型解析,可以指定返回值或抛出自定义的异常
- 配置
基于 SpringBoot-Dubbo
-
项目创建
( 聚合项目)-
添加模块
dubbo-provider-8080-
SpringBoot->Dubbo: https://github.com/apache/dubbo-spring-boot-project -
依赖
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<dependencies>
<!-- springboot 起步依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- dubbo 起步依赖 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.7</version>
</dependency>
<!-- zookeeper 客户端依赖 -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>5.1.0</version>
</dependency>
<!-- zookeeper 客户端依赖 -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.1.0</version>
</dependency>
</dependencies> -
缺少依赖添加技巧
NoClassDefFoundError =>apache/curator/framework => apache-curator-framework在仓库查找
-
配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15# application.yml
server:
port: 8080
dubbo:
registry:
address: zookeeper://43.142.80.9:2181 # 注册中心
timeout: 600000 # 超时时间
application:
name: dubbo-provider-8080 # 服务名称,如果多个启动的服务进程命名一致,自动组成集群
protocol:
name: dubbo # 协议
port: 20880 # 端口
config-center: # 配置中心超时时间
timeout: 600000 -
zookeeper存储内容解读 1
2
3
4
5
6
7
8
9
10
11
12
13/dubbo/com.coderitl.dubbo.service.FirstService/providers/dubbo%3A%2F%2F10.40.0.19%3A20880%2Fcom.coderitl.dubbo.service.FirstService%3Fanyhost%3Dtrue%26application%3Ddubbo-provider-8080%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26interface%3Dcom.coderitl.dubbo.service.FirstService%26methods%3DsayHello%26pid%3D15532%26release%3D2.7.7%26side%3Dprovider%26timestamp%3D1660132553394
%3A: => :
%2F: => /
%3F: => ?
%3D: => =
%26: => &
# 解析后的内容
/dubbo/com.coderitl.dubbo.service.FirstService/providers/dubbo://10.40.0.19:20880/com.coderitl.dubbo.service.FirstService?anyhost=true&application=dubbo-provider-8080&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=com.coderitl.dubbo.service.FirstService&methods=sayHello&pid=15532&release=2.7.7&side=provider×tamp=1660132553394 -
案例实现
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// 第一个 Dubbo 服务提供者: provider
// RPC 的服务逻辑
// Dubbo 框架提供了自定义注解,用于服务的注册和发现
// 当开发的代码是服务提供者的时候,需要把服务注册到注册中心, 等待客户端调用
// 当开发的代码是服务消费者时,需要从注册中心发现服务, 并创建代理对象, 访问远程服务
// Dubbo 自定义了注解: @Service 用于注册服务,注意导入的包不要和 spring 的 service 注解混淆
// Dubbo 2.7.7 版本开始,@Service注解过时, 提供新注解 @DubboService 共能和 Dubbo 的 @Service 注解完全一致
package com.coderitl.dubbo.service.impl;
// @DubboService: 功能是: 在 Spring 容器启动的时候,创建 bean 对象,并拼接一个 URL 保存到指定的注册中心 (zookeeper 内容解读部分)
// 默认环境中,Dubbo 的 Provider 是不分组的,可以指定服务分组, 目的: 多客户端并行生效, 多服务分组管理 group="分组名" =>,@DubboService(loadbalance = "roundrobin",group="coderitl")
// 当服务端将 Provider 分组后,消费端引用的 Consumer 必须指定要定位的分组 (服务端和消费端需同时指定)
// 默认环境中 Dubbo 的 Provider 没有版本设定的,可以指定版本, 目的是: 多版本并行提供服务 商业开发中, 服务都需要版本
// loadbalance = "roundrobin" 负载均衡配置 => 轮询
public class FirstServiceImpl implements FirstService {
public String sayHello() {
return "SpringBoot Dubbo sayHello...............";
}
}1
2
3
4
5
6
7
8// 启动类
public class DubboProviderMain8080 {
public static void main(String[] args) {
SpringApplication.run(DubboProviderMain8080.class, args);
}
}
-
-
-
consumer模块 -
配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14server:
port: 8001
dubbo:
registry:
address: zookeeper://43.142.80.9:2181
timeout: 600000
application:
name: dubbo-consumer-8001
protocol:
name: dubbo
port: 20880
config-center:
timeout: 600000 -
依赖
1
2
3
4
5
6
7
8
9
10
11
12<!-- 在 provider 的基础上添加如下依赖 -->
<!-- 接口抽取 -->
<dependency>
<groupId>com.coderitl.dubbo</groupId>
<artifactId>dubbo-common-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- springmvc web 起步依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> -
本地服务与远程服务无关
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
30package com.coderitl.dubbo.service.impl;
import com.coderitl.dubbo.service.FirstClientService;
import com.coderitl.dubbo.service.FirstService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Service;
// 客户端本地服务实现,和服务端代码实现无关系
public class FirstClientServiceImpl implements FirstClientService {
// 定义一个服务接口调用 基于 Dubbo 为这个接口创建代理对象
// 在 2.7.7 以前版本,使用 @Reference 注解, 2.7.7 使用 @DubboReference 注解
// 注解的功能: 就是访问 zookeeper,获取 URI 统一资源路径, 并创建代理对象
// 也可以指定负载均衡策略 loadbalance = "roundrobin"
// @DubboReference(loadbalance = "roundrobin")
private FirstService firstService;
public String clientSayHello() {
/**
* 当前服务代码: 本地无具体逻辑,实现由远程服务提供, 需要远程过程调用,RPC 调用
* 使用 Dubbo 技术,创建一个本地的代理对象, 调用远程的服务逻辑,
* 需要使用一个服务的标准,基于标准 (接口), 创建代理对象, 并调用远程服务
*/
return firstService.sayHello();
}
} -
控制器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25package com.coderitl.dubbo.controller;
import com.coderitl.dubbo.service.FirstClientService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
public class FirstController {
/**
* 如果没有本地的服务逻辑,直接注入远程的服务代理对象也可以 (不推荐)
* 使用 @DubboReference 注解
* 原因:
* 因为服务的调用者,一般都会有本地逻辑, 直接使用远程服务, 可能破坏分层体系
*/
private FirstClientService firstClientService;
public String first() {
// 本地服务调用(本地服务通过远程调用)
return firstClientService.clientSayHello();
}
} -
访问测试
访问测试 
-
总结
( Dubbo本身就是一个工具, 作用有限, 核心是) - 把服务端
provider注册到注册中心中去, 把 URL存储到 zookeeper中 - 访问注册中心,
获取 URL地址, 并创建代理对象
- 把服务端
-
负载均衡
-
集群: 一个内容,
部署多次, 形成的整体称为集群, 集群中每个个体应该部署到不同的服务器上 -
伪集群: 集群中内容部署到同一台服务器上,通过不同端口区分不同个体
负载均衡是在集群前提下,
当访问整个集群时, 集群中每个节点被访问次数或频率的规则 Dubbo内置了四个负载均衡策略, 默认为 Random -
负载均衡策略的决定规则
Provider方默认是负载均衡策略是 Random,随机策略Consumner方默认负载均衡不设定, 使用 Provider提供的负载均衡策略 - 如果双方都设置了负载均衡策略,
使用 Consumer的负载均衡策略, 因为局部优先, Provider是给所有的 Consumer提供服务的, 负载均衡策略也是给所有的 Consumer提供的默认策略, Consumer的负载均衡策略只局限在当前 i 消费者, 是一个局部策略
-
内置策略
-
Random:随机,随机访问集群中节点,访问概率和权重有关 -
RounodRobin: 轮询( 推荐,因为可以配置权重),访问频率和权重有关 - 权重: 占有比例,集群中每个项目部署的服务器的性能可能是不同的,
性能好的服务器权重应该高一些
weight => random
- 权重: 占有比例,集群中每个项目部署的服务器的性能可能是不同的,
-
LeastActice: 活跃数相同的随机,不同的活跃数高的放前面 最少的活跃调用数 
-
ConsistentHash:一致性Hash,相同参数请求总是发到一个提供者一致性 Hash,相同参数请求总是发到一个提供者
-
-
总结: 在
Dubbo中, 负载均衡的配置有 3个位置 -
@Service | @DubboService注解上的属性, loadbalance代表为 Provider集群定义负载均衡策略, 默认是 random -
@Reference | @DubboReference注解上的属性, loadbalance代表为 Consumer定义调用 Provider集群时使用的负载均衡策略, 默认 无,采用 Provider提供的负载均衡策略 -
在配置文件中,配置文件是当前应用全局配置,
注解是当前类型的对象对应的 Provider或 Consumer配置 1
2
3
4
5
6# 配置文件配置负载均衡
dubbo:
provider:
loadbalance: random # 配置当前应用中所有的`Provider`默认的负载均衡,如果注解定义其他负载均衡策略, 局部优先
consumer:
loadbalance: random # 配置当前应用中所有的 Consumer 的默认负载均衡策略
-
-
payload1
2
3
4
5
6
7
8# 配置文件常用配置: payload
# payload 是指请求容量和响应容量的限制,默认限制为 8M,8388608 字节 也就是 请求头 + 请求体容量必须小于 8M, 响应头 + 响应体容量必须小于 8M 只能在配置文件中进行配置, 只要配置了, 请求和响应的容量同步变更
# 在服务端配置文件中配置
dubbo:
provider:
payload: 83886080
-
retries1
2
3
4# 幂等性操作,
可以配置 > 0, 在非幂等性操作 配置为 0 => Consumer 配置中进行配置
dubbo:
consumer:
retries: 2 # 重试次数,默认为 2,Provider 因网络问题, 无响应, 消费端自动重新请求