目前我们已知chaosblade-exec-jvm是基于jvm-sandbox开发的混沌工程注入工具,我们可以基于jvm-sandbox创建一些其他的工具agent:流量回放agent、全链路压测agent等等
接下来我会用完全的代码实现一个可以流量透传、mock挡板、影子表数据落地等功能的压测agent:
MyAgentProject/
|-- agent/ (Agent 模块)
| |-- src/
| |-- pom.xml
|
|-- module/ (Module 模块)
| |-- src/
| |-- pom.xml
|
|-- instrument/ (Instrument 模块)
| |-- src/
| |-- pom.xml
|
|-- pom.xml (Parent POM)
Agent 模块
这个模块主要用于与控制平台交互,管理 Agent 的生命周期,包括安装、卸载和升级等。
- API Client: 与控制平台的 API 进行交互。
- Updater: 负责检查更新,并执行升级操作。
- Lifecycle Manager: 管理 Agent 的加载和卸载。
- Command Line Tools: 一些命令行工具用于手动管理 Agent。
Module 模块
这个模块主要包括一系列的子模块,每一个用于具体的任务,比如流量透传、方法 Mock 等。
- Common Interface: 定义所有模块都要实现的通用接口。
- Middleware Support: 包括一些预定义的中间件支持模块。
- User-Defined Modules: 用户可以按照规定的接口添加自己的模块。
Instrument 模块
这个模块是 Agent 的框架,负责程序的生命周期管理以及提供一些内置的命令工具。
- Instrumentation API: 提供用于字节码操纵的 API。
- Lifecycle API: 提供用于模块生命周期管理的 API。
- Built-in Tools: 一些内置的命令行或图形工具。
一:流量透传
在实现这个功能之前,我们需要明确目标服务之间的调用框架以及版本:
目标服务:采用spring boot web:2.1.2-release、feign-hystrix:10.1.0
通常公司在中间件层面流量透传能力基本已经实现。我们通过简单的例子来学习如何用jvm-sandbox来实现我们的功能
1.pom.xml添加相关依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>pressure-agent</artifactId> <packaging>pom</packaging> <version>1.0-SNAPSHOT</version> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>8</source> <target>8</target> </configuration> </plugin> </plugins> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> </resource> </resources> </build> <parent> <groupId>com.alibaba.jvm.sandbox</groupId> <artifactId>sandbox-module-starter</artifactId> <version>1.2.0</version> </parent> <dependencies> <dependency> <groupId>org.kohsuke.metainf-services</groupId> <artifactId>metainf-services</artifactId> <version>1.11</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>transmittable-thread-local</artifactId> <version>2.14.3</version> <scope>compile</scope> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.1</version> </dependency> </dependencies> </project> |
2. 通过spi实现module接口
package com.lchen; import com.alibaba.jvm.sandbox.api.Information; import com.alibaba.jvm.sandbox.api.Module; import com.alibaba.jvm.sandbox.api.ProcessController; import com.alibaba.jvm.sandbox.api.annotation.Command; import com.alibaba.jvm.sandbox.api.event.BeforeEvent; import com.alibaba.jvm.sandbox.api.listener.ext.EventWatchBuilder; import com.alibaba.jvm.sandbox.api.listener.ext.EventWatcher; import com.alibaba.jvm.sandbox.api.resource.ModuleEventWatcher; import org.kohsuke.MetaInfServices; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Resource; import java.lang.reflect.Method; import static com.alibaba.jvm.sandbox.api.event.Event.Type.BEFORE; @MetaInfServices(Module.class) @Information(id = "pressure-module") public class PressurePassModule implements Module { private static final Logger LOGGER = LoggerFactory.getLogger(PressurePassModule.class); @Resource private ModuleEventWatcher moduleEventWatcher; @Command("enable-traffic-pass") public void enableTrafficPass() { final EventWatcher watcher = new EventWatchBuilder(moduleEventWatcher) .onClass("javax.servlet.http.HttpServlet") .includeSubClasses() .includeBootstrap() .onBehavior("service") .onWatching() .onWatch(event -> { final BeforeEvent bEvent = (BeforeEvent) event; Object[] params = bEvent.argumentArray; Class<?> contentRequestWrapper = Class.forName("org.springframework.web.util.ContentCachingRequestWrapper", true, bEvent.javaClassLoader); if (params.length >= 2 && contentRequestWrapper.isInstance(params[0])) { Object contentRequestWrapperObj = params[0]; Method headerMethod = contentRequestWrapper.getMethod("getHeader", String.class); Object result = headerMethod.invoke(contentRequestWrapperObj, "X-Pressure-Test"); LOGGER.info("testFlag:{}", result); if (result != null) { Cache.set(String.valueOf(result)); } } ProcessController.returnImmediately(null); }, BEFORE); } @Command("enable-feign-pass") public void enableFeignPass() { LOGGER.info("enableFeignPass feign"); final EventWatcher watcher = new EventWatchBuilder(moduleEventWatcher) .onClass("feign.SynchronousMethodHandler") .includeSubClasses() .includeBootstrap() .onBehavior("executeAndDecode") .onWatching() .onWatch(event -> { final BeforeEvent bEvent = (BeforeEvent) event; Class<?> requestTemplateClass = Class.forName("feign.RequestTemplate", true, bEvent.javaClassLoader); Object[] params = bEvent.argumentArray; if (params.length >= 1 && requestTemplateClass.isInstance(params[0])) { Object requestTemplateObj = params[0]; Method headerMethod = requestTemplateClass.getMethod("header", String.class, String[].class); // 检查是否为压测流量(从 ThreadLocal 或其他地方) String testFlag = Cache.get(); // 假设这是你的实现 LOGGER.info("testFlag feign:{}", testFlag); if (testFlag != null) { headerMethod.invoke(requestTemplateObj, "X-Pressure-Test", new String[]{testFlag}); } } }, BEFORE); } }
3. 通过threadlocal实现流量服务内部传递,因为父子线程以及线程池原因,普通的threadlocal不能满足,我们采用TransmittableThreadLocal实现
package com.lchen; import com.alibaba.ttl.TransmittableThreadLocal; public class Cache<T> { private static TransmittableThreadLocal<String> threadLocal = new TransmittableThreadLocal<>(); public static void set(String request) { threadLocal.set(request); } public static void remove() { threadLocal.remove(); } public static String get() { return threadLocal.get(); } }
这样我们基于sandbox实现的简单流量透传能力框架就开发完毕
标签:模块,agent,alibaba,sandbox,jvm,import,com From: https://www.cnblogs.com/clovejava/p/17670367.html