天天看点

深度解析dubbo集群路由之标签路由

本文基于dubbo v2.6.x

1.标签路由介绍

官方对于标签路由的解释(官方文档地址:链接):

一次 dubbo 调用能够根据请求携带的 tag 标签智能地选择对应 tag 的服务提供者进行调用。

这个标签路由可以理解为给应用或者是一次调用打标签,然后具有相同标签的会被访问到,如果没有相同标签的服务提供者,你只要没有设置 dubbo.force.tag为true也就是强制使用标签,它就会去标签为null的服务提供者,dubbo管这个操作叫服务降级。

其实这个标签路由是dubbo自动装配的,不需要咱们配置使用哪种路由,它会在setRouters时候自动给你放到路由列表中。我们来看下

深度解析dubbo集群路由之标签路由

我们来看下使用标签路由怎样配置:

服务提供者方:

注解:直接在@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不同,携带标签的请求可以降级访问到无标签的服务,但不携带标签/携带其他种类标签的请求永远无法访问到其他标签的服务。