前言

大家好,明天开始给大家分享 — Dubbo 专题之 Dubbo 路由规定之标签路由。在前一个章节中咱们介绍了 Dubbo 路由规定之标签路由,以及咱们也例举了常见的应用场景并且进行了源码解析来剖析其实现原理,同时晓得 Dubbo 中标签路由其本质上是通过过滤器对服务提供者列表进行规定的匹配,如果匹配不上则过滤掉服务提供者。那接下来咱们解析探讨标签路由,什么是标签路由呢?有什么应用场景呢?上面就让咱们疾速开始吧!

1. 标签路由简介

首先小伙伴能够通过《Dubbo 路由规定之条件路由》回归一下什么是路由规定?上面咱们次要探讨什么标签路由:

上图中咱们能够看到有两个机房别离是机房A、机房B,其中机房 A 只能拜访到 Service A 和 Service B ,而机房B 只能拜访到 Service C 和 Service D。要实现下面这种场景咱们就须要用到标签路由。从机房 A 发动的调用携带标签 TAG_A 拜访到 Service A 和 Service B,而从机房 B 发动的调用携带 TAG_B Service C 和 Service D 。那什么是标签路由呢?

  • 标签路由:以服务提供者利用为粒度配置路由规定,通过将某一个或多个服务的提供者划分到同一个分组,束缚流量只在指定分组中流转,从而实现流量隔离的目标,能够作为蓝绿公布、灰度公布等场景的能力根底。标签次要是指对Provider端利用实例的分组,目前有两种形式能够实现实例分组,别离是动静规定打标动态规定打标,其中动静规定相较于动态规定优先级更高,而当两种规定同时存在且呈现抵触时,将以动静规定为准。

2. 应用形式

上面咱们简略的探讨下标签路由的应用形式:

2.1 标签路由

  • 动静规定打标,可随时在服务治理控制台下发标签归组规定

    # demo-provider利用减少了两个标签分组tag1和tag2# tag1蕴含一个实例 127.0.0.1:20880# tag2蕴含一个实例 127.0.0.1:20881---  force: false  runtime: true  enabled: true  key: demo-provider  tags:    - name: tag1      addresses: ["127.0.0.1:20880"]    - name: tag2    addresses: ["127.0.0.1:20881"]
  • 动态打标

    <dubbo:provider tag="tag1"/>

或者

 <dubbo:service tag="tag1"/>

或者

 java -jar xxx-provider.jar -Ddubbo.provider.tag={the tag you want, may come from OS ENV}
Tips:生产端通过编程的形式应用RpcContext.getContext().setAttachment(CommonConstants.TAG_KEY,"TAG_A")申请标签的作用域为每一次 invocation,应用 attachment 来传递申请标签,留神保留在 attachment 中的值将会在一次残缺的近程调用中继续传递,得益于这样的个性,咱们只须要在起始调用时,通过一行代码的设置,达到标签的继续传递。
  • 字段阐明:
编号字段名称阐明必填
1scope路由规定的作用粒度,scope的取值会决定key的取值。
service 服务粒度 application 利用粒度。
必填
2Key明确规定体作用在哪个接口服务或利用。 scope=service时,
key取值为[{group}:]{service}[:{version}]的组合 scope=application时,
key取值为application名称 。
必填
3enabledenabled=true 以后路由规定是否失效,,缺省失效。可不填
4forceforce=false 当路由后果为空时,是否强制执行,如果不强制执行,
路由后果为空的路由规定将主动生效,缺省为 false
可不填
5runtimeruntime=false 是否在每次调用时执行路由规定,
否则只在提供者地址列表变更时事后执行并缓存后果,
调用时间接从缓存中获取路由后果。如果用了参数路由,必须设为 true
须要留神设置会影响调用的性能,缺省为 false
可不填
6prioritypriority=1 路由规定的优先级,用于排序,优先级越大越靠前执行,缺省为 0可不填
7tags定义具体的标签分组内容,可定义任意n(n>=1)个标签并为每个标签指定实例列表。其中name为标签名称必填

2.2 降级约定

  • request.tag=tag1 时优先选择标记了tag=tag1provider。若集群中不存在与申请标记对应的服务,默认将降级申请 tag为空的provider;如果要扭转这种默认行为,即找不到匹配tag1provider返回异样,需设置request.tag.force=true
  • request.tag未设置时,只会匹配tag为空的provider。即便集群中存在可用的服务,若 tag 不匹配也就无奈调用,这与约定1不同,携带标签的申请能够降级拜访到无标签的服务,但不携带标签/携带其余品种标签的申请永远无法访问到其余标签的服务。
Tips: 2.6.x 版本以及更早的版本请应用老版本路由规定,自定义路由参考路由扩大

3. 应用场景

从下面的简略介绍咱们能够大抵理解到,标签路由通过将某一个或多个服务的提供者划分到同一个分组,束缚流量只在指定分组中流转,从而实现流量隔离的目标。咱们日常工作中罕用的场景有:蓝绿公布、灰度公布等场景的能力根底等。

4. 示例演示

咱们以获取图书列表为例进行实例演示,其中咱们会启动两个服务提供者配置两个端口:2088020881,而后别离指定两个服务标签为:TAG_ATAG_B。我的项目结构图如下:

这里咱们应用动静打标的形式所有 XML 中的配置维持以前案例的配置,咱们次要看看 Dubbo Admin 中的配置:

# demo-provider 利用减少了两个标签分组 TAG_A 和 TAG_B# TAG_A 蕴含一个实例 127.0.0.1:20880# TAG_B 蕴含一个实例 127.0.0.1:20881force: trueenabled: trueruntime: falsetags: - name: TAG_A   addresses: [192.168.0.1:20880] - name: TAG_B   addresses: [192.168.0.2:20881]

以上动静打标配置示意:当生产端指定标签为 TAG_A 时调用 127.0.0.1:20880 服务提供者,标签为 TAG_B 时调用 127.0.0.1:20881 服务。

Tips: 小伙伴通过在生产端动静切换标签TAG_ATAG_A来查看成果,服务端只需启动一个端口为20880的服务即可。

5. 实现原理

依据后面的介绍咱们晓得在生产端调用近程服务时通过路由规定进行服务的过滤,那么咱们通过源码简略的剖析下这个处理过程。这里咱们间接看到路由规定的调用外围代码`org.apache.dubbo.rpc.cluster.
RouterChain#route`外围办法如下:

    public List<Invoker<T>> route(URL url, Invocation invocation) {        List<Invoker<T>> finalInvokers = invokers;        for (Router router : routers) {            finalInvokers = router.route(finalInvokers, url, invocation);        }        return finalInvokers;    }

上面展现了咱们运行过程中的路由规定:

其中TagRouter就是咱们的标签路由外围代码如下:

/**     *     * 标签路由     *     * @author liyong     * @date 4:48 PM 2020/11/29     * @param invokers     * @param url     * @param invocation     * @exception     * @return java.util.List<org.apache.dubbo.rpc.Invoker<T>>     **/    @Override    public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {        if (CollectionUtils.isEmpty(invokers)) {            return invokers;        }        // 这里因为配置核心可能更新配置,所有应用另外一个常量援用(相似复制)        final TagRouterRule tagRouterRuleCopy = tagRouterRule;        //如果动静规定不存在或有效或没有激活,应用动态标签        if (tagRouterRuleCopy == null || !tagRouterRuleCopy.isValid() || !tagRouterRuleCopy.isEnabled()) {            //解决动态标签            return filterUsingStaticTag(invokers, url, invocation);        }        List<Invoker<T>> result = invokers;        //获取上下文中Attachment的标签参数,这个参数由客户端调用时候写入        String tag = StringUtils.isEmpty(invocation.getAttachment(TAG_KEY)) ? url.getParameter(TAG_KEY) :                invocation.getAttachment(TAG_KEY);        // 如果存在传递标签        if (StringUtils.isNotEmpty(tag)) {            //通过传递的标签找到动静配置对应的服务地址            List<String> addresses = tagRouterRuleCopy.getTagnameToAddresses().get(tag);            // 通过标签分组进行过滤            if (CollectionUtils.isNotEmpty(addresses)) {                //获取匹配地址的服务                result = filterInvoker(invokers, invoker -> addressMatches(invoker.getUrl(), addresses));                //如果返回后果不为null 或者 返回后果为空然而配置force=true也间接返回                if (CollectionUtils.isNotEmpty(result) || tagRouterRuleCopy.isForce()) {                    return result;                }            } else {                //检测动态标签                result = filterInvoker(invokers, invoker -> tag.equals(invoker.getUrl().getParameter(TAG_KEY)));            }            //如果提供者没有配置标签 默认force.tag = false 示意能够拜访任意的提供者 ,除非咱们显示的禁止            if (CollectionUtils.isNotEmpty(result) || isForceUseTag(invocation)) {                return result;            }            else {                //返回所有的提供者,不须要任意标签                List<Invoker<T>> tmp = filterInvoker(invokers, invoker -> addressNotMatches(invoker.getUrl(),                        tagRouterRuleCopy.getAddresses()));                //查找提供者标签为空                return filterInvoker(tmp, invoker -> StringUtils.isEmpty(invoker.getUrl().getParameter(TAG_KEY)));            }        } else {            //返回所有的 addresses            List<String> addresses = tagRouterRuleCopy.getAddresses();            if (CollectionUtils.isNotEmpty(addresses)) {                result = filterInvoker(invokers, invoker -> addressNotMatches(invoker.getUrl(), addresses));                // 1. all addresses are in dynamic tag group, return empty list.                if (CollectionUtils.isEmpty(result)) {                    return result;                }            }           //持续应用动态标签过滤            return filterInvoker(result, invoker -> {                String localTag = invoker.getUrl().getParameter(TAG_KEY);                return StringUtils.isEmpty(localTag) || !tagRouterRuleCopy.getTagNames().contains(localTag);            });        }    }

下面的代码中把次要的流程进行正文,请小伙伴自行进行代码调试查看。

6. 小结

在本大节中咱们次要学习了 Dubbo 中路由规定之标签路由以及应用形式。同时也剖析了标签路由规定实现的原理:如果生产端传递标签则和配置的动静规定和动态规定进行匹配,如果生产端未传递标签则应用服务提供端的本地配置的动态标签和动静配置标签进行匹配。

Tips: 动静规定相较于动态规定优先级更高,而当两种规定同时存在且呈现抵触时,将以动静规定为准。

本节课程的重点如下:

  1. 了解 Dubbo 标签路由
  2. 理解了标签路由应用形式
  3. 理解标签路由实现原理
  4. 理解标签路由应用场景

作者

集体从事金融行业,就任过易极付、思建科技、某网约车平台等重庆一流技术团队,目前就任于某银行负责对立领取零碎建设。本身对金融行业有强烈的喜好。同时也实际大数据、数据存储、自动化集成和部署、散布式微服务、响应式编程、人工智能等畛域。同时也热衷于技术分享创建公众号和博客站点对常识体系进行分享。关注公众号:青年IT男 获取最新技术文章推送!

博客地址: http://youngitman.tech

微信公众号: