天天看點

深度解析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不同,攜帶标簽的請求可以降級通路到無标簽的服務,但不攜帶标簽/攜帶其他種類标簽的請求永遠無法通路到其他标簽的服務。