Dubbo

什么是Dubbo

了解 Dubbo 核心概念和架构 | Apache Dubbo 官方文档

Dubbo是阿里巴巴公司开源的一个高性能、轻量级的 Java RPC 框架

致力于提供高性能和透明化的 RPC 远程服务调用方案,以及 SOA (面向服务的)服务治理方案。

所以我们在学习的时候,通常是用到了springboot来当主体,来帮助我们创建对象然后包括在里面实现mybatis,dubbo等等中间件,那么springboot就利用它ioc和aop的特性方便我们注册对象,注册配置,那么mybatis就方便我们调用数据库,那么dubbo就方便我们进行远程调用,那么只是远程调用是不行的,所以还会在远程调用的基础上,增加服务注册 注册中心,服务熔断等等其他的技术,但是要知道dubbo主要用来实现我们远程调用的框架的

Dubbo的基本使用

之前我们知道rpc是远程调用的,那么dubbo就是方便我们进行远程调用的,但是我们不知道调用谁,在哪里调用,ip和端口号是什么,那么这时候就需要注册中心了

先来个例子体验一下:这个例子中没有使用注册中心

创建两个maven工程一个当server一个当client,利用quickstart模板,然后在server端起一个api模块去专门的存放接口,然后再创建另一个模块存放服务端应该有的服务

那么这时候在api里面编写接口,在server里面写具体的业务实现类实现api里面的接口,那么在同个工程不同模块里面如何在server端导入api的接口呢?利用pom里面的依赖可以导入api依赖从而实现。

image-20240815184427043

api里面定义接口

1
2
3
public interface IloginService {
String login(String username, String password);
}

然后在服务端写实现类,并且在服务端写好配置类,配置dubbo以及定义对外的服务名以及服务对应的实现类

image-20240815185629543

服务端的实现类

1
2
3
4
5
6
7
8
9
10
11
public class IloginServiceImpl implements IloginService{

@Override
public String login(String username, String password) {
//实现业务逻辑
if(username.equals("admin") && password.equals("admin")){
return "success";
}
return "failed";
}
}

server的配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
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
http://www.springframework.org/schema/context/spring-context.xsd">

<dubbo:application name="dubbo-server"/>
<!--配置注册中心-->
<dubbo:registry address="N/A" />
<dubbo:protocol name="dubbo" port="20880"/>
<!--定义服务-->
<dubbo:service interface="com.bitzh.dubbo.server.IloginService" ref="loginService" />
<!--找到实现-->
<bean id="loginService" class="com.bitzh.dubbo.server.IloginServiceImpl"/>

</beans>

然后再server的启动类启动dobbo的服务,启动服务端记住duboo的url以供客户端访问

1
2
3
4
5
6
7
8
public class App 
{
public static void main( String[] args )
{

Main.main(args);
}
}

这个是server的pom依赖

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
<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>
<parent>
<groupId>com.bitzh.server</groupId>
<artifactId>dubbo-server-demo</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>

<artifactId>dubbo-server</artifactId>
<packaging>jar</packaging>

<name>dubbo-server</name>
<url>http://maven.apache.org</url>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>2.7.8</version>
</dependency>
<dependency>
<groupId>com.bitzh.server</groupId>
<artifactId>dubbo-server-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

现在问题是不同工程如何进行通信呢?他是跨进程的

那么很显然第一件事情要在client端引入整个server端的依赖,把server工程打包然后再client中导入api依赖

执行install将项目的构建结果安装到本地 Maven 仓库中,以供其他项目使用

image-20240815190003381

然后在client端里面导入模块的api

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
<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.bitzh.dubbo.client</groupId>
<artifactId>dubbo-client-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>dubbo-client-demo</name>
<url>http://maven.apache.org</url>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>2.7.8</version>
</dependency>
<dependency>
<groupId>com.bitzh.server</groupId>
<artifactId>dubbo-server-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

然后再客户端中实现逻辑

然后我们要将两个工程进行通信利用Dubbo,那么两个客户端和服务端都要引入Dubbo的依赖(注意不是两个工程)

然后client端写dubbo配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
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
http://www.springframework.org/schema/context/spring-context.xsd">

<dubbo:application name="dubbo-client"/>
<dubbo:registry address="N/A" />
<dubbo:reference id="loginService"
interface="com.bitzh.dubbo.server.IloginService"
url="dubbo://192.168.10.1:20880/com.bitzh.dubbo.server.IloginService"/>


</beans>

client启动类

1
2
3
4
5
6
7
8
9
10
11
public class App 
{
public static void main( String[] args )
{
IloginService iloginService = null;
ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:/META-INF/spring/application.xml");
iloginService = ac.getBean(IloginService.class);

System.out.println( iloginService.login("admin", "admin") );
}
}

总结

服务端和客户端都必须要使用Dubbo框架,如果没有注册中心,那么需要在服务端写死调用的服务的地址,并且假设服务如果宕机了,或者切换地址了会非常麻烦,还需要频繁调整。

那么有了注册中心,不再是消费者直接知道ip而是先去询问注册中心,谁提供了这个服务,然后有三个地址,然后消费者就能从这三个地址中选一个调用。

工作流程:

1、首先服务启动,然后向注册中心注册,提供服务名,服务的ip地址和端口号

2、然后消费者启动,向注册中心订阅服务

3、然后注册中心返回当前所有可用的服务地址

4、然后消费者缓存地址列表,并且使用Dubbo的负载均衡策略选择一个节点调用

5、如果一个提供者宕机,注册中心通知消费者更新列表,自动剔除故障节点

Dubbo的系统概念和架构

image-20251021172334013

抽象的理解,Dubbo实际上分两层,一层是服务治理抽象控制面类似于Dubbo的大脑负责决策,另一个是Dubbo的数据面,负责执行决策。

那么Dubbo的服务智力控制面,是对整个Dubbo治理体系的抽象表达,控制面包含协调服务发现的 注册中心、流量管控策略,Dubbo Admin控制台等,如果采用了Service Mesh架构 还包含Istio等服务网格控制面

什么是Service Mesh(服务网格)是一种用于 管理服务间通信的基础设施层,他的目标是,将服务之间的通信,比如远程调用,监控等从应用程序中玻璃出来,交给一个独立的、透明的基础设施层来处理。没有这个服务网格,会有如下问题

问题 说明
代码侵入性强 所有通信逻辑都写在业务代码里,升级框架要改代码
多语言不友好 不同语言要实现同一套 SDK(Java 有 Dubbo,Go 怎么办?)
版本碎片化 不同服务用不同版本的 SDK,维护困难
运维复杂 每个服务都要配置监控、安全策略,难以统一管理

image-20251022010305315

Dubbo数据面

从Dubbo数据面角度来看,这个约束了微服务的调用规范,利用RPC通信协议实现

image-20251022010657195

Dubbo 作为服务开发框架包含的具体内容如下:

  • RPC 服务定义、开发范式。比如 Dubbo 支持通过 IDL 定义服务,也支持编程语言特有的服务开发定义方式,如通过 Java Interface 定义服务。
  • RPC 服务发布与调用 API。Dubbo 支持同步、异步、Reactive Streaming 等服务调用编程模式,还支持请求上下文 API、设置超时时间等。
  • 服务治理策略、流程与适配方式等。作为服务框架数据面,Dubbo 定义了服务地址发现、负载均衡策略、基于规则的流量路由、Metrics 指标采集等服务治理抽象,并适配到特定的产品实现。

通信协议

HTTP vs RPC 对比表

对比项 HTTP(REST) RPC(如 Dubbo)
本质 应用层协议 远程调用模式/框架
通信协议 HTTP/1.1 或 HTTP/2 自定义 TCP 协议 或 HTTP/2
数据格式 JSON / XML(文本) Hessian / Protobuf / Kryo(二进制)
性能 较低(文本解析+头部开销) 高(二进制+长连接)
可读性 高(人类可读) 低(需工具解析)
跨平台性 极强(任何设备都能调用) 一般(需相同框架或 SDK)
典型框架 Spring Web MVC, Flask, Express Dubbo, gRPC, Thrift
适用场景 前后端交互、开放 API 后端服务间高性能调用

与 gRPC、Spring Cloud、Istio 的关系

Dubbo 与 Spring Cloud

image-20251022014758847

维度 Spring Cloud Dubbo
定位 微服务“模式抽象”框架(理念先行) 企业级“整体解决方案”(实践驱动)
开发体验 ✅ 入门简单,Starter 丰富,文档完善 ✅ 原生支持 Spring,上手快 ✅ 完全兼容 Spring / Spring Boot ✅ 学习曲线略陡,但生产更省心
通信模式 🔹 基于 HTTP/REST(文本协议) 🔹 性能一般,跨语言受限 🔹 支持多种协议:   • TCP(Dubbo 协议,高性能)   • HTTP/2(Triple/gRPC)   • REST、JSON 等 🔹 支持多语言互通(Java、Go、Rust 等)
序列化 默认 JSON(文本,体积大、解析慢) 支持 Hessian2、Protobuf、JSON 等 🔹 二进制序列化性能更高
服务治理 ⚠️ 能力较弱 🔹 负载均衡、路由能力有限 🔹 缺乏细粒度流量控制 ✅ 强大的服务治理能力 🔹 权重调整、标签路由、条件路由 🔹 流量隔离、灰度发布、熔断限流
实现依赖 ❌ 无官方稳定实现 🔹 依赖 Netflix、Alibaba 等第三方套件 🔹 各实现质量参差不齐 ✅ 官方提供完整、稳定、高性能实现 🔹 经过阿里等超大规模生产验证
集群规模 ⚠️ 适合小到中等规模集群 🔹 地址推送、内存占用在大规模下易成瓶颈 ✅ 支持百万级实例规模 🔹 专为超大规模集群设计,水平扩展能力强
生产稳定性 ⚠️ 落地成本高 🔹 优雅停机、预热、双注册等问题需自行解决 ✅ 内置企业级特性: 🔹 优雅上下线、启动预热、延迟注册 🔹 多注册中心、分组隔离、集群容错
生态扩展 🔹 与 Spring 生态深度集成 🔹 涉足批处理、定时任务、数据访问等 🔹 专注微服务核心能力 🔹 可与 Spring Cloud 共存(互通) 🔹 支持 Service Mesh(Proxyless)
迁移成本 ❌ 小集群易上手,但规模增长后迁移困难 ✅ 从中小到超大规模平滑演进

Dubbo 与 gRPC

Dubbo 与 gRPC 最大的差异在于两者的定位上:

  • gRPC 定位为一款 RPC 框架,Google 推出它的核心目标是定义云原生时代的 rpc 通信规范与标准实现;
  • Dubbo 定位是一款微服务开发框架,它侧重解决微服务实践从服务定义、开发、通信到治理的问题,因此 Dubbo 同时提供了 RPC 通信、与应用开发框架的适配、服务治理等能力。

Dubbo 不绑定特定的通信协议,即 Dubbo 服务间可通过多种 RPC 协议通信并支持灵活切换。因此,你可以在 Dubbo 开发的微服务中选用 gRPC 通信,Dubbo 完全兼容 gRPC,并将 gRPC 设计为内置原生支持的协议之一

dubbo-grpc

因此,gRPC 更适合作为底层的通信协议规范或编解码包,而 Dubbo 则可用作微服务整体解决方案。对于 gRPC 协议,我们推荐的使用模式 Dubbo + gRPC 的组合,这个时候,gRPC 只是隐藏在底层的一个通信协议,不被微服务开发者感知,开发者基于 Dubbo 提供的 API 和配置开发服务,并基于 dubbo 的服务治理能力治理服务,在未来,开发者还能使用 Dubbo 生态和开源的 IDL 配套工具管理服务定义与发布。

Dubbo 与 Istio

Service Mesh 是近年来在云原生背景下诞生的一种微服务架构,在 Kubernetes 体系下,让微服务开发中的更多能力如流量拦截、服务治理等下沉并成为基础设施,让微服务开发、升级更轻量。Istio 是 Service Mesh 的开源代表实现,它从部署架构上分为数据面与控制面,从这一点上与 Dubbo 总体架构 是基本一致的,Istio 带来的主要变化在于:

  • 数据面,Istio 通过引入 Sidecar 实现了对服务流量的透明拦截,Sidecar 通常是与 Dubbo 等开发的传统微服务组件部署在一起
  • 控制面,将之前抽象的服务治理中心聚合为一个具有统一实现的具体组件,并实现了与底层基础设施如 Kubernetes 无缝适配

Dubbo 已经实现了对 Istio 体系的全面接入,可以用 Istio 控制面治理 Dubbo 服务,而在数据面部署架构上,针对 Sidecar 引入的复杂性与性能问题,Dubbo 还支持无代理的 Proxyless 模式。 除此之外,Dubbo Mesh 体系还解决了 Istio 架构落地过程中的很多问题,包括提供更灵活的数据面部署架构、更低的迁移成本等。

Dubbo-Mesh

数据面的视角,Dubbo 支持如下两种开发和部署模式,可以通过 Istio、Consul、Linkerd 等控制面组件实现对数据面服务的治理。

  • Proxy 模式,Dubbo 与 Envoy 一起部署,Dubbo 作为编程框架 & 协议通信组件存在,流量管控由 Envoy 与 Istio 控制面交互实现。
  • Proxyless 模式,Dubbo 进程保持独立部署,Dubbo 通过标准 xDS 协议直接接入 Istio 等控制面组件。

控制面视角,Dubbo 可接入原生 Istio 标准控制面和规则体系

Dubbo功能

微服务开发

Dubbo 解决企业微服务从开发、部署到治理运维的一系列挑战,Dubbo 为开发者提供从项目创建、开发测试,到部署、可视化监测、流量治理,再到生态集成的全套服务。

  • 开发层面,Dubbo 提供了 Java、Go、Rust、Node.js 等语言实现并定义了一套微服务开发范式,配套脚手架可用于快速创建微服务项目骨架
  • 部署层面,Dubbo 应用支持虚拟机、Docker 容器、Kubernetes、服务网格架构部署
  • 服务治理层面,Dubbo 提供了地址发现、负载均衡、流量管控等治理能力,官方还提供 Admin 可视化控制台、丰富的微服务生态集成

服务发现

Dubbo 提供的是一种 Client-Based 的服务发现机制,依赖第三方注册中心组件来协调服务发现过程,支持常用的注册中心如 Nacos、Consul、Zookeeper 等。

以下是 Dubbo 服务发现机制的基本工作原理图:

service-discovery

服务发现包含提供者、消费者和注册中心三个参与角色,其中,Dubbo 提供者实例注册 URL 地址到注册中心,注册中心负责对数据进行聚合,Dubbo 消费者从注册中心读取地址列表并订阅变更,每当地址列表发生变化,注册中心将最新的列表通知到所有订阅的消费者实例。…………………….0

  • 首先先,Dubbo 注册中心以应用粒度聚合实例数据,消费者按消费需求精准订阅,避免了大多数开源框架如 Istio、Spring Cloud 等全量订阅带来的性能瓶颈
  • 其次,Dubbo SDK 在实现上对消费端地址列表处理过程做了大量优化,地址通知增加了异步、缓存、bitmap 等多种解析优化,避免了地址更新常出现的消费端进程资源波动。
  • 最后,在功能丰富度和易用性上,服务发现除了同步 ip、port 等端点基本信息到消费者外,Dubbo 还将服务端的 RPC/HTTP 服务及其配置的元数据信息同步到消费端,这让消费者、提供者两端的更细粒度的协作成为可能,Dubbo 基于此机制提供了很多差异化的治理能力。

元数据就是描述数据的数据,一系列描述数据而已

高效的地址推送实现

从注册中心视角来看,它负责以应用名 (dubbo.application.name) 对整个集群的实例地址进行聚合,每个对外提供服务的实例将自身的应用名、实例ip:port 地址信息 (通常还包含少量的实例元数..据,如机器所在区域、环境等) 注册到注册中心。

每个消费服务的实例从注册中心订阅实例地址列表,相比于一些产品直接将注册中心的全量数据 (应用 + 实例地址) 加载到本地进程,Dubbo 实现了按需精准订阅地址信息。比如一个消费者应用依赖 app1、app2,则只会订阅 app1、app2 的地址列表更新,大幅减轻了冗余数据推送和解析的负担。

总结:高效的地址推送实现,就是说是以一个应用为颗粒度,比如整个用户中心这个应用,里面有很多的服务,然后调用的时候可以直接调用到对应的用户中心,而不会推送整个已经注册的应用

一个“应用”是指:一个独立部署、独立运行、拥有唯一身份标识(App Name)的微服务进程或服务单元。

它通常对应:

  • 一个可执行的 JAR 包
  • 一个 Spring Boot 服务
  • 一个 Kubernetes Pod
  • 一个独立的进程(Process)

丰富元数据配置

除了与注册中心的交互,Dubbo3 的完整地址发现过程还有一条额外的元数据通路,我们称之为元数据服务 (MetadataService),实例地址与元数据共同组成了消费者端有效的地址列表。

service-discovery

完整工作流程如上图所示,首先,消费者从注册中心接收到地址 (ip:port) 信息,然后与提供者建立连接并通过元数据服务读取到对端的元数据配置信息,两部分信息共同组装成 Dubbo 消费端有效的面向服务的地址列表。以上两个步骤都是在实际的 RPC 服务调用发生之前。

负载均衡

Dubbo 负载均衡核心要点总结

维度 说明
类型 客户端负载均衡(由 Consumer 决定调用哪个 Provider)
默认策略 Weighted Random LoadBalance(基于权重的随机)
目标 在集群环境下,合理分配请求,提升系统性能、稳定性和资源利用率
扩展性 支持自定义负载均衡策略,可通过 SPI 扩展

Dubbo 内置负载均衡策略对比表

算法 核心思想 适用场景 优点 缺点
Weighted Random (加权随机) 按权重分配随机概率 通用场景,默认策略 实现简单,调用量大时分布均匀 存在慢节点累积请求风险(“卡住”问题)
RoundRobin (加权轮询) 按权重循环调用 均匀分发请求 平滑加权轮询(Nginx 风格),避免调用集中 慢节点仍可能堆积请求
LeastActive (最少活跃优先) 活跃数越低越优先 (能者多劳) 性能差异大的集群 避免慢节点过载,快节点多处理 需要维护活跃数统计
Shortest-Response (最短响应优先) 响应时间越短越优先 关注响应速度的场景 快速节点获得更多流量 可能导致流量过度集中,小节点“雪崩”
ConsistentHash (一致性哈希) 相同参数 → 同一节点 有状态请求(如缓存、会话) 请求分布稳定,节点宕机影响小 需配置 hash.argumentshash.nodes
P2C (Power of Two Choices) 随机选两个,挑连接数小的 高并发、低延迟场景 简单高效,接近最优选择 比完全随机略复杂
Adaptive (自适应) 选择负载最小的节点 动态负载变化环境 智能调度,适应性强 依赖负载指标采集,实现较复杂

各算法核心机制详解

  1. Weighted Random(加权随机)
  • 原理:根据 Provider 权重设置调用概率。
  • 示例:A(权重3)、B(权重1) → A 被调用概率为 75%。
  • 注意:调用量越大,分布越均匀;但慢节点可能堆积请求

  1. RoundRobin(加权轮询)
  • 优化:采用 平滑加权轮询(类似 Nginx),避免调用集中。
  • 示例:A(3)、B(2)、C(1) → 调用顺序:A, B, A, C, A, B → 分布更均匀。
  • 优势:即使在短时间窗口内,流量也接近期望比例。

  1. LeastActive(最少活跃优先)
  • 活跃数 = 当前正在处理的请求数(请求发出 - 响应返回)。
  • 思想:处理快的节点活跃数低 → 获得更多请求(能者多劳)。
  • 优势:天然避免慢节点过载。

  1. Shortest-Response(最短响应优先)
  • 依据:过去 30 秒滑动窗口内的平均响应时间
  • 优点:响应快的节点获得更多流量。
  • 风险:可能导致“马太效应”,小节点被压垮。

  1. ConsistentHash(一致性哈希)
  • 特点

    • 相同参数 → 固定 Provider(如 getUser(123) 总是调用同一实例)。
    • 使用 160 个虚拟节点,减少节点变动时的影响。
  • 配置

    1
    2
    <dubbo:parameter key="hash.arguments" value="0,1"/> <!-- 对第0、1个参数哈希 -->
    <dubbo:parameter key="hash.nodes" value="320"/> <!-- 虚拟节点数 -->
  • 适用:缓存、会话保持等有状态场景。


  1. P2C(Power of Two Choices)
  • 步骤
    1. 随机选两个 Provider
    2. 比较“当前连接数”或“活跃数”
    3. 选择较小的那个
  • 优势:简单却高效,性能接近“最优选择”,但成本低。

  1. Adaptive(自适应负载均衡)
  • 思想:动态感知后端负载(如 CPU、load、响应时间),选择负载最小的节点。
  • 本质:P2C 的增强版,比较的是“系统负载”而非连接数。
  • 优势:真正实现“智能调度”,适应复杂环境。

如何选择合适的负载均衡策略?

业务需求 推荐策略
通用场景,无特殊要求 random(默认)
希望请求更均匀分布 roundrobin(平滑轮询)
节点性能差异大(有快有慢) leastactiveshortestresponse
有状态服务(如本地缓存) consistenthash
高并发、低延迟场景 p2c
动态负载变化大(如自动扩缩容) adaptive

流量管控

在理解之前要通过对比一下负载均衡和流量管控的区别

流量管控是控制谁能被选,负载均衡是选谁处理

负载均衡:当Consumer有多个Provider实例可选时,用什么策略选择一个实例。

流量管控:在负载均衡之前,现根据规则过滤或者路由可用的Provider列表

Dubbo的流量管控规则可以基于应用、服务、方法、参数等粒度精准的控制流量的走向。

流量管控规则有以下几种:

1、条件路由规则

2、标签路由规则

3、脚本路由规则

4、动态配置规则

具体一点的实现流程:客户端在调用远程服务时,发起的屎基于HTTP协议的rpc请求,,这个请求有URL和Header,然后根据这些来进行流量管控,过滤出一系列的服务列表最后再根据负载均衡来决定具体到了哪个服务中。

如果是非基于http协议的rpc调用需要自己实现一套规则来进行流量管控

Dubbo的Router

Router是实现服务地址动态筛选的核心组件,这个组件主要是解决服务该调用哪个实例的筛选问题,在分布式系统中,一个服务通常会部署多个实例,比如多机部署和多版本部署。Router的作用就是在调用的时候,根据业务需求,比如只调用v2版本的,黑名单实例不调用,等需求对实例地址进行过滤和排序。

总结:路由规则匹配的时候,依赖两个,一个是请求上下文,一个是设定的路由规则

请求上下文的意思是请求的实际信息是动态的,比如请求的时间,请求的region等

Dubbo内置了很多Router的实现, 条件路由、脚本路由、标签路由、应用路由,自定义路由

在使用路由的时候,绝大多数业务都能通过配置来实现,然后底层的功能,是根据配置,框架帮你已经实现好了,具体的标签如何配置可以看官方文档,如果需要了解框架如何实现的,需要继续看源码,可以工作需要的时候再详细看。

动态配置规则 | Apache Dubbo

实际应用中,是混合使用的,这些配置可以写在服务端也可以写在消费端,形成多层筛选,服务端先通过路由规则过滤掉绝对不允许暴露的实例,然后向注册中心推送筛选后的地址集。

消费端从注册中心获取地址集后,再通过本地路由规则进一步筛选,最终经过多层路由之后再传送给负载均衡的组件。

Dubbo的Sentinel

Dubbo集成Sentinel来实现限流和熔断,这个是在服务接收到的请求数量严重超过服务处理能力时,如大促期间的流量洪峰等场景,这种时候就需要处理这种超过处理能力的情况如何处理。

首先理解限流和熔断的区别

限流是控制进入系统的流量速率,避免超过承载能力,如果超出阈值会直接拒绝请求,当流量下降的时候自动恢复

熔断是当依赖的服务持续故障,暂时切断调用,避免故障扩散,转而去调用别的服务,经过熔断期后,尝试少量请求调用,若成功则恢复,否则继续熔断。

限流更关注负载过高的前期调整,熔断更关注持续负载过高后的应对。

难点在于确定服务器最大能承载的流量。总的分为静态评估和动态感知两个维度来决定

静态评估师基于预设值的固定限流

1、在测试环境压测获取基准值

在测试环境模拟真实流量,逐步增加请求压力,QPS从100加到100000,观察系统指标

关键指标:响应时间RT(如从100ms飙升到1s),CPU使用率是否超过80%,内存是否泄露、线程池是否满负荷

2、结合业务场景调整阈值

压测是理论值,需要结合线上实际场景打折,比如预留20%缓冲等等,非核心业务适当降低阈值,允许更高的响应时间,优先保障

动态感知基于系统负载的自适应限流Dubbo内置模式

这个模式无需预设阈值,而是实时监控系统的动态负载指标,自动计算当前可承载的流量上限

1、Dubbo会监控服务端的关键运行状态,作为负载压力的判断依据

硬件层面:CPU使用率,内存占用率,磁盘I/O,网络带宽

应用层面:线程池活跃线程数,队列等待长度,请求响应时间RT,错误率比如5xx的比例。

2、然后动态计算限流阈值

3、Dubbo会结合服务的集群规模和单实例负载动态调整。