暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Dubbox 2.8.4 的一种分布式开发调试方案

老吕架构 2021-05-14
773

一、需求

云服务产品,微服务架构,使用的Dubbo 框架。由于种种原因目前使用的版本是 Dubbox2.8.4 。业务开发人员的痛点:他们每个人的开发范围一般固定在某一个微服务内,但是如果想完成某个接口的测试、调试工作、联调工作,本机需要启动很多服务,效率低下,苦不堪言。

这个问题的现状是这样的:

1、dubbo里自带的group、version服务分组隔离方案是不能解决我现在面临的问题的;

2、有网友是从自定义ip黑白名单的角度来实现 路由的,同样也解决不了我现在面临的问题;

3、apache dubbo 2.7.X版本里面新增了 TagRoute  路由策略,但是我的dubbo版本里没有此路由特性,各种原因,不能立刻升级到新版本;

于是干脆手撕一个MyTagRouter路由策略吧

二、方案

利用Dubbo的SPI机制进行Router扩展,实现个性化路由。

1、provider端增加自定义参数mytag,暴露到url中,导出到注册中心,从而传递到订阅端的consumer;

2、consumer端通过自定义路由策略进行提供者集合过滤;

3、如果当前请求线程中携带了mytag变量,则寻找与存在mytag并且内容一致的provider;

4、如果当前请求线程中没有携带mytag变量,则寻找不存在mytag的provider;

5、如果当前请求携带了mytag,但是没有找到满足条件的provider,则降级到寻找不带有mytag的provider上;

6、无论如何都不会使用mytag内容不一致的provider;

7、最终可以实现自由穿梭,到达正确的provider;

三、实现

1、Router接口的实现

    public class MyRouter implements Router {
    private static final Logger logger = LoggerFactory.getLogger(MyRouter.class);
    private final URL url;


    public MyRouter(URL url){
    this.url = url;
    }
    @Override
    public URL getUrl() {
    return this.url;
    }


    @Override
    public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
    if (invokers != null && invokers.size() != 0) {
    try {
    //获取需要自定义路由的tag
    String mytag = TraceUtil.getMytag();
    logger.info("mytag:"+mytag);
    List<Invoker<T>> result = new ArrayList();
    while (true){
    Iterator iterator = invokers.iterator();
    while(iterator.hasNext()) {
    Invoker<T> invoker = (Invoker)iterator.next();
    URL invokerUrl = invoker.getUrl();
    //获取提供者携带的mytag参数
    String providerMytag = invokerUrl.getParameter("mytag");
    if(mytag==null){
    //排除所有debug提供者
    if (providerMytag==null) {
    result.add(invoker);
    }
    }else{
    //添加所有符合mytag内容一致的提供者
    if (providerMytag!=null&&providerMytag.equals(mytag)) {
    result.add(invoker);
    }
    }
    }
    if (result.size()==0&&mytag!=null) {
    //不存在mytag内容一致的debug服务,下面要走正常服务了
    mytag=null;
    }else{
    //存在mytag对应的debug服务
    break;
    }
    }
    return result;




    } catch (Throwable throwable) {
    logger.error("Failed to execute tag router rule: " + this.getUrl() + ", invokers: " + invokers + ", cause: " + throwable.getMessage(), throwable);
    }
    }


    return invokers;
    }


    @Override
    public int compareTo(Router o) {
    return 0;
    }
    }

    2、MyRouterFactory接口的实现

      public class MyRouterFactory implements RouterFactory {
      @Override
      public Router getRouter(URL url) {
      return new MyRouter(url);
      }
      }

      3、对dubbo服务进行mytag标签的设置,mytag默认是空的,在开发者机器上设置下这个环境变量,启动服务就会自动生成一个带mytag标签的服务,这样才能根据mytag准确路由过来

        <dubbo:provider>
        <dubbo:parameter key="mytag" value="${mytag:}"/>
        </dubbo:provider>

        4、META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.RouterFactory的配置

          myRouterFactory=com.xxx.dubbo.router.MyRouterFactory

          5、自定义路由策略生效设置

             <dubbo:registry address="xxx"  group="xxx" >
            <dubbo:parameter key="router" value="myRouterFactory" >
            </dubbo:registry>

            6、设置mytag的工具类(TraceUtil是一个使用ThreadLocal实现的工具类,用来进行mytag的传递)

              public class MytagUtil {
              public static void setMytag(){
              String mytag = System.getenv("mytag");
              RpcContext.getContext().setAttachment("mytag",mytag);
              TraceUtil.setMytag(mytag);
              }
              }

              7、mytag的传递,依赖dubbo的自定义过滤器(一个消费者过滤器,一个提供者过滤器),持续传递下去

              消费者过滤器ConsumingFilter

                @Override
                public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
                String mytag = TraceUtil.getMytag();
                RpcContext.getContext().setAttachment(DubboHelper.mytag, mytag);
                return invoker.invoke(invocation);
                }

                提供者过滤器ProvidingFilter

                  @Override
                  public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
                  String mytag = RpcContext.getContext().getAttachment(DubboHelper.mytag)
                  TraceUtil.setMytag(mytag);
                  return invoker.invoke(invocation);
                  }

                  四、总结

                  1、关键点梳理,新增自定义的mytag参数必须设置到<dubbo:provider>配置中,不能设置到 <dubbo:application>中,更不能设置到<dubbo:consumer>中,原因在于dubbo参数的优先级覆盖上(我开发的第1版中就是因为将参数设置到了dubbo:application中从而导致问题,最后使用不优雅的代码解决问题,虽然也实现了想要的功能,但是代码的实现不够优雅,根本原因在于参数的位置放错了)

                  2、此方案简单易用,目前已经在项目中开始广泛应用,非常好使^_^

                  3、使用步骤:

                  1)开发人员设置一个本机环境变量即可

                  2)注册中心统一使用dev服务器的注册中心

                  3)本地启动想要调试、测试的服务

                  4)在发起请求的地方带上mytag环境变量(如果从浏览器/postman进入网关进行测试,则需要在header或者url中增加mytag变量;如果直接绕过网关进行RPC方法调用,则在dubbo的消费者端使用MytagUtil工具进行mytag环境变量设置)


                  文章转载自老吕架构,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                  评论