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,找到适合的组件接口,
- 从http请求头获取灰度标签,并设置到threadLocal,
- 请求处理完后,清除该信息;
- 分析发现比较合适对HandlerAdapter(不是唯一)进行拦截:
1.定义插件
public class HandlerAdapterDefine extends ClassEnhancePluginDefine {private static final String CLASS_INTERCEPTOR = "com.dbcat.gray.agent.mvc.HandlerAdapterInterceptor";@Overrideprotected ClassMatch enhanceClass() {return HierarchyMatch.byHierarchyMatch("org.springframework.web.servlet.HandlerAdapter");}@Overridepublic ConstructorInterceptPoint[] getConstructorsInterceptPoints() {return new ConstructorInterceptPoint[0];}@Overridepublic InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {return new InstanceMethodsInterceptPoint[] {new InstanceMethodsInterceptPoint() {@Overridepublic ElementMatcher<MethodDescription> getMethodsMatcher() {return named("handle").and(takesArguments(3));}@Overridepublic String getMethodsInterceptor() {return CLASS_INTERCEPTOR;}@Overridepublic boolean isOverrideArgs() {return false;}}};}@Overridepublic StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() {return new StaticMethodsInterceptPoint[0];}}
2.实现灰度增强拦截
public class HandlerAdapterInterceptor implements InInterceptor, InstanceMethodsAroundInterceptor {@Overridepublic void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, MethodInterceptResult result){String routingEnv = getRoutingEnv(allArguments);this.setContext(routingEnv);CounterManager.increaseConsume(ComponentType.MVC,routingEnv);}@Overridepublic Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Object ret) {//清除threadLocal里的数据this.removeContext();return ret;}private String getRoutingEnv(Object[] allArguments){//从http 请求头或Attribute 获取灰度标签设置到threadLocalObject 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";@Overrideprotected ClassMatch enhanceClass() {return new ThreadingMatch(CALLABLE_CLASS);}@Overridepublic ConstructorInterceptPoint[] getConstructorsInterceptPoints() {return new ConstructorInterceptPoint[] {new ConstructorInterceptPoint() {@Overridepublic ElementMatcher<MethodDescription> getConstructorMatcher() {//拦截构造,切换线程前保存灰度标签到当前Callable实现上return any();}@Overridepublic String getConstructorInterceptor() {return CALLABLE_CLASS_INTERCEPTOR;}}};}@Overridepublic InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {return new InstanceMethodsInterceptPoint[] {new InstanceMethodsInterceptPoint() {@Overridepublic ElementMatcher<MethodDescription> getMethodsMatcher() {//拦截call方法return named("call").and(takesArguments(0));}@Overridepublic String getMethodsInterceptor() {return CALLABLE_CALL_METHOD_INTERCEPTOR;}@Overridepublic 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";@Overrideprotected ClassMatch enhanceClass() {return new ThreadingMatch(RUNNABLE_CLASS);}@Overridepublic ConstructorInterceptPoint[] getConstructorsInterceptPoints() {return new ConstructorInterceptPoint[] {new ConstructorInterceptPoint() {@Overridepublic ElementMatcher<MethodDescription> getConstructorMatcher() {//拦截构造,切换线程前保存灰度标签到当前Runnable实现上return any();}@Overridepublic String getConstructorInterceptor() {return RUNNABLE_CLASS_INTERCEPTOR;}}};}@Overridepublic InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {return new InstanceMethodsInterceptPoint[] {new InstanceMethodsInterceptPoint() {@Overridepublic ElementMatcher<MethodDescription> getMethodsMatcher() {//拦载run方法return named("run").and(takesArguments(0));}@Overridepublic String getMethodsInterceptor() {return RUNNABLE_RUN_METHOD_INTERCEPTOR;}@Overridepublic boolean isOverrideArgs() {return false;}}};}
}
拦载Callabl或Runnable构造(可共用)
public class ThreadingConstructorInterceptor implements InInterceptor, InstanceConstructorInterceptor {@Overridepublic 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 {@Overridepublic 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);}@Overridepublic 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";@Overridepublic boolean isBootstrapInstrumentation() {return true;}@Overrideprotected ClassMatch enhanceClass() {return LogicalMatchOperation.or(HierarchyMatch.byHierarchyMatch(ENHANCE_CLASS), MultiClassNameMatch.byMultiClassMatch(ENHANCE_CLASS));}@Overridepublic ConstructorInterceptPoint[] getConstructorsInterceptPoints() {return new ConstructorInterceptPoint[0];}@Overridepublic InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {return new InstanceMethodsInterceptPoint[]{new InstanceMethodsInterceptPoint() {@Overridepublic ElementMatcher<MethodDescription> getMethodsMatcher() {return ElementMatchers.named("execute");}@Overridepublic String getMethodsInterceptor() {return INTERCEPT_EXECUTE_METHOD_HANDLE;}@Overridepublic boolean isOverrideArgs() {return true;}},new InstanceMethodsInterceptPoint() {@Overridepublic ElementMatcher<MethodDescription> getMethodsMatcher() {return ElementMatchers.named("submit");}@Overridepublic String getMethodsInterceptor() {return INTERCEPT_SUBMIT_METHOD_HANDLE;}@Overridepublic boolean isOverrideArgs() {return true;}}};}
}
ThreadPool公共拦截类
public abstract class AbstractThreadPoolInterceptor implements InstanceMethodsAroundInterceptor {@Overridepublic 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);@Overridepublic Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Object ret) throws Throwable {return ret;}@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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";@Overrideprotected ClassMatch enhanceClass() {return HierarchyMatch.byHierarchyMatch(ENHANCE_CLASS);}@Overridepublic ConstructorInterceptPoint[] getConstructorsInterceptPoints() {return new ConstructorInterceptPoint[0];}@Overridepublic InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {return new InstanceMethodsInterceptPoint[]{new InstanceMethodsInterceptPoint() {@Overridepublic ElementMatcher<MethodDescription> getMethodsMatcher() {return ElementMatchers.named("apply");}@Overridepublic String getMethodsInterceptor() {return INTERCEPTOR_CLASS;}}};}
}
Feign灰度标签拦截器
public class FeignContextInterceptor implements InstanceMethodsAroundInterceptor {@Overridepublic 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";@Overrideprotected ClassMatch enhanceClass() {return HierarchyMatch.byHierarchyMatch(ENHANCE_CLASS);}@Overridepublic ConstructorInterceptPoint[] getConstructorsInterceptPoints() {return new ConstructorInterceptPoint[0];}@Overridepublic InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {return new InstanceMethodsInterceptPoint[]{new InstanceMethodsInterceptPoint() {@Overridepublic ElementMatcher<MethodDescription> getMethodsMatcher() {return ElementMatchers.named("intercept");}@Overridepublic String getMethodsInterceptor() {return INTERCEPTOR_CLASS;}}};}
}
RestTemplate http 拦截灰度标签续传
public class HttpRequestContextInterceptor implements InstanceMethodsAroundInterceptor {@Overridepublic 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";@Overrideprotected ClassMatch enhanceClass() {return HierarchyMatch.byHierarchyMatch(ENHANCE_CLASS);}@Overridepublic ConstructorInterceptPoint[] getConstructorsInterceptPoints() {return new ConstructorInterceptPoint[0];}@Overridepublic InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {return new InstanceMethodsInterceptPoint[]{new InstanceMethodsInterceptPoint() {@Overridepublic ElementMatcher<MethodDescription> getMethodsMatcher() {return ElementMatchers.named("choose");}@Overridepublic String getMethodsInterceptor() {return INTERCEPTOR_CLASS;}}};}
}
Rule 灰度路由规则拦截改写
public class RuleInterceptor implements InstanceMethodsAroundInterceptor {@Overridepublic 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";@Overrideprotected ClassMatch enhanceClass() {return NameMatch.byName(ENHANCE_CLASS);}@Overridepublic ConstructorInterceptPoint[] getConstructorsInterceptPoints() {return new ConstructorInterceptPoint[0];}@Overridepublic InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {return new InstanceMethodsInterceptPoint[]{new InstanceMethodsInterceptPoint() {@Overridepublic ElementMatcher<MethodDescription> getMethodsMatcher() {return ElementMatchers.named("invoke");}@Overridepublic 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";@Overrideprotected ClassMatch enhanceClass() {return NameMatch.byName(ENHANCE_CLASS);}@Overridepublic ConstructorInterceptPoint[] getConstructorsInterceptPoints() {return new ConstructorInterceptPoint[0];}@Overridepublic InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {return new InstanceMethodsInterceptPoint[]{new InstanceMethodsInterceptPoint() {@Overridepublic ElementMatcher<MethodDescription> getMethodsMatcher() {return ElementMatchers.named("invoke");}@Overridepublic String getMethodsInterceptor() {return INTERCEPTOR_CLASS;}}};}
}
服务消费方ContextFilter拦截
public class ConsumerContextInterceptor implements InstanceMethodsAroundInterceptor {@Overridepublic 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 {@Overridepublic 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";@Overrideprotected ClassMatch enhanceClass() {return NameMatch.byName(ENHANCE_CLASS);}@Overridepublic ConstructorInterceptPoint[] getConstructorsInterceptPoints() {return new ConstructorInterceptPoint[0];}@Overridepublic InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {return new InstanceMethodsInterceptPoint[]{new InstanceMethodsInterceptPoint() {@Overridepublic ElementMatcher<MethodDescription> getMethodsMatcher() {return ElementMatchers.named("route");}@Overridepublic String getMethodsInterceptor() {return INTERCEPTOR_CLASS;}}};}
}
灰度路由规则拦截改写
public class AppGrayRouterInterceptor implements InstanceMethodsAroundInterceptor {@Overridepublic 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";@Overrideprotected ClassMatch enhanceClass() {return NameMatch.byName(ENHANCE_CLASS);}@Overridepublic ConstructorInterceptPoint[] getConstructorsInterceptPoints() {return new ConstructorInterceptPoint[]{new ConstructorInterceptPoint() {@Overridepublic ElementMatcher<MethodDescription> getConstructorMatcher() {return ElementMatchers.any();}@Overridepublic String getConstructorInterceptor() {return INTERCEPTOR_CLASS;}}};}@Overridepublic InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {return new InstanceMethodsInterceptPoint[0];}
}
实例uuid拦截上报
public class NacosDiscoveryPropertiesInterceptor implements InstanceConstructorInterceptor {@Overridepublic 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";@Overrideprotected ClassMatch enhanceClass() {return NameMatch.byName(ENHANCE_CLASS);}@Overridepublic ConstructorInterceptPoint[] getConstructorsInterceptPoints() {return new ConstructorInterceptPoint[]{new ConstructorInterceptPoint() {@Overridepublic ElementMatcher<MethodDescription> getConstructorMatcher() {return ElementMatchers.any();}@Overridepublic String getConstructorInterceptor() {return INTERCEPTOR_CLASS;}}};}@Overridepublic InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {return new InstanceMethodsInterceptPoint[0];}
}
实例uuid拦截上报
public class InstanceInfoInterceptor implements InstanceConstructorInterceptor {@Overridepublic 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 等信息
体验演示
下载地址
相关文章:
3.微服务灰度发布落地实践(组件灰度增强)
文章目录 前言调用链示图dubbo服务之间的的调链cloud 服务之间的调用链 网关servlet容器: 标签续传1.定义插件2.实现灰度增强拦截 线程池: 标签续传1.拦截Runnable或Callable,接口增强实现标签续传;Callable 插件定义Runnable 插件定义拦载Callabl或Runnable构造(可共用)拦载ru…...
每天40分玩转Django:Django Email
Django Email 一、知识要点总览表 类别知识点基础配置SMTP设置、Email配置项发送方式同步发送、异步发送邮件类型纯文本、HTML邮件、带附件邮件异步任务Celery集成、任务队列高级特性邮件模板、批量发送 二、Email基础配置 1. 配置settings.py # settings.py EMAIL_BACKEN…...
ipad如何直连主机(Moonlight Sunshine)
Windows 被连接主机(Windows) 要使用的话需要固定ip,不然ip会换来换去,固定ip方法本人博客有记载Github下载Sunshine Sunshine下载地址除了安装路径需要改一下,其他一路点安装完成后会打开Sunshine的Web UIÿ…...
linux 网络安全不完全笔记
一、安装Centos 二、Linux网络网络环境设置 a.配置linux与客户机相连通 b.配置linux上网 三、Yum详解 yum 的基本操作 a.使用 yum 安装新软件 yum install –y Software b.使用 yum 更新软件 yum update –y Software c.使用 yum 移除软件 yum remove –y Software d.使用 yum …...
一、Hadoop概述
文章目录 一、Hadoop是什么二、Hadoop发展历史三、Hadoop三大发行版本1. Apache Hadoop2. Cloudera Hadoop3. Hortonworks Hadoop 四、Hadoop优势1. 高可靠性2. 高扩展性3. 高效性4. 高容错性 五、Hadoop 组成1. Hadoop1.x、2.x、3.x区别2. HDFS 架构概述3. YARN 架构概述4. Ma…...
Android 转场动画合集
项目背景:mvvm组件化,然后由于领导要求,使用MainActivity为主界面,各种业务使用Fragment处理。所以~ 由于单独ActivityN Fragment 的方式,经过十多天的试错,我确认一件事情。再确认过架构方式后一定要了解下…...
STM32 软件I2C读写
单片机学习! 目录 前言 一、软件I2C读写代码框架 二、I2C初始化 三、六个时序基本单元 3.1 引脚操作的封装和改名 3.2 起始条件执行逻辑 3.3 终止条件执行逻辑 3.4 发送一个字节 3.5 接收一个字节 3.5 发送应答&接收应答 3.5.1 发送应答 3.5.2 接…...
MySQL数据导出导出的三种办法(1316)
数据导入导出 基本概述 目前常用的有3中数据导入与导出方法: 使用mysqldump工具: 优点: 简单易用,只需一条命令即可完成数据导出。可以导出表结构和数据,方便完整备份。支持过滤条件,可以选择导出部分数据…...
三层交换机配置
✍作者:柒烨带你飞 💪格言:生活的情况越艰难,我越感到自己更坚强;我这个人走得很慢,但我从不后退。 📜系列专栏:网路安全入门系列 目录 一,三层交换二,实验案…...
odoo中@api.model, @api.depends和@api.onchange 装饰器的区别
文章目录 1. api.model用途特点示例 2. api.depends用途特点示例 3. api.onchange用途特点示例 总结 在 Odoo 中,装饰器(decorators)用于修饰方法,以指定它们的行为和触发条件。api.model、api.depends 和 api.onchange 是三个常用…...
Mysql的事务隔离机制
文章目录 事务基础概念隔离性与隔离机制的重要性四种隔离级别读未提交读已提交可重复读串行化 隔离级别设置与查看 事务基础概念 事务是一组数据库操作,这些操作要么全部成功执行,要么全部不执行。在 MySQL 中,事务通常以START TRANSACTION开…...
鸿蒙项目云捐助第二十八讲云捐助项目首页组件云数据库加载轮播图
鸿蒙项目云捐助第二十八讲云捐助项目首页组件云数据库加载轮播图 在前面的章节中实现了云捐赠项目的底部导航和分类导航,本讲继续使用云技术丰富首页组件中的功能。这里使用云数据库进行首页组件轮播图的加载。 一、云数据库进行首页组件轮播图的加载 在云捐助项…...
vue项目搭建规范
项目搭建规范 一. 代码规范1.1. 集成editorconfig配置1.2. 使用prettier工具1.3. 使用ESLint检测1.4. git Husky和eslint1.5. git commit规范1.5.1. 代码提交风格1.5.2. 代码提交验证 二. 第三方库集成2.1. vue.config.js配置2.2. vue-router集成2.3. vuex集成2.4. element-plu…...
Spring Boot的开发工具(DevTools)模块中的热更新特性导致的问题
问题: java.lang.ClassCastException: class cn.best.scholarflow.framework.system.domain.entity.SysUser cannot be cast to class cn.best.scholarflow.framework.system.domain.entity.SysUser (cn.best.scholarflow.framework.system.domain.…...
Unity Shader TexelSize的意义
TexelSize在制作玻璃折射效果时会用到。 // Get the normal in tangent space fixed3 bump UnpackNormal(tex2D(_BumpMap, i.uv.zw)); // Compute the offset in tangent space float2 offset bump.xy * _Distortion * _RefractionTex_TexelSize.xy; i.scrPos.xy offset * i…...
一个C#开发的APP
开发方式 C#Web、AndroidWebView 系统设计 系统主要分两个部分。一个是内容(文章)发布系统,另一个是预约和支付系统。 内容发布系统 和普通的文章发布系统不一样的地方在于,我们把每篇文章和大师关联起来。在文章的下方会显示…...
C++ 设计模式:原型模式(Prototype Pattern)
链接:C 设计模式 链接:C 设计模式 - 工厂方法 链接:C 设计模式 - 抽象工厂 链接:C 设计模式 - 建造者模式 原型模式(Prototype Pattern)是一种创建型设计模式,它允许一个对象通过复制现有对象来…...
window如何将powershell以管理员身份添加到右键菜单?(按住Shift键显示)
window如何将powershell以管理员身份添加到右键菜单? 在 Windows 中,将 PowerShell 以管理员身份添加到右键菜单,可以让你在需要提升权限的情况下快速打开 PowerShell 窗口。以下是详细的步骤,包括手动编辑注册表和使用注册表脚本…...
python爬虫——爬取全年天气数据并做可视化分析
一、主题页面的结构与特征分析 1.主题页面的结构与特征分析 目标内容界面: 2. Htmls 页面解析 3.节点查找方法与遍历方法 查找方法:find(): 查找第一个匹配到的节点。find_all(): 查找所有匹配到的节点,并返回一个…...
【Unity3D】ECS入门学习(十二)IJob、IJobFor、IJobParallelFor
IJob:开启单个线程进行计算,线程内不允许对同一个数据进行操作,也就是如果你想用多个IJob分别计算,将其结果存储到同一个NativeArray<int>数组是不允许的,所以不要这样做,如下例子就是反面教材&#…...
存储进阶笔记(二):Linux 存储栈:从 Device Mapper、LVM 到文件系统(2024)
记录一些平时接触到的存储知识。由于是笔记而非教程,因此内容不求连贯,有基础的同学可作查漏补缺之用。 存储进阶笔记(一):硬件基础:HDD/SDD、JBOD、RAID 等(2024) 存储进阶笔记&am…...
MySQL——操作
一.库的操作 1.基本操作 创建数据库 create database 数据库名称; 查看数据库 show databases; 删除数据库 drop database 数据库名称; 执行删除之后的结果: 数据库内部看不到对应的数据库对应的数据库文件夹被删除,级联删除,里面的数据表全部被删 所…...
c++表达范围勿用数学符号
目的 遇上了一个C基础问题,一下子陷到里面,不知怎么回事了,知道后,又感觉太可笑。 这也许就是成长的代价。 下面就是细说说所遇上的问题。 关于C逻辑的一些知识点: 定义: 用逻辑运算符将两个表达式链接起来的式子称为…...
SAP PP bom历史导出 ALV 及XLSX 带ECN号
bom总数 104W PS超过XLSX上限 ,那就分文件 *&---------------------------------------------------------------------* *& Report ZRPT_PP_BOM_HIS_ECN *&---------------------------------------------------------------------* *& tcode:zpp0…...
【AIGC-ChatGPT职业提示词指令】智能职业规划助手:基于SVG可视化的职业发展指南系统
引言 在当今快速变化的职场环境中,职业发展规划变得越来越复杂和充满挑战。无论是想要转行的技术人员,还是希望突破瓶颈的职场人士,都需要一个清晰的指导方向和可执行的行动计划。基于这种需求,我们设计了一个智能职业规划助手系统,它能够通过数据可视化的方式,为用户提…...
node.js之---单线程异步非阻塞 I/O
单线程模型 1、Node.js 使用 单线程 来处理客户端请求和执行任务 2、如果遇到异步任务,node.js使用事件循环和异步 I/O 模型,使得它能够高效地处理大量并发请求 异步操作有哪些 1、读取文件 2、网络请求 3、数据库操作等等 异步非阻塞 I/O Node.…...
DotnetSpider实现网络爬虫
1. 使用DotnetSpider框架 DotnetSpider是一个开源的、轻量、灵活、高性能、跨平台的分布式网络爬虫框架,适用于.NET平台。它可以帮助开发者快速实现网页数据的抓取功能。 1.1 安装DotnetSpider NuGet包 首先,你需要在你的.NET项目中安装DotnetSpider NuGet包。你可以通过…...
01 Oracle 基本操作
Oracle 基本操作 初使用步骤 1.创建表空间 2.创建用户、设置密码、指定表空间 3.给用户授权 4.切换用户登录 5.创建表 注意点:oracle中管理表的基本单位是用户 文章目录 了解Oracle体系结构 1.创建表空间**2.删除表空间**3.创建用户4.给用户授权5.切换用户登录6.表操…...
纯血鸿蒙ArkUI线性布局详解
线性布局说明 线性布局(LinearLayout)是开发中最常用的布局,通过线性容器Row和Column构建。线性布局是其他布局的基础,其子元素在线性方向上(水平方向和垂直方向)依次排列。线性布局的排列方向由所选容器组…...
MySQL root用户密码忘记怎么办(Reset root account password)
在使用MySQL数据库的的过程中,不可避免的会出现忘记密码的现象。普通用户的密码如果忘记,可以用更高权限的用户(例如root)进行重置。但是如果root用户的密码忘记了,由于root用户本身就是最高权限,那这个方法…...
18.springcloud_openfeign之扩展组件二
文章目录 一、前言二、子容器默认组件FeignClientsConfigurationDecoder的注入Contract约定对注解的支持对类上注解的支持对方法上注解的支持对参数上注解的支持@MatrixVariable@PathVariable@RequestParam@RequestHeader@SpringQueryMap@RequestPart@CookieValueFormattingCon…...
18、【OS】【Nuttx】用gdb调试nuttx os
背景 接之前wiki 14、【OS】【Nuttx】Nsh中运行第一个程序 15、【OS】【Nuttx】OS裁剪,运行指定程序,周期打印当前任务 程序跑起来了,OS也裁剪了,下一步就是调试了 目标 用gdb把nuttx程序跑起来 准备环境 vscode商店里C和C相…...
网络爬虫科普:原理、类型、策略与常用工具
网络爬虫科普:原理、类型、策略与常用工具 网络爬虫在当今互联网时代扮演着极为重要的角色,它能帮助我们从海量的网络信息中提取出有价值的数据。以下将从网络爬虫的基本概念、工作流程、类型、搜索策略以及常用工具等方面进行详细科普介绍。 一、网络…...
SQL 实战:动态表创建与多表更新的高级 SQL
在实际的数据库管理和开发中,经常需要临时存储中间计算结果或对多表数据进行批量更新。SQL 提供了动态表创建和多表更新的能力,使复杂业务逻辑能够通过一条 SQL 语句高效完成。本文将介绍如何动态创建临时表和实现多表联动更新,并通过具体示例…...
RabbitMQ基础篇之快速入门
文章目录 一、目标需求二、RabbitMQ 控制台操作步骤1.创建队列2.交换机概述3.向交换机发送消息4.结果分析5.消息丢失原因 三、绑定交换机与队列四、测试消息发送五、消息查看六、结论 一、目标需求 新建队列:创建 hello.queue1 和 hello.queue2 两个队列。消息发送…...
NLP论文速读(NeurIPS 2024)|BERT作为生成式上下文学习者BERTs are Generative In-Context Learners
论文速读|BERTs are Generative In-Context Learners 论文信息: 简介: 本文探讨了在自然语言处理(NLP)领域中,上下文学习(in-context learning)的能力,这通常与因果语言模型&#x…...
LeetCode - Google 校招100题 第7天 序列(数据结构贪心) (15题)
欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://spike.blog.csdn.net/article/details/144744418 相关文章: LeetCode 合计最常见的 112 题: 校招100题 第1天 链表(List) (19题)校招100题 第2天 树(Tree) (21…...
基于Docker基础与操作实战
6.1 Docker容器简介 Docker是一个开源的应用容器引擎,它基于Go语言并遵从Apache2.0 协议开源。 Docker是一个用于开发,交付和运行应用程序的开放平台。Docker能将应用程序与基础架构分开,从而可以快速交付软件。借助Docker,您可…...
高转化的Facebook广告文案的秘诀
Facebook 广告文案是制作有效 Facebook 广告的关键方面。它侧重于伴随广告视觉元素的文本内容。今天我们的博客将深入探讨成功的 Facebook 广告文案的秘密! 一、广告文案怎么写? 正文:这是帖子的正文,出现在您姓名的正下方。它可…...
支持向量机入门指南:从原理到实践
目录 1 支持向量机的基本概念 1.2 数学表达 2 间隔与支持向量 2.1 几何间隔 2.2 支持向量的概念 2.3 规范化超平面 2.4 支持向量的深入分析 2.4.1 支持向量的特征 2.4.2 支持向量的作用 2.4.3 支持向量的代数表示 2.5 KKT条件 3 最优化问题 3.1 问题的形成 3.2 规…...
汽车打气泵方案|智能充气泵工作原理
汽车打气泵方案最开始是机械式的开发,后来慢慢地演变成由一个气缸、压力传感器和ADC芯片以及主控芯片,就能够使得打气筒具备智能充气功能,摇身一变变成汽车打气泵方案。它具备精准压力检测以及过充过放等功能,利用ADC芯片和压力传…...
Jenkins入门使用
Jenkins入门使用 1先安装jdk才能运行jenkins yum install -y java-1.8.0-openjdk.x86_64 2 安装jenkins,运行,进行端口绑定,启动jenkins docker search jenkins docker pull jenkins/jenkins docker run -d -u root -p 8080:8080 -p 50000:50…...
iOS Masonry对包体积的影响
01 Masonry介绍 Masonry是iOS在控件布局中经常使用的一个轻量级框架,Masonry让NSLayoutConstraint使用起来更为简洁。Masonry简化了NSLayoutConstraint的使用方式,让我们可以以链式的方式为我们的控件指定约束。 常用接口声明与实现: 使用方式…...
Hive分区再分桶表
在Hive中,数据通常是根据分区(partition)来组织的,但是对于大数据集,单层分区可能不够用,因此可以进一步细分为桶(bucket)。桶可以用于提供额外的并行处理和优化查询性能。在这种情况…...
三大行业案例:AI大模型+Agent实践全景
本文将从AI Agent和大模型的发展背景切入,结合51Talk、哈啰出行以及B站三个各具特色的行业案例,带你一窥事件驱动架构、RAG技术、人机协作流程,以及一整套行之有效的实操方法。具体包含内容有:51Talk如何让智能客服“主动进攻”&a…...
国产低代码框架zdppy开发笔记002 标准的接口响应
前言 通过前面的学习, 我们已经知道了zdppy_api和zdppy_req的基本用法, 接下来我们会在学习中多次用到这两个框架. 我们已经知道了该如何响应一个字符串,但是我们该如何响应json数据呢? 在zdppy_api中,我们定义了一组规范的API响应, 我们慢慢来看看. 规范的响应 首先来看…...
关于Nginx
1.Nginx的配置 proxy_pass http: 当你需要将请求分发到多个后端服务器时,需要实现负载均衡功能,可以使用upstream指令定义一组服务器,并在proxy_pass中引用这个服务组名称。。如果不需要负载均衡,只需要将请求转发到单一的后端…...
数据库实时会话管理,性能问题诊断后的临门一脚
目录 前言 实时会话管理 DBdoctor 实时会话功能 1.实时会话列表 2.结束会话 3.操作历史 4.SQL分析 结语 前言 在之前的文章中我们介绍了DBdoctor性能洞察功能,它能够快速量化数据库连接会话单条SQL的资源消耗,实现性能问题快速根因定位并给出优…...
以EM算法为例介绍坐标上升(Coordinate Ascent)算法:中英双语
中文版 什么是 Coordinate Ascent 算法? Coordinate Ascent(坐标上升)是一种优化算法,它通过在每次迭代时优化一个变量(或一个坐标),并保持其他变量不变,逐步逼近最优解。与坐标下…...
visual studio连接sql server数据库
目录 1、为什么要建立连接2、在sql server中建立数据库3、visual studio连接sql server数据库4、学生信息管理系统页面布局5、添加事件逻辑 5.1 页面跳转5.2 读取学生信息5.3 查询学生信息5.4 修改学生信息5.5 删除学生信息5.6 添加学生信息 bilibili演示视频 github源码 1、…...