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

news2025/1/5 6:35:27

文章目录

  • 前言
  • 调用链示图
    • 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 等信息
体验演示
下载地址

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

UE5 丧尸类杂兵的简单AI

A、思路 1、关卡初始化时&#xff0c;自动产生随机巡逻点&#xff0c;小兵到达后&#xff0c;去另一个随机巡逻点。 2、加入视力&#xff0c;发现主角后&#xff0c;不再巡逻&#xff0c;而开始追击主角并攻击。条件循环。 3、加入听力。主角的奔跑与射击会产生噪音&#xf…

数据库管理-第275期 Oracle 23ai:画了两张架构图(20241225)

数据库管理275期 2024-12-25 数据库管理-第275期 Oracle 23ai&#xff1a;画了两张架构图&#xff08;20241225&#xff09;1 系统管理分片2 用户定义分片总结 数据库管理-第275期 Oracle 23ai&#xff1a;画了两张架构图&#xff08;20241225&#xff09; 作者&#xff1a;胖…

【代码分析】Unet-Pytorch

1&#xff1a;unet_parts.py 主要包含&#xff1a; 【1】double conv&#xff0c;双层卷积 【2】down&#xff0c;下采样 【3】up&#xff0c;上采样 【4】out conv&#xff0c;输出卷积 """ Parts of the U-Net model """import torch im…

小程序基础 —— 08 文件和目录结构

文件和目录结构 一个完整的小程序项目由两部分组成&#xff1a;主体文件、页面文件&#xff1a; 主体文件&#xff1a;全局文件&#xff0c;能够作用于整个小程序&#xff0c;影响小程序的每个页面&#xff0c;主体文件必须放到项目的根目录下&#xff1b; 主体文件由三部分组…

Maven 测试和单元测试介绍

一、测试介绍 二、单元测试 1&#xff09;介绍 2&#xff09;快速入门 添加依赖 <dependencies><!-- junit依赖 --><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter</artifactId><version>5.9…

大数据技术-Hadoop(一)Hadoop集群的安装与配置

目录 一、准备工作 1、安装jdk&#xff08;每个节点都执行&#xff09; 2、修改主机配置 &#xff08;每个节点都执行&#xff09; 3、配置ssh无密登录 &#xff08;每个节点都执行&#xff09; 二、安装Hadoop&#xff08;每个节点都执行&#xff09; 三、集群启动配置&a…

android sqlite 数据库简单封装示例(java)

sqlite 数据库简单封装示例&#xff0c;使用记事本数据库表进行示例。 首先继承SQLiteOpenHelper 使用sql语句进行创建一张表。 public class noteDBHelper extends SQLiteOpenHelper {public noteDBHelper(Context context, String name, SQLiteDatabase.CursorFactory fact…

【CSS in Depth 2 精译_095】16.3:深入理解 CSS 动画(animation)的性能

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第五部分 添加动效 ✔️【第 16 章 变换】 ✔️ 16.1 旋转、平移、缩放与倾斜 16.1.1 变换原点的更改16.1.2 多重变换的设置16.1.3 单个变换属性的设置 16.2 变换在动效中的应用 16.2.1 放大图标&am…

yii2 手动添加 phpoffice\phpexcel

1.下载地址&#xff1a;https://github.com/PHPOffice/PHPExcel 2.解压并修改文件名为phpexcel 在yii项目的vendor目录下创建一个文件夹命名为phpoffice 把phpexcel目录放到phpoffic文件夹下 查看vendor\phpoffice\phpexcel目录下会看到这些文件 3.到vendor\composer目录下…

如何通过采购管理系统提升供应链协同效率?

供应链是企业运营的命脉&#xff0c;任何环节的延迟或失误都会对企业造成严重影响。在采购环节中&#xff0c;如何保证与供应商的协同效率&#xff0c;避免因信息不对称而导致的决策失误&#xff0c;是企业面临的一大挑战。采购管理系统作为数字化供应链管理的重要工具&#xf…

yolov5 yolov6 yolov7 yolov8 yolov9目标检测、目标分类 目标切割 性能对比

文章目录 YOLOv1-YOLOv8之间的对比如下表所示&#xff1a;一、YOLO算法的核心思想1. YOLO系列算法的步骤2. Backbone、Neck和Head 二、YOLO系列的算法1.1 模型介绍1.2 网络结构1.3 实现细节1.4 性能表现 2. YOLOv2&#xff08;2016&#xff09;2.1 改进部分2.2 网络结构 3. YOL…

Vue项目如何设置多个静态文件;如何自定义静态文件目录

Vite实现方案 安装插件 npm i vite-plugin-static-copy在vite.config.ts引入 import { viteStaticCopy } from vite-plugin-static-copy配置 plugins: [viteStaticCopy({targets: [{src: "要设置的静态文件目录的相对路径 相对于vite.config.ts的", dest: ./, // …

学习threejs,THREE.RingGeometry 二维平面圆环几何体

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.RingGeometry 圆环几…

【畅购商城】详情页模块之评论

目录 接口 分析 后端实现&#xff1a;JavaBean 后端实现 前端实现 接口 GET http://localhost:10010/web-service/comments/spu/2?current1&size2 { "code": 20000, "message": "查询成功", "data": { "impressions&q…

04.HTTPS的实现原理-HTTPS的混合加密流程

04.HTTPS的实现原理-HTTPS的混合加密流程 简介1. 非对称加密与对称加密2. 非对称加密的工作流程3. 对称加密的工作流程4. HTTPS的加密流程总结 简介 主要讲述了HTTPS的加密流程&#xff0c;包括非对称加密和对称加密两个阶段。首先&#xff0c;客户端向服务器发送请求&#xf…

【每日学点鸿蒙知识】数据迁移、大量图片存放、原生自定义键盘调用、APP包安装到测试机、photoPicker顶部高度

1、迁移&#xff08;克隆&#xff09;手机中经过 ArkData &#xff08;方舟数据管理&#xff09;服务持久化后的数据&#xff1f; 在用户手动迁移&#xff08;克隆&#xff09;手机数据至另一台设备后&#xff0c;使用 ArkData &#xff08;方舟数据管理&#xff09;服务持久化…

Centos 7.6 安装mysql 5.7

卸载mysql 之前服务器上一直是mysql8&#xff0c;因为不经常使用&#xff0c;而且8的内存占用还挺高的&#xff0c;所以想降低到5.7&#xff0c;腾出点运行内存 停止服务 # 查询服务的状态 systemctl status mysqld # 停止服务 systemctl stop mysqld随后再次查询状态 查询…

大数据学习之Redis 缓存数据库二,Scala分布式语言一

一.Redis 缓存数据库二 26.Redis数据安全_AOF持久化机制 27.Redis数据安全_企业中该如何选择持久化机制 28.Redis集群_主从复制概念 29.Redis集群_主从复制搭建 30.Redis集群_主从复制原理剖析 31.Redis集群_哨兵监控概述 32.Redis集群_配置哨兵监控 33.Redis集群_哨兵监控原理…

Elasticsearch:使用 Ollama 和 Go 开发 RAG 应用程序

作者&#xff1a;来自 Elastic Gustavo Llermaly 使用 Ollama 通过 Go 创建 RAG 应用程序来利用本地模型。 关于各种开放模型&#xff0c;有很多话要说。其中一些被称为 Mixtral 系列&#xff0c;各种规模都有&#xff0c;而一种可能不太为人所知的是 openbiollm&#xff0c;这…

【日常开发】Git Stash使用技巧

文章目录 引言一、git stash 基础命令&#xff08;一&#xff09;存储当前工作区的修改&#xff08;二&#xff09;查看存储列表 二、查看存储的内容&#xff08;一&#xff09;查看特定存储的详细内容&#xff08;二&#xff09;查看特定存储修改的文件列表 三、恢复存储的修改…