Dubbo
1.Dubbo项目的结构特点
首先我们知道Dubbo是Alibaba开源的高性能分布式服务框架,它具有以下主要的六个特点:
- 分层架构:
- Dubbo框架设计一共划分了多个层(如10层或不同资料中提及的具体分层略有差异,但核心层相似),这些层之间最大限度地解耦合,使得框架更加灵活和可扩展。
- 从下至上,这些层包括数据序列化层、网络传输层、信息交换层、远程调用层、服务注册层、集群层、监控层等,每一层都承担了不同的职责,共同构成了Dubbo的分布式服务架构。
- 角色划分:
- Dubbo中明确划分了服务提供者(Provider)、服务消费者(Consumer)、注册中心(Registry)和监控中心(Monitor)等关键角色。
- 服务提供者负责实现并发布服务,服务消费者负责调用服务,注册中心负责服务的注册与发现,监控中心则负责统计和监控服务的调用情况。
- 远程通信与协议支持:
- Dubbo支持多种远程通信协议,如Dubbo协议、Hessian协议、HTTP协议等,这些协议保证了服务提供者和服务消费者之间的高效、稳定的消息传输。
- Dubbo提供了对多种基于长连接的NIO框架的抽象封装,包括多种线程模型、序列化方式以及“请求-响应”模式的信息交换方式,进一步增强了远程通信的灵活性和可靠性。
- 集群容错与负载均衡:
- Dubbo提供了多种集群容错机制,如失败重试、快速失败、失败安全等,这些机制提高了系统的稳定性和可用性。
- 同时,Dubbo还支持多种负载均衡策略,如随机负载均衡、轮询负载均衡、最少活跃调用数负载均衡等,这些策略可以根据实际需求进行选择和配置。
- 服务注册与发现:
- Dubbo的服务注册与发现机制基于注册中心实现,服务提供者在启动时将自己的服务注册到注册中心,服务消费者则通过注册中心获取服务提供者的信息。
- 这种机制使得服务提供者和服务消费者之间可以动态地查找和调用服务,无需提前配置固定的服务地址。
- 可视化服务治理:
- Dubbo提供了可视化的服务治理界面,方便开发者对分布式系统进行监控和管理。
- 通过该界面,开发者可以实时查看服务的调用情况、性能指标和统计信息,从而进行性能优化和故障排查。
在这里,我写了三个项目文件,大家认真看了就会发现它具有以下的层级关系:业务模块 => 业务父模块 => 业务功能模块。
我们看图更好理解一点:
shared.all
↓
demo.shared
↓
demo.api
并且业务模块包下可以有多个业务父模块,业务父模块包下可以有多个业务功能模块。这就造成了一对多的问题。
另外有几个注意点要注意。
1.<packaging>标签
其中 业务模块 和 业务父模块 都是父级模块,没有 Spring Bean只是为了组织模块,整理好模块的顺序;而业务功能模块 是有具体的 Spring Bean 实现代码的。
如果你一定要写,就要写作,这也<packaging>jar</packaging>是 Maven 系统的默认值。
- pom 用于父级工程或聚合工程,主要用于组织下级模块、控制模版版本。
- jar 用于业务功能模块,系统将会打包成iar用作iar包使用,是packaging默认值。
2.不要忘记添加联系模块的依赖
demo.api是具体的业务功能模块,包含了代码,就需要在项目启动时被加载。但由于跟主启动类 Application.java 不在同一个模块中了,所以必须修改 application/pom.xml,告诉 application 在启动的同时需要加载 demo.api。
因此需要为application/pom.xml增加依赖:
<dependencies>
<dependency>
<artifactId>demo.api</artifactId>
<groupId>com.youkeda.exercise.shared</groupId>
<version>${project.version}</version>
</dependency>
</dependencies>
${project.version} 是一个变量,表示同一个项目(app.al1 )中,不同的子模块之间可以 自动识别 版本号。
demo.api的<groupId>值是继承父模块的<groupId>
以后每增加一个包含代码的具体的业务子模块,都 必须 在application/pom.xm/中 增加依赖。否则项目启动的时候,不会自动加载的哦。
3.modules标签
当一个模块有子模块的时候必须写<modules>标签,如下:
<modules>
<module>application</module>
<module>shared.all</module>
</modules>
注意了,只要一个模块下面有子模块,必须加。
4.JDK版本
JDK的版本建议一致,52.0是jdk8,55.0是jdk11。
5.包的扫描路径
@SpringBootApplication(scanBasePackages = {"com.youkeda.exercise","com.youkeda.comment"})
@MapperScan(basePackages = {"com.youkeda.comment.dao"})
public class Application {
}
上面只是个例子。
6.实现类不要忘加添加依赖和相关注解
<!-- dubbo依赖 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.7</version>
</dependency>
<!-- nacos 注册中心 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-nacos</artifactId>
<version>2.7.7</version>
</dependency>
import org.apache.dubbo.config.annotation.DubboService;
@DubboService(version = "${user.service.version}")
public class UserServiceImpl implements UserService {
}
7.dobbo的相关配置文件也不要忘记。
## 项目名称
dubbo.application.name = youkeda-user-app
## 注册中心地址
dubbo.registry.address = nacos://nacos.dev.youkeda.com:8848
## 服务实现类所在的包,会被自动扫描
dubbo.scan.base-packages = com.youkeda.comment.service.impl
## UserService 的版本号
user.service.version = 1.0.0
可以在nacos注册中心里面查询 nacos.dev.youkeda.com:8848/nacos/index…
2.Dubbo的基本原理
我们可以看到主要分为三个阶段:
1.系统启动
springboot项目启动时会向注册中心注册服务和订阅服务。另外,提供者应该在消费者之前启动项目才行。上面的评论系统调用了提供者,那么提供者就不能依赖消费者,否则就不知道该启动那个了。
2.系统进行时
消费者订阅了服务,那么提供者有什么变化,都会通知给消费者更新。
例如提供者可以是集群,有 N 台服务器当提供者增加和减少服务器的时候,都会通知给消费者。注意看图中 右上角 的图示说明,这个通知的过程是异步的。所谓异步,就意味着提供者的变化不一定是立即的实时的通知。
3.服务调用
从图中可以看到,消费者调用提供者的服务,并不是通过注册中心中转的,而是 直接请求 提供者的。所以这里要求服务提供者和消费者在同一个局域网中,服务器计算机之间能够连通。
这对安全性是有保证的,也就是说,服务不是任意计算机都能调用的。
3.Dubbo的核心功能
负载均衡
什么是负载均衡?
负载均衡就是把用户的请求分发到每台服务器上。但是分发方式不一致,这里拿随机分发来举例子,请求量越大,那么随机的结果就越均衡;也可以轮循,每台服务器依次分发。
另外还有软负载,因为这种硬件负载服务器的成本较高,反而使用软件来进行负载均衡在当前的云时代更合适。
了解了概念,就可以看Dubbo里面负载均衡的实现了。
通过上图我们可以得知,因为服务提供者可能是集群,所以 Dubbo 负载均衡指的是 消费者调用服务能够均匀落在每台具体的提供者的服务器上。
负载均衡的实现原理
1.随机负载均衡
这个是dubbo的默认负载均衡。消费者会从注册中心订阅到所有的服务提供者,当消费者需要调用某一个服务时,会默认采用随机负载均衡策略确定一个具体的服务提供者,然后指定这个提供者的 IP 直接发起网络请求。
在添加注解@DubboService里面可以添加一个权重字段,一般不添加默认为0,即@DubboService(weight = 0)。
1.1根据权重随机
假设有三台服务器,那么A,B,C的权重值分别是10,20,30。总和为60,那么权重分布为:
A | B | C |
0----9|10----29|30-----59
调用random.nextInt(60)得到一个 0-59 范围的随机数,随机数落在哪个区间,就表示调用哪个提供者。
因为服务器 C的权重高,所以 C的区间广,理论上被随机命中的概率就高。例如随机数是 40 ,命中区间是 30-59 ,所以命中 C。
1.2均等随机
例如三台服务器的权重值一样,那么就是纯属随机了,编号为1,2,3。这里的编号指的是索引。
2.轮询负载均衡。
就是简单的依次调用Provider,这里也是跟权重值有关系的,权重高的则会优先轮到。
因为随机负载在请求量大的情况下会与权重值趋于一致,但是请求量小的情况下就不一定了。但是轮询负载均衡可以。
3.最少活跃负载均衡
在调用时选择此时处理请求个数最少的提供者,这样子可以让集群中的每个提供者处理的都更加均衡。
4.最短响应时间负载均衡
指预估出来每个处理完请求的提供者所需时间,然后又选择最少最短时间的提供者进行调用。整体处理逻辑和最少活跃负载均衡基本相似,但主要指标是衡量哪个 Provider处理速度更快。
这个策略是比较新的 Dubbo 2.7.7版本才有的。本课程第 3 章第 5节引入的正是 2.7.7版本。
5.一致哈希负载均衡
这个策略很少用到,主要作用就是利用哈希算法将相同的请求落到同一个服务器。
如何修改负载均衡策略
在消费者端可以定义,利用注解@DubboReference(loadbalance = "")。如下:
重试机制
既然是计算机之间的网络调用,那么就不可能保证100%不出错。因此我们可以在注解上面添加参数来改变重试机制。
@DubboReference(timeout = 1000, retries = 2)
一般设置超时时间默认都是1000ms,重试次数默认都是2次(重试的次数不包括第一次正常调用,因此实际上是调用了三次)。
在一些写操作建议不要重试,因为写操作要进行io操作,这就导致一般还没完成操作就会超时,一旦设置了重试次数,就会导致产生脏数据(一模一样的数据)。
小结
在我们一般的平常使用中一般只需要会使用@DubboReference(version = "")指定版本号就OK。上面的一般都是架构师讨论评估的,只需要了解即可。