服务路由
1、服务路由是什么?
服务路由包含一条路由规则,路由规则决定了服务消费者的调用目标,即规定了服务消费者可调用哪些服务提供者。Dubbo 目前提供了三种服务路由实现,分别为条件路由 ConditionRouter、脚本路由 ScriptRouter 和标签路由 TagRouter。其中条件路由是我们最常使用的
2、条件路由
条件路由规则将符合特定条件的请求转发到特定的地址实例子集上。规则首先对发起流量的请求参数进行匹配,符合匹配条件的请求将被转发到包含特定实例地址列表的子集。
条件路由的格式:
[服务消费者匹配条件] => [服务提供者匹配条件]
host = 10.20.150.30 => host = 10.20.150.31
该条规则表示 IP 为 10.20.150.30 的服务消费者只可调用 IP 为 10.20.150.31 机器上的服务
当然,服务消费者匹配条件为空,表示不对消费者进行限制
服务提供者为空,表示对指定的消费者禁用服务
高频的路由配置:
白名单:
host != 10.20.30.140.50,10.20.30.140.51 =>
不是(10.20.30.140.50,10.20.30.140.51)的不可以访问
黑名单:
host != 10.20.30.140.50,10.20.30.140.51 =>
是(10.20.30.140.50,10.20.30.140.51)的不可以访问
读写分离:
method = fifind,list,get,is => host = 172.22.3.94,172.22.3.95,172.22.3.96
是(fifind , list , get , is)的访问ip为(172.22.3.94,172.22.3.95,172.22.3.96)的服务端
method != fifind,list,get,is => host = 172.22.3.97,172.22.3.98
不是(fifind , list , get , is)的访问ip为(172.22.3.94,172.22.3.95,172.22.3.96)的服务端
前后台分离:
application = front => host = 172.22.3.91,172.22.3.92,172.22.3.93
是(front【前端】)的访问ip为(172.22.3.91,172.22.3.92,172.22.3.93)的服务端
application != front => host = 172.22.3.94,172.22.3.95,172.22.3.96
意思:应用不是(front【前端】)的访问ip为(172.22.3.91,172.22.3.92,172.22.3.93)的服务端
ConditionRule
条件路由规则主体。定义路由规则生效的目标服务或应用、流量过滤条件以及一些特定场景下的行为。
字段名 | 类型 | 描述 | 必填 |
---|---|---|---|
configVersion | string | 条件路由的版本,当前版本为 v3.0 | 是 |
scope | string | 支持 service 和 application 两种规则 | 是 |
key | string | 应用到的目标服务或应用程序的标识符 - 当 scope:service 时, key 应该是该规则生效的服务名比如 org.apache.dubbo.samples.CommentService - 当 scope:application 时, then key 应该是该规则应该生效的应用名称,比如说my-dubbo-service. | 是 |
enabled | bool | 规则是否生效 当 enabled:false 时,规则不生效 | 是 |
conditions | string[] | 配置中定义的条件规则,详情可以看条件规则 | 是 |
force | bool | T路由后实例子集为空时的行为。 true 则抛出一个No Provider Exception。 false 则忽略规则,直接去请求其他的实例。默认值是false | 否 |
runtime | bool | 是否为每个 rpc 调用运行路由规则或使用路由缓存(如果可用)。默认值是false(false则走缓存,true不走缓存) | 否 |
Condition
Condition
为条件路由规则的主体,类型为一个复合结构的 string 字符串,如 method=getComment => region=Hangzhou
。其中,
-
=> 之前的为请求参数匹配条件,指定的
匹配条件指定的参数
将与消费者的请求上下文 (URL)、甚至方法参数
进行对比,当消费者满足匹配条件时,对该消费者执行后面的地址子集过滤规则。 -
=> 之后的为地址子集过滤条件,指定的
过滤条件指定的参数
将与
提供者实例地址 (URL)
进行对比,消费者最终只能拿到符合过滤条件的实例列表,从而确保流量只会发送到符合条件的地址子集。
- 如果匹配条件为空,表示对所有请求生效,如:
=> status != staging
- 如果过滤条件为空,表示禁止来自相应请求的访问,如:
application = product =>
- 如果匹配条件为空,表示对所有请求生效,如:
匹配/过滤条件
参数支持
- 服务调用上下文,如:interface, method, group, version 等
- 请求上下文,如 attachments[key] = value
- 方法参数,如 arguments[0] = tom
- URL 本身的字段,如:protocol, host, port 等
- URL 上任务扩展参数,如:application, organization 等
- 支持开发者自定义扩展
条件支持
- 等号 = 表示 “匹配”,如:method = getComment
- 不等号 != 表示 “不匹配”,如:method != getComment
值支持
- 以逗号 , 分隔多个值,如:host != 10.20.153.10,10.20.153.11
- 以星号 * 结尾,表示通配,如:host != 10.20.*
- 以美元符 $ 开头,表示引用消费者参数,如:region = $region
- 整数值范围,如:userId = 1~100、userId = 101~
- 支持开发者自定义扩展
3、标签路由
标签路由通过将某一个服务的实例划分到不同的分组,约束具有特定标签的流量只能在指定分组中流转,不同分组为不同的流量场景服务,从而达到实现流量隔离的目的,可以作为蓝绿发布、灰度发布等场景能力的基础。
目前有两种方式可以对实例打标,分别是动态规则打标
和静态规则打标
。动态规则打标
可以在运行时动态的圈住一组机器实例,而 静态规则打标
则需要实例重启后才能生效,其中,动态规则相较于静态规则优先级更高,而当两种规则同时存在且出现冲突时,将以动态规则为准。
标签路由是一套严格隔离的流量体系,对于同一个应用而言,一旦打了标签则这部分地址子集就被隔离出来,只有带有对应标签的请求流量可以访问这个地址子集,这部分地址不再接收没有标签或者具有不同标签的流量。
举个例子,如果我们将一个应用进行打标,打标后划分为 tag-a、tag-b、无 tag 三个地址子集,则访问这个应用的流量,要么路由到 tag-a (当请求上下文 dubbo.tag=tag-a),要么路由到 tag-b (dubbo.tag=tag-b),或者路由到无 tag 的地址子集 (dubbo.tag 未设置),不会出现混调的情况。
标签路由的作用域是提供者应用,消费者应用无需配置标签路由规则。一个提供者应用内的所有服务只能有一条分组规则,不会有服务 A 使用一条路由规则、服务 B 使用另一条路由规则的情况出现。以下条件路由示例,在 shop-detail
应用中圈出了一个隔离环境 gray
,gray
环境包含所有带有 env=gray
标识的机器实例。
configVersion: v3.0
force: true
enabled: true
key: shop-detail
tags:
- name: gray
match:
- key: env
value:
exact: gray
假设有一个微服务应用,包含两个版本的服务实例:v1和v2。现在我们希望通过标签路由来控制流量,将一部分流量路由到v1,另一部分流量路由到v2。
首先,在启动v1和v2的实例时,我们可以给它们分别打上静态的标签,如下:
- v1实例:打上标签tag-a
- v2实例:打上标签tag-b
然后,我们可以配置一个规则,使得带有特定标签的请求流量可以路由到相应的实例。这可以通过配置中心或其他方式进行配置。例如,我们定义以下规则:
- 请求上下文中的dubbo.tag=tag-a,则将流量路由到v1实例。
- 请求上下文中的dubbo.tag=tag-b,则将流量路由到v2实例。
- 请求上下文中没有dubbo.tag的标签,则流量会路由到默认的地址子集。
现在,如果有100个请求,其中80个请求的dubbo.tag=tag-a,20个请求的dubbo.tag=tag-b,那么标签路由将根据标签规则将80%的流量路由到v1实例,20%的流量路由到v2实例。
通过这种方式,我们可以实现简单的蓝绿发布或灰度发布。我们可以在v1实例上进行新功能的测试和验证,同时保留v2实例作为备份或者线上版本。当我们确认新功能稳定可靠后,可以通过调整标签路由的规则,逐渐增加流量到v2实例,最终完成版本的切换。
这只是一个简单的例子,实际上,标签路由可以根据具体的需求和场景来定义更多的规则,并且可以结合权重、负载均衡等策略来更精确地控制流量的分发和管理。
TagRule
标签路由规则主体。定义路由规则生效的目标应用、标签分类规则以及一些特定场景下的行为。
Field | Type | Description | Required |
---|---|---|---|
configVersion | string | The version of the tag rule definition, currently available version is v3.0 | Yes |
key | string | The identifier of the target application that this rule is about to control | Yes |
enabled | bool | Whether enable this rule or not, set enabled:false to disable this rule. | Yes |
tags | Tag[] | The tag definition of this rule. | Yes |
force | bool | The behaviour when the instance subset is empty after routing. true means return no provider exception while false means fallback to subset without any tags. | No |
runtime | bool | Whether run routing rule for every rpc invocation or use routing cache if available. | No |
Tag
标签定义,根据 match
条件筛选出一部分地址子集。
Field | Type | Description | Required |
---|---|---|---|
name | string | The name of the tag used to match the dubbo.tag value in the request context. | Yes |
match | MatchCondition | A set of criterion to be met for instances to be classified as member of this tag. | No |
MatchCondition
定义实例过滤条件,根据 Dubbo URL 地址中的特定参数进行过滤。
Field | Type | Description | Required |
---|---|---|---|
key | string | The name of the key in the Dubbo url address. | Yes |
value | StringMatch (oneof) | The matching condition for the value in the Dubbo url address. | Yes |
4、脚本路由
脚本路由为流量管理提供了最大的灵活性,所有流量在执行负载均衡选址之前,都会动态的执行一遍规则脚本,根据脚本执行的结果确定可用的地址子集。
脚本路由只对消费者生效且只支持应用粒度管理,因此, key
必须设置为消费者应用名;脚本语法支持多种,以 Dubbo Java SDK 为例,脚本语法支持 Javascript、Groovy、Kotlin 等,具体可参见每个语言实现的限制。
脚本路由由于可以动态加载远端代码执行,因此存在潜在的安全隐患,在启用脚本路由前,一定要确保脚本规则在安全沙箱内运行。
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)); // 表示立即执行方法
ScriptRule
脚本路由规则主体。定义脚本规则生效的目标消费者应用、流量过滤脚本以及一些特定场景下的行为。
Field | Type | Description | Required |
---|---|---|---|
configVersion | string | The version of the script rule definition, currently available version is v3.0 | Yes |
key | string | The identifier of the target application that this rule is about to apply to. | Yes |
type | string | The script language used to define script . | Yes |
enabled | bool | Whether enable this rule or not, set enabled:false to disable this rule. | Yes |
script | string | The script definition used to filter dubbo provider instances. | Yes |
force | bool | The behaviour when the instance subset is empty after after routing. true means return no provider exception while false means ignore this rule. | No |
Script
script
为脚本路由规则的主体,类型为一个具有符合结构的 string 字符串,具体取决于 type
指定的脚本语言。
以下是 type: javascript
的一个脚本规则示例:
(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)); // 表示立即执行方法