3.微服务灰度发布落地实践(组件灰度增强)

文章目录

  • 前言
  • 调用链示图
    • dubbo服务之间的的调链
    • cloud 服务之间的调用链
  • 网关
  • servlet容器: 标签续传
    • 1.定义插件
    • 2.实现灰度增强拦截
  • 线程池: 标签续传
    • 1.拦截Runnable或Callable,接口增强实现标签续传;
      • Callable 插件定义
      • Runnable 插件定义
      • 拦载Callabl或Runnable构造(可共用)
      • 拦载run或call 方法(可共用)
    • 2.拦截ThreadPoolExecutor, 但是当业务使用Callable或Runnable 时,使用的是lambda表达式时,
      • ThreadPool插件定义
      • ThreadPool公共拦截类
      • execute方法拦截
      • submit方法拦截
  • spring-cloud 服务增强
    • 1. 灰度标签续传
      • Feign htttp 拦载插件定义
      • Feign灰度标签拦截器
      • RestTemplate http 拦截插件定义
      • RestTemplate http 拦截灰度标签续传
    • 2.ribbon灰度路由
      • Rule 插件定义
      • Rule 灰度路由规则拦截改写
  • dubbo服务增强
    • 1. 灰度标签续传
      • 服务消费方ContextFilter插件定义
      • 服务提供方ContextFilter插件定义
      • 服务消费方ContextFilter拦截
      • 服务提供方ContextFilter拦截
    • 2. 灰度路由规则拦截改写
      • 路由插件定义
      • 灰度路由规则拦截改写
  • nacos client增强
    • 插件定义
    • 实例uuid拦截上报
  • eureka client增强
    • 插件定义
    • 实例uuid拦截上报

前言

上一篇介绍,agent基础框架的实现,本篇主要介绍灰度标签在各种组件、协议之间续传,以及路由规则改写;从用户客户端发送请求,到用户收到后端响应信息,整个请求链路会经过各种组件,

调用链示图

dubbo服务之间的的调链

在这里插入图片描述

cloud 服务之间的调用链

在这里插入图片描述

上面展示的组件调用链为: 用户->网关->servlet容器服务->线程池->dubbo服务或cloud服务;这仅展示某一种调用路径,实际环境可能更复杂,有经过cloud或消息队列等等,不再一一列举。

网关

网关交互相对杂复一些,单独开篇

servlet容器: 标签续传

通常web容器服务,都会实现servlet,找到适合的组件接口,

  1. 从http请求头获取灰度标签,并设置到threadLocal,
  2. 请求处理完后,清除该信息;
  3. 分析发现比较合适对HandlerAdapter(不是唯一)进行拦截:

1.定义插件

      public class HandlerAdapterDefine extends ClassEnhancePluginDefine {
      private static final String CLASS_INTERCEPTOR = "com.dbcat.gray.agent.mvc.HandlerAdapterInterceptor";
      @Override
      protected ClassMatch enhanceClass() {
          return HierarchyMatch.byHierarchyMatch("org.springframework.web.servlet.HandlerAdapter");
      }
      @Override
      public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
          return new ConstructorInterceptPoint[0];
      }
      @Override
      public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
          return new InstanceMethodsInterceptPoint[] {
                  new InstanceMethodsInterceptPoint() {
                      @Override
                      public ElementMatcher<MethodDescription> getMethodsMatcher() {
                          return named("handle").and(takesArguments(3));
                      }
                      @Override
                      public String getMethodsInterceptor() {
                          return CLASS_INTERCEPTOR;
                      }
                      @Override
                      public boolean isOverrideArgs() {
                          return false;
                      }
                  }
          };
      }
  
      @Override
      public StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() {
          return new StaticMethodsInterceptPoint[0];
      }
  }

2.实现灰度增强拦截

public class HandlerAdapterInterceptor implements InInterceptor, InstanceMethodsAroundInterceptor {
  @Override
  public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, MethodInterceptResult result){
      String routingEnv = getRoutingEnv(allArguments);
      this.setContext(routingEnv);
      CounterManager.increaseConsume(ComponentType.MVC,routingEnv);
  }
  @Override
  public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Object ret) {
      //清除threadLocal里的数据
      this.removeContext();
      return ret;
  }
  private String getRoutingEnv(Object[] allArguments){
      //从http 请求头或Attribute 获取灰度标签设置到threadLocal
      Object request =  allArguments[0];
      Method getHeader = ReflectUtils.getMethod(request, "getHeader", String.class);
      String env = (String)ReflectUtils.invokeMethod(getHeader, request, X_ENV);
      if(env != null && !env.trim().equals("")){
          return env;
      }
      Method getAttribute = ReflectUtils.getMethod(request, "getAttribute", String.class);
      return  (String)ReflectUtils.invokeMethod(getAttribute, request, X_ENV);
  }
}

这里只是介绍Servlet容器,如果项目实际使用其它类型web容器,也可以用类似的方式对其增强

线程池: 标签续传

服务或消息的路由规则依赖灰度标签,如果业务代码内出现跨线程操作后,则会出现灰度标签断传,从而导致路由错误;针对该问题可以分别对以下线程相关接口或类进行标签续传增强处理:

1.拦截Runnable或Callable,接口增强实现标签续传;

Callable 插件定义

public class CallableDefine extends ClassEnhancePluginDefine {
    private static final String CALLABLE_CLASS = "java.util.concurrent.Callable";
    private static final String CALLABLE_CLASS_INTERCEPTOR = "com.dbcat.gray.agent.threading.ThreadingConstructorInterceptor";
    private static final String CALLABLE_CALL_METHOD_INTERCEPTOR = "com.dbcat.gray.agent.threading.ThreadingMethodInterceptor";
    @Override
    protected ClassMatch enhanceClass() {
        return new ThreadingMatch(CALLABLE_CLASS);
    }

    @Override
    public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
        return new ConstructorInterceptPoint[] {
            new ConstructorInterceptPoint() {
                @Override
                public ElementMatcher<MethodDescription> getConstructorMatcher() {
                    //拦截构造,切换线程前保存灰度标签到当前Callable实现上
                    return any();
                }
                @Override
                public String getConstructorInterceptor() {
                    return CALLABLE_CLASS_INTERCEPTOR;
                }
            }
        };
    }

    @Override
    public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[] {
            new InstanceMethodsInterceptPoint() {
                @Override
                public ElementMatcher<MethodDescription> getMethodsMatcher() {
                    //拦截call方法
                    return named("call").and(takesArguments(0));
                }

                @Override
                public String getMethodsInterceptor() {
                    return CALLABLE_CALL_METHOD_INTERCEPTOR;
                }

                @Override
                public boolean isOverrideArgs() {
                    return false;
                }
            }
        };
    }
}

Runnable 插件定义

public class RunnableDefine extends ClassEnhancePluginDefine {
    private static final String RUNNABLE_CLASS = "java.lang.Runnable";
    private static final String RUNNABLE_CLASS_INTERCEPTOR = "com.dbcat.gray.agent.threading.ThreadingConstructorInterceptor";
    private static final String RUNNABLE_RUN_METHOD_INTERCEPTOR = "com.dbcat.gray.agent.threading.ThreadingMethodInterceptor";
    @Override
    protected ClassMatch enhanceClass() {
        return new ThreadingMatch(RUNNABLE_CLASS);
    }
    @Override
    public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
        return new ConstructorInterceptPoint[] {
            new ConstructorInterceptPoint() {
                @Override
                public ElementMatcher<MethodDescription> getConstructorMatcher() {
                   //拦截构造,切换线程前保存灰度标签到当前Runnable实现上
                    return any();
                }
                @Override
                public String getConstructorInterceptor() {
                    return RUNNABLE_CLASS_INTERCEPTOR;
                }
            }
        };
    }
    @Override
    public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[] {
            new InstanceMethodsInterceptPoint() {
                @Override
                public ElementMatcher<MethodDescription> getMethodsMatcher() {
                    //拦载run方法
                    return named("run").and(takesArguments(0));
                }
                @Override
                public String getMethodsInterceptor() {
                    return RUNNABLE_RUN_METHOD_INTERCEPTOR;
                }

                @Override
                public boolean isOverrideArgs() {
                    return false;
                }
            }
        };
    }
}

拦载Callabl或Runnable构造(可共用)

public class ThreadingConstructorInterceptor implements InInterceptor, InstanceConstructorInterceptor {
    
    @Override
    public void onConstruct(final EnhancedInstance objInst, final Object[] allArguments) {
        //获取当前线程ThreadLocal里的灰度标签,并保存当前Callable或 Runnable 实现上
        String env = this.getContext(null);
        objInst.setGrayDynamicField(env);
    }
}

拦载run或call 方法(可共用)

public class ThreadingMethodInterceptor implements InInterceptor, InstanceMethodsAroundInterceptor {

    @Override
    public void beforeMethod(final EnhancedInstance objInst, final Method method, final Object[] allArguments,
                             final Class<?>[] argumentsTypes, final MethodInterceptResult result) {
        String xEnv = (String) objInst.getGrayDynamicField();
        this.setContext(xEnv);
    }

    @Override
    public Object afterMethod(final EnhancedInstance objInst, final Method method, final Object[] allArguments,
        final Class<?>[] argumentsTypes, final Object ret) {
        this.removeContext();
        return ret;
    }
}

2.拦截ThreadPoolExecutor, 但是当业务使用Callable或Runnable 时,使用的是lambda表达式时,

可以通过拦截ThreadPoolExecutor,增强实现标签续传

ThreadPool插件定义

需要拦截execute和submit方法

public class ThreadPoolExecutorDefine extends ClassInstanceMethodsEnhancePluginDefine {

    private static final String ENHANCE_CLASS = "java.util.concurrent.ThreadPoolExecutor";
    
    private static final String INTERCEPT_EXECUTE_METHOD_HANDLE = "com.dbcat.gray.agent.threading.ThreadPoolExecuteMethodInterceptor";

    private static final String INTERCEPT_SUBMIT_METHOD_HANDLE = "com.dbcat.gray.agent.threading.ThreadPoolSubmitMethodInterceptor";

    @Override
    public boolean isBootstrapInstrumentation() {
        return true;
    }

    @Override
    protected ClassMatch enhanceClass() {
        return LogicalMatchOperation.or(HierarchyMatch.byHierarchyMatch(ENHANCE_CLASS), MultiClassNameMatch.byMultiClassMatch(ENHANCE_CLASS));
    }

    @Override
    public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
        return new ConstructorInterceptPoint[0];
    }

    @Override
    public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[]{
                new InstanceMethodsInterceptPoint() {
                    @Override
                    public ElementMatcher<MethodDescription> getMethodsMatcher() {
                        return ElementMatchers.named("execute");
                    }
                    @Override
                    public String getMethodsInterceptor() {
                        return INTERCEPT_EXECUTE_METHOD_HANDLE;
                    }
                    @Override
                    public boolean isOverrideArgs() {
                        return true;
                    }
                },
                new InstanceMethodsInterceptPoint() {
                    @Override
                    public ElementMatcher<MethodDescription> getMethodsMatcher() {
                        return ElementMatchers.named("submit");
                    }

                    @Override
                    public String getMethodsInterceptor() {
                        return INTERCEPT_SUBMIT_METHOD_HANDLE;
                    }
                    @Override
                    public boolean isOverrideArgs() {
                        return true;
                    }
                }
        };
    }
}

ThreadPool公共拦截类

public abstract class AbstractThreadPoolInterceptor implements InstanceMethodsAroundInterceptor {

    @Override
    public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, MethodInterceptResult result) throws Throwable {
        if (notToEnhance(allArguments)) {
            return;
        }
        Object wrappedObject = wrap(allArguments[0]);
        if (wrappedObject != null) {
            allArguments[0] = wrappedObject;
        }
    }
    public abstract Object wrap(Object param);

    @Override
    public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Object ret) throws Throwable {
        return ret;
    }

    @Override
    public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Throwable t) {

    }

    private boolean notToEnhance(Object[] allArguments) {
        if (allArguments == null || allArguments.length < 1) {
            return true;
        }
        Object argument = allArguments[0];
        //如果已经被增强过,不必在增强了
        return argument instanceof EnhancedInstance ;
    }
}

execute方法拦截

public class ThreadPoolExecuteMethodInterceptor extends AbstractThreadPoolInterceptor {

    @Override
    public Object wrap(Object param) {
        if (param instanceof RunnableWrapper) {
            return null;
        }

        if (param instanceof RunnableFuture) {
            return null;
        }

        if (!(param instanceof Runnable)) {
            return null;
        }

        Runnable runnable = (Runnable) param;
        return new RunnableWrapper(runnable);
    }
}

submit方法拦截

public class ThreadPoolSubmitMethodInterceptor extends AbstractThreadPoolInterceptor {

    @Override
    public Object wrap(Object param) {
        if (param instanceof Callable) {
            Callable callable = (Callable) param;
            return new CallableWrapper(callable);
        }

        if (param instanceof Runnable) {
            Runnable runnable = (Runnable) param;
            return new RunnableWrapper(runnable);
        }
        return null;
    }
}

spring-cloud 服务增强

cloud 服务之间的通通http通信的,首先要解决灰度标签在服务之间传递,可以利用http请求头,携带灰度标签;其它次是路由问题,spring-cloud 的远程调用负载是由ribbon实现,只要据灰度标签修改ribbon的路由规则,则可以实现灰度服务路由。

1. 灰度标签续传

分析可能存在通过feign调用或LoadBalance的 RestTemplate 调用服务,所以需要这两种方式的调用进行拦截,通过http请头续传灰度标。

Feign htttp 拦载插件定义

public class FeignTargetPluginDefine extends ClassInstanceMethodsEnhancePluginDefine {
    private static final String ENHANCE_CLASS = "feign.Target";
    private static final String INTERCEPTOR_CLASS = "com.dbcat.gray.agent.cloud.FeignRequestContextInterceptor";
    @Override
    protected ClassMatch enhanceClass() {
        return HierarchyMatch.byHierarchyMatch(ENHANCE_CLASS);
    }

    @Override
    public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
        return new ConstructorInterceptPoint[0];
    }

    @Override
    public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[]{
                new InstanceMethodsInterceptPoint() {

                    @Override
                    public ElementMatcher<MethodDescription> getMethodsMatcher() {
                        return ElementMatchers.named("apply");
                    }
                    @Override
                    public String getMethodsInterceptor() {
                        return INTERCEPTOR_CLASS;
                    }
                }
        };
    }
}

Feign灰度标签拦截器

public class FeignContextInterceptor implements InstanceMethodsAroundInterceptor {

    @Override
    public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, MethodInterceptResult result) throws Throwable {
        RequestTemplate template = (RequestTemplate) allArguments[0];
        //续传灰度标签
        String routingEnv = (String) ServerContextHolder.getData(X_ENV);
        if (routingEnv != null && !routingEnv.trim().equals("")) {
            template.header(X_ENV, routingEnv);
        }
    }

}

RestTemplate http 拦截插件定义

public class ClientHttpRequestInterceptorPluginDefine extends ClassInstanceMethodsEnhancePluginDefine {

    private static final String ENHANCE_CLASS = "org.springframework.http.client.ClientHttpRequestInterceptor";

    private static final String INTERCEPTOR_CLASS = "com.dbcat.gray.agent.cloud.HttpRequestContextInterceptor";

    @Override
    protected ClassMatch enhanceClass() {
        return HierarchyMatch.byHierarchyMatch(ENHANCE_CLASS);
    }

    @Override
    public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
        return new ConstructorInterceptPoint[0];
    }

    @Override
    public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[]{
                new InstanceMethodsInterceptPoint() {

                    @Override
                    public ElementMatcher<MethodDescription> getMethodsMatcher() {
                        return ElementMatchers.named("intercept");
                    }

                    @Override
                    public String getMethodsInterceptor() {
                        return INTERCEPTOR_CLASS;
                    }
                }
        };
    }
}

RestTemplate http 拦截灰度标签续传

public class HttpRequestContextInterceptor implements InstanceMethodsAroundInterceptor {

    @Override
    public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, MethodInterceptResult result) throws Throwable {
        HttpRequest request = (HttpRequest) allArguments[0];
        String routingEnv = (String) ServerContextHolder.getData(X_ENV);
        if (routingEnv != null && !routingEnv.trim().equals("")) {
            request.getHeaders().set(X_ENV, routingEnv);
        }
    }

}

2.ribbon灰度路由

Rule 插件定义

public class RulePluginDefine extends ClassInstanceMethodsEnhancePluginDefine {
    private static final String ENHANCE_CLASS = "com.netflix.loadbalancer.AbstractLoadBalancerRule";
    private static final String INTERCEPTOR_CLASS = "com.dbcat.gray.agent.cloud.RuleInterceptor";

    @Override
    protected ClassMatch enhanceClass() {
        return HierarchyMatch.byHierarchyMatch(ENHANCE_CLASS);
    }

    @Override
    public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
        return new ConstructorInterceptPoint[0];
    }

    @Override
    public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[]{
                new InstanceMethodsInterceptPoint() {

                    @Override
                    public ElementMatcher<MethodDescription> getMethodsMatcher() {
                        return ElementMatchers.named("choose");
                    }

                    @Override
                    public String getMethodsInterceptor() {
                        return INTERCEPTOR_CLASS;
                    }
                }
        };
    }
}

Rule 灰度路由规则拦截改写

public class RuleInterceptor implements InstanceMethodsAroundInterceptor {
    @Override
    public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, MethodInterceptResult result) throws Throwable {
        ZoneAvoidanceRule rule = (ZoneAvoidanceRule) objInst;
        ILoadBalancer loadBalancer = rule.getLoadBalancer();
        List<Server> allServers = loadBalancer.getAllServers();
        if (allServers.isEmpty()) {
            result.defineReturnValue(null);
            return;
        }
        //据灰度标签,选择相应的服务
        String env = (String) ServerContextHolder.getData(GrayConstant.X_ENV);
        ServerSelector serverSelector = CloudServerSelector.build(env, allServers);
        List<Server> targetServers = serverSelector.selectServers();
        Server server = doChooseServer(targetServers, rule, allArguments);
        result.defineReturnValue(server);
    }

    private Server doChooseServer(List<Server> targetServers, ZoneAvoidanceRule rule, Object[] allArguments) {
        if (targetServers.isEmpty()) {
            return null;
        }
        Object loadBalancerKey = allArguments[0];
        Optional<Server> server = rule.getPredicate().chooseRoundRobinAfterFiltering(targetServers, loadBalancerKey);
        return server.isPresent() ? server.get() : null;
    }
}

dubbo服务增强

分析dubbo 源码发布,灰度标签的续传,只要改写ConsumerContextFilter和ContextFilter 两个filter,它们分别是消费的filter和服务提供方的filter;ConsumerContextFilter的作用是消费方上下文件信息通过 rpc的attachment传递给服务提供方;而ContextFilter的作用则是服务提供方将消息传递的上下文从attachment中取出;对灰度路由规则的改写,仅需拦载Router接口的实现即可。

1. 灰度标签续传

服务消费方ContextFilter插件定义


public class ConsumerConextFilterPluginDefine extends ClassInstanceMethodsEnhancePluginDefine {

    private static final String ENHANCE_CLASS = "org.apache.dubbo.rpc.filter.ConsumerContextFilter";

    private static final String INTERCEPTOR_CLASS = "com.dbcat.gray.agent.dubbo.ConsumerContextInterceptor";

    @Override
    protected ClassMatch enhanceClass() {
        return NameMatch.byName(ENHANCE_CLASS);
    }

    @Override
    public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
        return new ConstructorInterceptPoint[0];
    }

    @Override
    public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[]{
                new InstanceMethodsInterceptPoint() {
                    @Override
                    public ElementMatcher<MethodDescription> getMethodsMatcher() {
                        return ElementMatchers.named("invoke");
                    }

                    @Override
                    public String getMethodsInterceptor() {
                        return INTERCEPTOR_CLASS;
                    }
                }
        };
    }
}

服务提供方ContextFilter插件定义

public class ProviderContextFilterPluginDefine extends ClassInstanceMethodsEnhancePluginDefine {

    private static final String ENHANCE_CLASS = "org.apache.dubbo.rpc.filter.ContextFilter";

    private static final String INTERCEPTOR_CLASS = "com.dbcat.gray.agent.dubbo.ProviderContextInterceptor";

    @Override
    protected ClassMatch enhanceClass() {
        return NameMatch.byName(ENHANCE_CLASS);
    }

    @Override
    public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
        return new ConstructorInterceptPoint[0];
    }

    @Override
    public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[]{
                new InstanceMethodsInterceptPoint() {
                    @Override
                    public ElementMatcher<MethodDescription> getMethodsMatcher() {
                        return ElementMatchers.named("invoke");
                    }

                    @Override
                    public String getMethodsInterceptor() {
                        return INTERCEPTOR_CLASS;
                    }
                }
        };
    }
}

服务消费方ContextFilter拦截

public class ConsumerContextInterceptor implements InstanceMethodsAroundInterceptor {

    @Override
    public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, MethodInterceptResult result) {
        RpcContext context = RpcContext.getContext();
        String routingEnv = (String) ServerContextHolder.getData(X_ENV);
        CounterManager.increaseConsume(DUBBO, routingEnv);
        if (routingEnv != null && !routingEnv.trim().equals("")) {
            context.setAttachment(X_ENV, routingEnv);
        }
    }
}

服务提供方ContextFilter拦截

public class ProviderContextInterceptor implements InstanceMethodsAroundInterceptor {

    @Override
    public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, MethodInterceptResult result) throws Throwable {
        Invocation invocation = (Invocation) allArguments[1];
        String routingEnv = invocation.getAttachment(X_ENV);
        CounterManager.increasePublish(DUBBO, routingEnv);
        if (routingEnv != null && !routingEnv.trim().equals("")) {
            ServerContextHolder.setData(X_ENV, routingEnv);
        }
    }
}

2. 灰度路由规则拦截改写

路由插件定义

public class AppGrayRouterPluginDefine extends ClassInstanceMethodsEnhancePluginDefine {

    private static final String ENHANCE_CLASS = "org.apache.dubbo.rpc.cluster.router.condition.config.AppRouter";

    private static final String INTERCEPTOR_CLASS = "com.dbcat.gray.agent.dubbo.AppGrayRouterInterceptor";
    @Override
    protected ClassMatch enhanceClass() {
        return NameMatch.byName(ENHANCE_CLASS);
    }
    @Override
    public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
        return new ConstructorInterceptPoint[0];
    }

    @Override
    public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[]{
                new InstanceMethodsInterceptPoint() {
                    @Override
                    public ElementMatcher<MethodDescription> getMethodsMatcher() {
                        return ElementMatchers.named("route");
                    }

                    @Override
                    public String getMethodsInterceptor() {
                        return INTERCEPTOR_CLASS;
                    }
                }
        };
    }
}

灰度路由规则拦截改写

public class AppGrayRouterInterceptor implements InstanceMethodsAroundInterceptor {

    @Override
    public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, MethodInterceptResult result)  {
        List<Invoker> invokers = (List<Invoker>) allArguments[0];
        if (invokers.isEmpty()) {
            result.defineReturnValue(Collections.emptyList());
            return;
        }
        String env = (String) ServerContextHolder.getData(GrayConstant.X_ENV);
        DubboServerSelector selector = DubboServerSelector.build(env, invokers);
        List<Invoker> targetInvokers = selector.selectServers();
        result.defineReturnValue(targetInvokers);
    }
}

nacos client增强

为了方便 注册中心与灰度管理方及应用实例,统一识别某一个实例,应用在启动时,会动态生产一个实例id, 分别上报到注册中或和灰度发布管理管,所以,如果使用nacos作为注册中心,需在应用启动时,作为元数据上报到注册中收。

插件定义

public class NacosDiscoveryPropertiesPluginDefine extends ClassInstanceMethodsEnhancePluginDefine {

    private static final String ENHANCE_CLASS = "com.alibaba.cloud.nacos.NacosDiscoveryProperties";

    private static final String INTERCEPTOR_CLASS = "com.dbcat.gray.agent.cloud.NacosDiscoveryPropertiesInterceptor";

    @Override
    protected ClassMatch enhanceClass() {
        return NameMatch.byName(ENHANCE_CLASS);
    }


    @Override
    public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
        return new ConstructorInterceptPoint[]{
                new ConstructorInterceptPoint() {
                    @Override
                    public ElementMatcher<MethodDescription> getConstructorMatcher() {
                        return ElementMatchers.any();
                    }

                    @Override
                    public String getConstructorInterceptor() {
                        return INTERCEPTOR_CLASS;
                    }
                }
        };
    }
    @Override
    public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[0];
    }
}

实例uuid拦截上报

public class NacosDiscoveryPropertiesInterceptor implements InstanceConstructorInterceptor {
    
    @Override
    public void onConstruct(EnhancedInstance objInst, Object[] allArguments) throws Throwable {
        ServerInstance instance = ServerInstance.getInstance();
        NacosDiscoveryProperties properties = (NacosDiscoveryProperties) objInst;
        Map<String, String> metadata = properties.getMetadata();
        metadata.put(GrayConstant.INSTANCE_UUID, instance.getUuid());
        metadata.put(GrayConstant.APP_NAME,instance.getAppName());
    }
}

eureka client增强

原因同上

插件定义

public class InstanceInfoPluginDefine extends ClassInstanceMethodsEnhancePluginDefine {

    private static final String ENHANCE_CLASS = "com.netflix.appinfo.InstanceInfo";

    private static final String INTERCEPTOR_CLASS = "com.dbcat.gray.agent.cloud.InstanceInfoInterceptor";

    @Override
    protected ClassMatch enhanceClass() {
        return NameMatch.byName(ENHANCE_CLASS);
    }


    @Override
    public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
        return new ConstructorInterceptPoint[]{
                new ConstructorInterceptPoint() {
                    @Override
                    public ElementMatcher<MethodDescription> getConstructorMatcher() {
                        return ElementMatchers.any();
                    }

                    @Override
                    public String getConstructorInterceptor() {
                        return INTERCEPTOR_CLASS;
                    }
                }
        };
    }
    @Override
    public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[0];
    }
}

实例uuid拦截上报

public class InstanceInfoInterceptor implements InstanceConstructorInterceptor {
    
    @Override
    public void onConstruct(EnhancedInstance objInst, Object[] allArguments) throws Throwable {
        ServerInstance instance = ServerInstance.getInstance();
        InstanceInfo instanceInfo = (InstanceInfo) objInst;
        Map<String, String> metadata = instanceInfo.getMetadata();
        metadata.put(GrayConstant.INSTANCE_UUID, instance.getUuid());
        metadata.put(APP_NAME,instance.getAppName());
    }
}

未完,待续…

给大家安利一款mysql监控软件: 安装方便,消耗低,可视化,傻瓜式操作,可以监控慢日志详情、cpu、内存、连接数、tps 等信息
体验演示
下载地址

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/944499.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

不修改内核镜像的情况下,使用内核模块实现“及时”的调度时间片超时事件上报

一、背景 之前的博客 不修改内核镜像的情况下&#xff0c;使用内核模块实现高效监控调度时延-CSDN博客 里&#xff0c;我们讲了不修改内核镜像高效监控每次的调度时延的方法。这篇博客里&#xff0c;我们对于调度时间片也做这么一个不修改内核镜像的改进。关于调度时间片过长的…

机器视觉中的单线程、多线程与跨线程:原理与应用解析

在机器视觉应用中&#xff0c;程序的运行效率直接影响到系统的实时性和稳定性。随着任务复杂度的提高&#xff0c;单线程处理往往无法满足高性能需求&#xff0c;多线程技术因此被广泛应用。此外&#xff0c;跨线程操作&#xff08;如在多线程中更新界面或共享资源&#xff09;…

喜报 | 擎创科技入围上海市优秀信创解决方案

近日&#xff0c;由上海市经信委组织的“2024年上海市优秀信创解决方案”征集遴选活动圆满落幕&#xff0c;擎创科技凭借实践经验优秀的《擎创夏洛克智能预警与应急处置解决方案》成功入选“2024年上海市优秀信创解决方案”名单。 为激发创新活力&#xff0c;发挥标杆作用&…

linux-21 目录管理(一)mkdir命令,创建空目录

对linux而言&#xff0c;对一个系统管理来讲&#xff0c;最关键的还是文件管理。那所以我们接下来就来看看如何实现文件管理。当然&#xff0c;在文件管理之前&#xff0c;我们说过&#xff0c;文件通常都放在目录下&#xff0c;对吧&#xff1f;所以先了解目录&#xff0c;可能…

【Redis】 数据淘汰策略

面试官询问缓存过多而内存有限时内存被占满的处理办法&#xff0c;引出 Redis 数据淘汰策略。 数据淘汰策略与数据过期策略不同&#xff0c; 过期策略针对设置过期时间的 key 删除&#xff0c; 淘汰策略是在内存不够时按规则删除内存数据。 八种数据淘汰策略介绍 no evision&…

一网多平面

“一网多平面”是一种网络架构概念&#xff0c;具体指的是在一张物理网络之上&#xff0c;逻辑划分出“1N”个平面。以下是对“一网多平面”的详细解释&#xff1a; 定义与构成 01一网多平面 指的是在统一的物理网络基础设施上&#xff0c;通过逻辑划分形成多个独立的网络平面…

小程序配置文件 —— 12 全局配置 - pages配置

全局配置 - pages配置 在根目录下的 app.json 文件中有一个 pages 字段&#xff0c;这里我们介绍一下 pages 字段的具体用法&#xff1b; pages 字段&#xff1a;用来指定小程序由哪些页面组成&#xff0c;用来让小程序知道由哪些页面组成以及页面定义在哪个目录&#xff0c;…

MyBatis如何处理延迟加载?

大家好&#xff0c;我是锋哥。今天分享关于【MyBatis如何处理延迟加载&#xff1f;】面试题。希望对大家有帮助&#xff1b; MyBatis如何处理延迟加载&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 MyBatis 支持 延迟加载&#xff08;Lazy Loading&am…

Quartz任务调度框架实现任务动态执行

说明&#xff1a;之前使用Quartz&#xff0c;都是写好Job&#xff0c;指定一个时间点&#xff0c;到点执行。最近有个需求&#xff0c;需要根据前端用户设置的时间点去执行&#xff0c;也就是说任务执行的时间点是动态变化的。本文介绍如何用Quartz任务调度框架实现任务动态执行…

vue3使用video-player实现视频播放(可拖动视频窗口、调整大小)

1.安装video-player npm install video.js videojs-player/vue --save在main.js中配置全局引入 // 导入视频播放组件 import VueVideoPlayer from videojs-player/vue import video.js/dist/video-js.cssconst app createApp(App) // 视频播放组件 app.use(VueVideoPlayer)2…

从入门到精通:Vim 高效文本编辑全面指南

文章目录 前言&#x1f9ec;一、Vim 的编辑哲学&#xff1a;模式分离与高效键盘操作&#x1f9ec;二、基础命令与快捷键&#xff1a;从简单到熟悉&#x1f9ec;三、进阶功能&#xff1a;多文件、分屏与可视化模式&#x1f9ec;四、自定义配置与 .vimrc&#xff1a;打造你的专属…

正则表达式(三剑客之sed)

1.sed工具的使用 1.1 sed工具 1&#xff09;命令格式&#xff1a;sed -n ‘n’ p filename 1.2 打印某行 1&#xff09;打印第二行 [rootlocalhost ~]# sed -n 2p /etc/passwd 2&#xff09;第二行重复打印 [rootlocalhost ~]# sed 2p /etc/passwd 3&#xff09;所有行全部…

珞珈一号夜光遥感数据地理配准,栅格数据地理配准

目录 一、夜光数据下载&#xff1a; 二、夜光遥感数据地理配准 三、计算夜光数据值 四、辐射定标 五、以表格显示分区统计 五、结果验证 夜光数据位置和路网位置不匹配&#xff0c;虽然都是WGS84坐标系&#xff0c;不匹配&#xff01;&#xff01;&#xff01;不要看到就直接…

虚幻引擎是什么?

Unreal Engine&#xff0c;是一款由Epic Games开发的游戏引擎。该引擎主要是为了开发第一人称射击游戏而设计&#xff0c;但现在已经被成功地应用于开发模拟游戏、恐怖游戏、角色扮演游戏等多种不同类型的游戏。虚幻引擎除了被用于开发游戏&#xff0c;现在也用于电影的虚拟制片…

多个微服务 Mybatis 过程中出现了Invalid bound statement (not found)的特殊问题

针对多个微服务的场景&#xff0c;记录一下这个特殊问题&#xff1a; 如果启动类上用了这个MapperScan注解 在resource 目录下必须建相同的 com.demo.biz.mapper 目录结构&#xff0c;否则会加载不到XML资源文件 。 并且切记是com/demo/biz 这样的格式创建&#xff0c;不要使用…

使用Excel制作通达信自定义外部数据,安排!!!

Excel相信大家电脑上都有这个工具&#xff0c;相比敲编程代码&#xff0c;用这个去做自定义数据对大多数人&#xff0c;应该是比较友好的。自定义数据分为外部序列数据&#xff0c;看了一下内容理解起来比较多&#xff0c;分两期给大家介绍。为了照顾电脑基础薄弱的朋友&#x…

win10、win11-鼠标右键还原、暂停更新

系统优化 win 10jihuo win 11jihuo鼠标右键还原暂停更新 update 2024.12.28win 10 jihuo winx&#xff0c;打开powershell管理员&#xff0c;输入以下命令,选择1并等待 irm https://get.activated.win | iex参考&#xff1a;https://www.bilibili.com/video/BV1TN411M72J/?sp…

QT集成IntelRealSense双目摄像头2,集成OpenGL

上一篇文章写了如何把IntelRealSense摄像头的SDK集成到QT项目&#xff0c;并成功采集数据&#xff0c;在没有用OpenCV的情况下完成色彩数据&#xff0c;以及深度数据的显示。 具体地址&#xff1a;https://blog.csdn.net/qujia121qu/article/details/144734163 本次主要写如何…

数据分析的分类和EDIT思维框架

为了服务于企业不同层次的决策&#xff0c;商业数据分析过程需要提供相应的数据科学产出物。 一般而言&#xff0c;数据分析需要经历从需求层、数据层、分析层到输出层四个阶段。 第一个阶段是需求层——确定目标&#xff0c;具体目标需要依据具体的层次进行分析&#xff1a…

面试场景题系列:设计URL短链

1.场景需求界定 1.缩短URL&#xff1a;提供一个长URL&#xff0c;返回一个短很多的URL。 2.重定向URL&#xff1a;提供一个缩短了的URL&#xff0c;重定向到原URL。 3.高可用、可扩展性和容错性考量。 •写操作&#xff1a;每天生成1亿个URL。 •每秒的写操作数&#xff1a…