本文基于dubbo v2.6.x
1.标签路由介绍
官方对于标签路由的解释(官方文档地址:链接):
一次 dubbo 调用能够根据请求携带的 tag 标签智能地选择对应 tag 的服务提供者进行调用。
这个标签路由可以理解为给应用或者是一次调用打标签,然后具有相同标签的会被访问到,如果没有相同标签的服务提供者,你只要没有设置 dubbo.force.tag为true也就是强制使用标签,它就会去标签为null的服务提供者,dubbo管这个操作叫服务降级。
其实这个标签路由是dubbo自动装配的,不需要咱们配置使用哪种路由,它会在setRouters时候自动给你放到路由列表中。我们来看下
我们来看下使用标签路由怎样配置:
服务提供者方:
注解:直接在@Service注解上配置tag 属性:
@Service(tag = "red")
xml:
服务调用者方:
请求标签的作用域为每一次 invocation,使用 attachment 来传递请求标签,注意保存在 attachment 中的值将会在一次完整的远程调用中持续传递,得益于这样的特性,我们只需要在起始调用时,通过一行代码的设置,达到标签的持续传递。
2. TagRouter源码解析
先看下TagRouter的class定义:
可以看到TagRouter 继承AbstractRouter ,这个就没啥好说的了,排序规则AbstractRouter 帮他完成了。
接着看下类成员与构造方法:
private static final int DEFAULT_PRIORITY = 100;
private static final URL ROUTER_URL = new URL("tag", Constants.ANYHOST_VALUE, 0, Constants.ANY_VALUE).addParameters(Constants.RUNTIME_KEY, "true");
public TagRouter() {
this.url = ROUTER_URL;
// 获取优先级
this.priority = url.getParameter(Constants.PRIORITY_KEY, DEFAULT_PRIORITY);
}
需要关注的是静态成员ROUTER_URL 添加了一个属性值,runtime=true,也就是支持运行时进行路由过滤。构造方法中就是赋值了一下路由url与获取了优先级priority,缺省是100。
最后我们来看下route方法
@Override
public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
// filter
List<Invoker<T>> result = new ArrayList<Invoker<T>>();
// Dynamic param
// 从上下文中获取dubbo.tag 扩展属性值标签
String tag = RpcContext.getContext().getAttachment(Constants.TAG_KEY);
// Tag request
if (!StringUtils.isEmpty(tag)) {// 如果tag标签不是空
// Select tag invokers first
for (Invoker<T> invoker : invokers) {// 与服务提供者invoker的tag 做对比
if (tag.equals(invoker.getUrl().getParameter(Constants.TAG_KEY))) {
result.add(invoker);// tag标签相等的 设置到结果列表中
}
}
}
/**
* request.tag=red 时优先选择 tag=red 的 provider。若集群中不存在与请求标记对应的服务,可以降级请求 tag=null 的 provider,即默认 provider。
*
* request.tag=null 时,只会匹配 tag=null 的 provider。即使集群中存在可用的服务,若 tag 不匹配就无法调用,这与规则1不同,携带标签的请求可以降级访问到无标签的服务,但不携带标签/携带其他种类标签的请求永远无法访问到其他标签的服务。
*/
// If Constants.REQUEST_TAG_KEY unspecified or no invoker be selected, downgrade to normal invokers
if (result.isEmpty()) {// 如果results 是空的话
// Only forceTag = true force match, otherwise downgrade
// 获取force标签 dubbo.force.tag 属性值
String forceTag = RpcContext.getContext().getAttachment(Constants.FORCE_USE_TAG);
// 如果forceTag 是空或者是forceTag
if (StringUtils.isEmpty(forceTag) || "false".equals(forceTag)) {
for (Invoker<T> invoker : invokers) {
//若集群中不存在与请求标记对应的服务,可以降级请求 tag=null 的 provider,即默认 provider。
if (StringUtils.isEmpty(invoker.getUrl().getParameter(Constants.TAG_KEY))) {
result.add(invoker);
}
}
}
}
return result;
}
在route方法中先是获取调用这dubbo.tag 属性值,如果这个属性值不是空的话,就循环遍历服务提供者列表,找到与服务调用者dubbo.tag 属性值相等的服务提供者添加到result集合中。
如果最后result集合没有服务提供者,就获取dubbo.force.tag 属性值(这个属性值表示是否强制使用tag),如果没有配置这个属性值或者是false,就将dubbo.tag 属性值 是null的服务提供者添加到result集合中,然后返回。
下面引用一段官网的解释(关于服务降级):
1.request.tag=red 时优先选择 tag=red 的 provider。若集群中不存在与请求标记对应的服务,可以降级请求 tag=null 的 provider,即默认 provider。
2.request.tag=null 时,只会匹配 tag=null 的 provider。即使集群中存在可用的服务,若 tag 不匹配就无法调用,这与规则1不同,携带标签的请求可以降级访问到无标签的服务,但不携带标签/携带其他种类标签的请求永远无法访问到其他标签的服务。