Dubbo的服务路由规则
Dubbo服务路由通过条件/标签规则,在负载均衡前筛选提供者,实现灰度发布、环境隔离等流量治理。
我们来详细解析一下 Dubbo 的服务路由规则。
服务路由(Service Routing)是 Dubbo 流量治理的核心功能之一。它的主要目的是在服务消费者(Consumer)发起调用时,从注册中心返回的所有服务提供者(Provider)地址列表中,根据预设的规则筛选出一部分符合条件的 Provider,然后再由负载均衡策略从这个子集中选择一个最终的 Provider 进行调用。
简单来说,路由规则决定了“哪些消费者可以调用哪些提供者”。
一、为什么需要服务路由?
服务路由解决了微服务架构中的许多关键问题,主要应用场景包括:
- 灰度发布 (Grayscale Release):当新版本服务上线时,可以先让一小部分流量(例如来自特定 IP、特定用户或内部员工的请求)访问新版本的 Provider,验证其稳定性,然后逐步扩大流量范围,最终实现全量上线。
- 多环境/多机房隔离 (Multi-environment/Data Center Isolation):在开发、测试、预发、生产等多个环境中,确保消费者的调用只发生在当前环境内,避免跨环境调用导致数据污染或服务异常。同理,可以实现同机房优先调用,减少网络延迟。
- 流量切换与容灾 (Traffic Shifting & Disaster Recovery):当某个机房或服务集群出现故障时,可以快速通过路由规则将流量切换到正常的机房或集群,实现服务容灾。
- 读写分离 (Read/Write Separation):对于某些服务,可以将写请求路由到主节点,读请求路由到从节点,实现读写分离。
- 为重要应用预留资源:将某些核心服务的 Provider 实例专门留给核心的 Consumer 应用调用,避免被非核心应用占用资源。
二、Dubbo 路由规则的分类(以 Dubbo 3.x 为例)
Dubbo 3.x 推荐使用基于 YAML 格式的路由规则,通过动态配置中心(如 Nacos, Zookeeper, Apollo)下发。主要分为以下两种:
1. 条件路由 (Conditional Routing)
这是最常用、最灵活的路由方式。它允许你根据调用上下文中的参数(如方法名、参数值、attachments)或者 Provider 的 URL 属性(如 IP 地址、端口、应用名)来定义路由规则。
核心要素:
scope: 规则的作用范围,可以是service(服务级别)或application(应用级别)。key: 规则的唯一标识,通常是服务名或应用名。conditions: 路由规则的核心,定义了匹配条件。它是一个when => then格式的表达式。when: 消费者匹配条件。如果消费者的上下文信息满足when条件,则此规则对其生效。then: 提供者匹配条件。消费者会从满足then条件的提供者列表中选择。
force: 是否强制执行。true表示如果筛选后没有可用的 Provider,则直接报错;false表示如果筛选后没有可用 Provider,则忽略此规则,使用所有 Provider。默认为true。
示例:灰度发布
假设有一个 com.example.UserService 服务,新版本 2.0.0 已经部署在一台机器 192.168.1.100 上。我们希望所有来自应用 frontend-app 的调用,都路由到这台新版本的机器上。
# 存储在配置中心:/dubbo/config/com.example.UserService/dubbo.routing.yml
scope: service
key: com.example.UserService
force: true
conditions:
# 规则1:来自 frontend-app 的消费者,只能调用 IP 为 192.168.1.100 的提供者
- when:
- consumer.application=frontend-app
then:
- provider.host=192.168.1.100
示例:同机房优先
假设有两个机房 hz 和 sh。我们希望来自 hz 机房的消费者优先调用 hz 机房的提供者。
# 存储在配置中心:/dubbo/config/dubbo/dubbo.routing.yml (应用级规则)
scope: application
key: my-consumer-app
force: false # force=false 很重要,如果同机房没有,可以跨机房调用
conditions:
# 规则1:如果消费者在 hz 机房,则优先调用 hz 机房的提供者
- when:
- consumer.meta[zone]=hz
then:
- provider.meta[zone]=hz
# 规则2:如果消费者在 sh 机房,则优先调用 sh 机房的提供者
- when:
- consumer.meta[zone]=sh
then:
- provider.meta[zone]=sh
这里的 meta 是 Dubbo 3 的元数据中心信息,也可以通过 provider/consumer 的其他属性来标识机房。
2. 标签路由 (Tag Routing)
标签路由是条件路由的一种简化和特例,专门用于灰度发布、蓝绿部署等场景。它通过给 Provider 打上不同的“标签”(如 gray, stable),消费者在调用时指定需要访问的标签,从而实现流量的精细化控制。
核心要素:
- Provider 启动时通过配置声明自己的标签。
- Consumer 在调用时通过
RpcContext动态指定要请求的标签。 - 也可以通过路由规则为某类 Consumer 静态地绑定到某个标签的 Provider。
配置方式:
第一步:Provider 端配置标签
# application.properties
dubbo.provider.tag=gray
第二步:Consumer 端调用
方式A:动态标签路由(推荐)
消费者在代码中动态指定本次调用要访问的标签。
// 本次调用将只会发送到标签为 "gray" 的 Provider
RpcContext.getContext().setAttachment("dubbo.tag", "gray");
userService.sayHello("world");
方式B:静态标签路由(通过规则配置)
为某个应用或 IP 的所有调用都路由到带特定标签的 Provider。
# 存储在配置中心:/dubbo/config/com.example.UserService/dubbo.tag-router.yml
scope: service
key: com.example.UserService
force: true
tags:
# 规则1: 将来自 10.0.0.1 的消费者的请求,路由到标签为 gray 的提供者
- name: gray
addresses: ["10.0.0.1"]
# 规则2: 其他所有请求,都路由到没有标签的提供者
- name: default
addresses: ["0.0.0.0/0"] # 匹配所有
三、路由规则的执行流程
- 获取 Provider 列表:Consumer 从注册中心获取所有可用的 Provider 地址列表。
- 执行路由规则链:Dubbo 会依次执行所有适用的路由规则(条件路由、标签路由等),对 Provider 列表进行层层过滤。
- AND 逻辑:一个 Provider 必须同时满足所有路由规则的条件,才能保留在最终的候选列表中。
- 负载均衡:路由规则执行完毕后,得到一个符合条件的 Provider 子集。
- 最终选择:负载均衡策略(如 Random, RoundRobin)从这个子集中选择一个 Provider 发起最终调用。
关键点:路由(Routing)发生在负载均衡(Load Balance)之前。 路由是筛选,负载均衡是选择。
四、Dubbo 2.7 与 3.x 的差异
配置格式:
- Dubbo 2.7:主要使用基于 URL 的格式,通过 Dubbo Admin 或直接写入 Zookeeper
/dubbo/服务名/routers节点。格式比较晦涩难懂,例如host = 10.20.153.10 => host = 10.20.153.11。还支持脚本路由(如 JavaScript),灵活性高但有性能和安全风险。 - Dubbo 3.x:全面拥抱云原生,推荐使用标准化的 YAML 格式。语义更清晰,结构化更好,更易于维护和理解。
- Dubbo 2.7:主要使用基于 URL 的格式,通过 Dubbo Admin 或直接写入 Zookeeper
概念:
- 核心思想(条件、标签)是类似的,但 3.x 的实现更加标准化和强大。
- 3.x 引入了
scope(service/application) 的概念,使得规则管理更加灵活。
总结
Dubbo 的服务路由规则是实现精细化流量治理的强大武器。
- 条件路由是最基础和灵活的规则,适用于各种复杂的场景。
- 标签路由是针对灰度发布等场景的简化版,使用起来更方便。
- 路由规则的核心作用是在负载均衡前对 Provider 列表进行过滤。
- 在 Dubbo 3.x 中,推荐使用 YAML 格式通过动态配置中心来管理路由规则,这已经成为现代微服务治理的最佳实践。