Dubbo 提供了丰富的流量管控策略
服务发现保证调用方看到最新的提供方实例地址,服务发现机制依赖注册中心 (Zookeeper、Nacos、Istio 等) 实现。在消费端,Dubbo 提供了多种负载均衡策略,如随机负载均衡策略、一致性哈希负载、基于权重的轮询、最小活跃度优先、P2C 等。
Dubbo 的流量管控规则可以基于应用、服务、方法、参数等粒度精准的控制流量走向,根据请求的目标服务、方法以及请求体中的其他附加参数进行匹配,符合匹配条件的流量会进一步的按照特定规则转发到一个地址子集。流量管控规则有以下几种:
如果底层用的是基于 HTTP 的 RPC 协议 (如 REST、gRPC、Triple 等),则服务和方法等就统一映射为 HTTP 路径 (path),此时 Dubbo 路由规则相当于是基于 HTTP path 和 headers 的流量分发机制。
Dubbo 中有应用、服务和方法的概念,一个应用可以发布多个服务,一个服务包含多个可被调用的方法,从抽象的视角来看,一次 Dubbo 调用就是某个消费方应用发起了对某个提供方应用内的某个服务特定方法的调用,Dubbo 的流量管控规则可以基于应用、服务、方法、参数等粒度精准的控制流量走向。
以下是 Dubbo 单个路由器的工作过程,路由器接收一个服务的实例地址集合作为输入,基于请求上下文 (Request Context) 和 (Router Rule) 实际的路由规则定义对输入地址进行匹配,所有匹配成功的实例组成一个地址子集,最终地址子集作为输出结果继续交给下一个路由器或者负载均衡组件处理。
通常,在 Dubbo 中,多个路由器组成一条路由链共同协作,前一个路由器的输出作为另一个路由器的输入,经过层层路由规则筛选后,最终生成有效的地址集合。
标签路由通过将某一个服务的实例划分到不同的分组,约束具有特定标签的流量只能在指定分组中流转,不同分组为不同的流量场景服务,从而实现流量隔离的目的。标签路由可以作为蓝绿发布、灰度发布等场景能力的基础。
标签路由规则是一个非此即彼的流量隔离方案,也就是匹配标签
的请求会 100% 转发到有相同标签
的实例,没有匹配标签
的请求会 100% 转发到其余未匹配的实例。如果您需要按比例的流量调度方案,请参考示例 基于权重的按比例流量路由。
标签
主要是指对 Provider 端应用实例的分组,目前有两种方式可以完成实例分组,分别是动态规则打标
和静态规则打标
。动态规则打标
可以在运行时动态的圈住一组机器实例,而 静态规则打标
则需要实例重启后才能生效,其中,动态规则相较于静态规则优先级更高,而当两种规则同时存在且出现冲突时,将以动态规则为准。
静态打标需要在服务提供者实例启动前确定,并且必须通过特定的参数 tag
指定。
在 Dubbo 实例启动前,指定当前实例的标签,如部署在杭州区域的实例,指定 tag=gray
。
<dubbo:provider tag="gray"/>
or
<dubbo:service tag="gray"/>
or
java -jar xxx-provider.jar -Ddubbo.provider.tag=gray
发起调用的一方,在每次请求前通过 tag
设置流量标签,确保流量被调度到带有同样标签的服务提供方。
RpcContext.getContext().setAttachment(Constants.TAG_KEY, "gray");
相比于静态打标只能通过 tag
属性设置,且在启动阶段就已经固定下来,动态标签可以匹配任意多个属性,根据指定的匹配条件将 Provider 实例动态的划分到不同的流量分组中。
以下规则对 shop-detail
应用进行了动态归组,匹配 env: gray
的实例被划分到 gray
分组,其余不匹配 env: gray
继续留在默认分组 (无 tag)。
configVersion: v3.0
force: true
enabled: true
key: shop-detail
tags:
- name: gray
match:
- key: env
value:
exact: gray
这里牵涉到如何给您的实例打各种原始 label 的问题,即上面示例中的
env
,一种方式是直接写在配置文件中,如上面静态规则实例 provider 部分的配置所示,另一种方式是通过预设环境变量指定,关于这点请参考下文的 如何给实例打标 一节。
服务发起方的设置方式和之前静态打标规则保持一致,只需要在每次请求前通过 tag
设置流量标签,确保流量被调度到带有同样标签的服务提供方。
RpcContext.getContext().setAttachment(Constants.TAG_KEY, "Hangzhou");
设置了以上标签的流量,将全部导流到 hangzhou-region
划分的实例上。
请求标签的作用域仅为一次点对点的 RPC 请求。比如,在一个 A -> B -> C 调用链路上,如果 A -> B 调用通过
setAttachment
设置了tag
参数,则该参数不会在 B -> C 的调用中生效,同样的,在完成了 A -> B -> C 的整个调用同时 A 收到调用结果后,如果想要相同的tag
参数,则在发起其他调用前仍需要单独设置setAttachment
。可以参考 示例任务 - 环境隔离 了解更多tag
全链路传递解决方案。
条件路由与标签路由的工作模式非常相似,也是首先对请求中的参数进行匹配,符合匹配条件的请求将被转发到包含特定实例地址列表的子集。相比于标签路由,条件路由的匹配方式更灵活:
条件路由规则的主体 conditions
主要包含两部分内容:
匹配条件指定的参数
将与 消费者的请求上下文 (URL)、甚至方法参数
进行对比,当消费者满足匹配条件时,对该消费者执行后面的地址子集过滤规则。过滤条件指定的参数
将与 提供者实例地址 (URL)
进行对比,消费者最终只能拿到符合过滤条件的实例列表,从而确保流量只会发送到符合条件的地址子集。=> status != staging
application = product =>
基于以下示例规则,所有 org.apache.dubbo.demo.CommentService
服务调用都将被转发到与当前消费端机器具有相同 region
标记的地址子集。$region
是特殊引用符号,执行过程中将读取消费端机器的实际的 region
值替代。
configVersion: v3.0
enabled: true
force: false
key: org.apache.dubbo.samples.CommentService
conditions:
- '=> region = $region'
针对条件路由,我们通常推荐配置
scope: service
的规则,因为它可以跨消费端应用对所有消费特定服务 (service) 的应用生效。关于 Dubbo 规则中的scope
以及service
、application
的说明请阅读 条件路由规则手册。
条件路由规则还支持设置具体的机器地址如 ip 或 port,这种情况下使用条件路由可以处理一些开发或线上机器的临时状况,实现黑名单、白名单、实例临时摘除等运维效果,如以下规则可以将机器 172.22.3.91
从服务的可用列表中排除。
=> host != 172.22.3.91
条件路由还支持基于请求参数的匹配,示例如下:
conditions:
- method=getDetail&arguments[0]=dubbo => port=20880
通过 Dubbo 提供的动态配置规则,您可以动态的修改 Dubbo 服务进程的运行时行为,整个过程不需要重启,配置参数实时生效。基于这个强大的功能,基本上所有运行期参数都可以动态调整,比如超时时间、临时开启 Access Log、修改 Tracing 采样率、调整限流降级参数、负载均衡、线程池配置、日志等级、给机器实例动态打标签等。与上文讲到的流量管控规则类似,动态配置规则支持应用、服务两个粒度,也就是说您一次可以选择只调整应用中的某一个或几个服务的参数配置。
当然,出于系统稳定性、安全性的考量,有些特定的参数是不允许动态修改的,但除此之外,基本上所有参数都允许动态修改,很多强大的运行态能力都可以通过这个规则实现,您可以找个示例应用去尝试一下。通常 URL 地址中的参数均可以修改,这在每个语言实现的参考手册里也记录了一些更详细的说明。
以下示例将 org.apache.dubbo.samples.UserService
服务的超时参数调整为 2000ms
configVersion: v3.0
scope: service
key: org.apache.dubbo.samples.UserService
enabled: true
configs:
- side: provider
parameters:
timeout: 2000
以下部分指定这个配置是服务粒度,具体变更的服务名为 org.apache.dubbo.samples.UserService
。scope
支持 service
、application
两个可选值,如果 scope: service
,则 key
应该配置为 version/service:group
格式。
scope: service
key: org.apache.dubbo.samples.UserService
关于 Dubbo 规则中的
scope
以及service
、application
的说明请参考 动态配置参考手册 或 动态配置示例。
parameters
参数指定了新的修改值,这里将通过 timeout: 2000
将超时时间设置为 2000ms。
parameters:
timeout: 2000
脚本路由是最直观的路由方式,同时它也是当前最灵活的路由规则,因为你可以在脚本中定义任意的地址筛选规则。如果我们为某个服务定义一条脚本规则,则后续所有请求都会先执行一遍这个脚本,脚本过滤出来的地址即为请求允许发送到的、有效的地址集合。
configVersion: v3.0
key: demo-provider
type: javascript
enabled: true
script: |
(function route(invokers,invocation,context) {
var result = new java.util.ArrayList(invokers.size());
for (i = 0; i < invokers.size(); i ++) {
if ("10.20.3.3".equals(invokers.get(i).getUrl().getHost())) {
result.add(invokers.get(i));
}
}
return result;
} (invokers, invocation, context)); // 表示立即执行方法
当前,有两种方式可以在启动阶段为 Dubbo 实例指定标签,一种是之前提到的应用内配置的方式,如在 xml 文件中设置 <dubbo:provider tag="gray"/>
,应用打包部署后即自动被打标。
还有一种更灵活的方式,那就是通过读取所部署机器上的环境信息给应用打标,这样应用的标签就可以跟随实例动态的自动填充,避免每次更换部署环境就重新打包应用镜像的问题。当前 Dubbo 能自动读取以下环境变量配置:
spec:
containers:
- name: detail
image: apache/demo-detail:latest
env:
- name: DUBBO_LABELS
value: "region=hangzhou; env=gray"
spec:
containers:
- name: detail
image: apache/demo-detail:latest
env:
- name: DUBBO_ENV_KEYS
value: "REGION, ENV"
- name: REGION
value: "hangzhou"
- name: ENV
value: "gray"
如果您有不同的实例环境保存机制,可以通过扩展 InfraAdapter 扩展点
来自定义自己的标签加载方式。如果您的应用是部署在 Kubernetes 环境下,并且已经接入了服务网格体系,则也可以使用标准 deployment 标签的方式打标,具体请跟随 服务网格任务示例 学习。
Dubbo 提供了控制台 Dubbo Admin,帮助您可视化的下发流量管控规则,并实时监控规则生效情况。
Dubbo 还提供了 dubboctl
命令行工具,需要有 Dubbo Admin 提前部署就绪,因为 dubboctl 是通过与 Admin 进行 http 通信完成规则下发的。
如果您使用的是如 Istio 的服务网格架构,还可以使用 Istioctl、kubectl 等下发 Istio 标准规则。
以上介绍的都是 Dubbo 体系内的流量治理规则,如果您对服务网格架构感兴趣,则可以将 Dubbo 服务接入服务网格体系,这样,您就可以使用服务网格提供的流量治理能力,如 Istio 体系的 VirtualService 等。
具体请参见 Dubbo 中的服务网格架构。
我们搭建了一个 线上商城系统 供您学习流量规则的具体使用。