|
1 |
| -### javaagent 技术 |
| 1 | +### 项目介绍 |
2 | 2 |
|
3 |
| -#### 1、技术点 |
| 3 | +#### 1、抛出问题 |
| 4 | + |
| 5 | +- 在基于 JAVA (SpringBoot)开发定时任务时,一般的情况下会使用以下几种进行实现 |
| 6 | + - Xxl |
| 7 | + - Quartz |
| 8 | + - Spring 注解 @Scheduled |
| 9 | +- 定时任务开发完成后,需要针对定时任务进行测试,一般的测试思路 |
| 10 | + 1. 写 http 接口对外暴露进行测试,可能需要给每一个定时任务都需要写一个接口 |
| 11 | + 2. 一般定时任务是基于 Cron 表达式写在配置文件中,通过修改定配合重启服务进行测试 |
| 12 | + |
| 13 | +> 以上发现基于上面的测试方式很麻烦且很费时,浪费大量开发和测试时间 |
| 14 | +
|
| 15 | +#### 2、解决思路 |
| 16 | + |
| 17 | +- 分析常见的定时任务实现原理,找到存储定时任务的地方(源码阅读): 针对 Xxl、Quartz、Spring 注解 @Scheduled 进行源码分析 |
| 18 | +- 针对不同实现的定时任务暴露2个 http 接口,可对其进行立即执行和CRUD操作(接口定义): 约定对外暴露 http 接口的入参出参 |
| 19 | +- 暴露的 http 接口可针对定时任务进行传参测试(可先写死): 在 controller 中写死逻辑 |
| 20 | +- 做到代码无侵入(最终目标): 使用 javaagent 技术进行代理实现 |
| 21 | + |
| 22 | +#### 4、实现技术 |
4 | 23 |
|
5 | 24 | - javaagent
|
6 |
| -- javassist |
7 |
| -- xxl job |
8 |
| -- quartz job |
9 |
| -- spring job |
10 |
| -- simple 自定义 job |
| 25 | +- Javassist、ASM、ByteBuddy |
| 26 | +- Xxl、Spring、Quartz、Simple(基于Spring) 定时任务 |
| 27 | + |
| 28 | +#### 5、使用说明 |
| 29 | + |
| 30 | +1. git 拉取项目 |
| 31 | + |
| 32 | + ```sh |
| 33 | + git clone [email protected]:wangchirl/javaagent.git |
| 34 | + ``` |
| 35 | + |
| 36 | +2. 进入项目使用 maven 打包 |
| 37 | + |
| 38 | + ```sh |
| 39 | + cd javaagent |
| 40 | + mvn clean package -Dmaven.test.skip=true |
| 41 | + ``` |
| 42 | + |
| 43 | +3. git 拉取测试项目 |
| 44 | + |
| 45 | + ```sh |
| 46 | + git clone [email protected]:wangchirl/scheduler.git |
| 47 | + ``` |
| 48 | + |
| 49 | +4. 进入项目使用 maven 打包 |
| 50 | + |
| 51 | + ```sh |
| 52 | + cd scheduler |
| 53 | + mvn clean package -Dmaven.test.skip=true |
| 54 | + ``` |
11 | 55 |
|
12 |
| -#### 2、说明 |
| 56 | +5. 进入测试项目target目录下,执行脚本 |
13 | 57 |
|
14 |
| -- 定时任务开发完成后,如果要进行测试时,需要修改定时任务时间,不能立即执行,测试不方便 |
15 |
| -- 项目针对以上问题做了针对以上定时任务实现方式的代理实现,利用 javaagent 技术,无侵入 |
16 |
| -- 此项目可直接打包为 jar 包 |
17 |
| -- 可结合测试仓库进行测试,测试仓库地址 :https://github.com/wangchirl/scheduler.git |
18 |
| -- 源码以开源,可根据自己需求进行改造 |
19 |
| -- 后续版本将使用 ASM 、Byte buddy 来对项目进行改造 |
20 |
| -- 目前实现了 load time 、dynamic 的 javassist 实现方式,其他方式将在后续进行实现 |
| 58 | + ```sh |
| 59 | + java -javaagent:本地路径(替换为自己的)\javaagent\target\SuperAgent-jar-with-dependencies.jar=ctlClass=com.shadow.controller.AgentBaseController,tlClass=com.shadow.supports.framework.ScheduleService,tlFieldName=JOB_PARAMETERS_THREAD_LOCAL,debug=true,logger=true,crudFieldName=xxxx,crud=true,proxyType=asm,jobType=spring -jar shadow.jar |
| 60 | + ``` |
21 | 61 |
|
22 |
| -#### 3、针对测试仓库进行测试的相关说明 |
| 62 | +6. 测试定时任务立即执行及CRUD |
23 | 63 |
|
| 64 | + ```sh |
| 65 | + # 执行定时任务 [spring2] 为 spring 定时任务的方法名称 |
| 66 | + # 立即执行 请求参数为 test Spring |
| 67 | + curl localhost:50084/shadow/api/system/agent/run/spring2?params=test%20Spring |
| 68 | + # 修改 cron 表达式为 0/3 * * * * ? |
| 69 | + curl localhost:50084/shadow/api/system/agent/crud/1/spring1?cron=0/3%20*%20*%20*%20*%20? |
| 70 | + ``` |
| 71 | + |
| 72 | +7. 可在本地自行导入 [postman 测试文件](./javaagent+schedule项目POSTMAN测试.json) 进行测试 |
| 73 | + |
| 74 | +#### 6、特别说明 |
| 75 | + |
| 76 | +> 接口说明: |
| 77 | +> |
| 78 | +> - 立即执行路径:/agent/run/{taskKey} |
| 79 | +> - CRUD路径:/agent/crud/{operation}/{taskKey} |
| 80 | +> |
| 81 | +> > 动态参数说明: |
| 82 | +> > |
| 83 | +> > - taskKey(字符串) |
| 84 | +> > |
| 85 | +> > 定时任务名称 |
| 86 | +> > |
| 87 | +> > - operation (数字) |
| 88 | +> > |
| 89 | +> > - 0 删除定时任务 |
| 90 | +> > - 1 添加定时任务 |
| 91 | +> > - 2 更新定时任务 |
| 92 | +> > - 其他数字 查询定时任务 |
| 93 | +> |
24 | 94 | > 参数说明:
|
25 | 95 | >
|
26 | 96 | > ##### Load time 参数:
|
|
34 | 104 | > - methodBody :暴露 HTTP 请求的方法体,已提供以上四种定时任务实现方式【可选】
|
35 | 105 | > - httpUri :暴露 HTTP 请求的 URI 前缀,会拼接上 /{taskKey} ,默认 /agent/run【可选】
|
36 | 106 | > - debug :是否 debug 模式,次模式方便调试,可生成对应的增强的 controller 类信息文件【可选】
|
| 107 | +> - logger :打印 agent 中的一些提示信息【可选】 |
37 | 108 | >
|
38 | 109 | > ##### Dynamic time 参数:
|
39 | 110 | >
|
|
49 | 120 | > > - methodName :暴露 HTTP 请求的方法的名称,有默认值【可选】
|
50 | 121 | > > - methodBody :暴露 HTTP 请求的方法体,已提供以上四种定时任务实现方式【可选】
|
51 | 122 | > > - debug :是否 debug 模式,此模式方便调试,可生成对应的增强的 controller 类信息文件【可选】
|
| 123 | +> > - logger :打印 agent 中的一些提示信息【可选】 |
52 | 124 |
|
53 | 125 | - xxl job 测试
|
54 | 126 |
|
55 | 127 | ```sh
|
56 | 128 | # 启动项目
|
57 |
| - -javaagent:F:\source\javaagent\target\SuperAgent-jar-with-dependencies.jar=jobType=XXL&ctlClass=com.shadow.controller.AgentBaseController&debug=true&tlClass=com.shadow.supports.framework.ScheduleService&tlFieldName=JOB_PARAMETERS_THREAD_LOCAL |
| 129 | + java -javaagent:本地路径(替换为自己的)\javaagent\target\SuperAgent-jar-with-dependencies.jar=ctlClass=com.shadow.controller.AgentBaseController,tlClass=com.shadow.supports.framework.ScheduleService,tlFieldName=JOB_PARAMETERS_THREAD_LOCAL,debug=true,logger=true,crudFieldName=xxxx,crud=true,proxyType=asm,jobType=xxl -jar shadow.jar |
58 | 130 | ```
|
59 | 131 |
|
60 | 132 | ```sh
|
61 | 133 | # 执行定时任务 [xxl2] 为定时任务JOB NAME
|
62 |
| - localhost:50084/shadow/api/system/agent/run/xxl2?params=王钦测试Xxl |
| 134 | + curl localhost:50084/shadow/api/system/agent/run/xxl2?params=testXxl |
| 135 | + # XXL 只有查询实现,因为其调度器和执行器是分离的 |
| 136 | + curl localhost:50084/shadow/api/system/agent/crud/1/1 |
63 | 137 | ```
|
64 | 138 |
|
65 | 139 |
|
|
68 | 142 |
|
69 | 143 | ```sh
|
70 | 144 | # 启动项目
|
71 |
| - -javaagent:F:\source\javaagent\target\SuperAgent-jar-with-dependencies.jar=jobType=QUARTZ&ctlClass=com.shadow.controller.AgentBaseController&debug=true&tlClass=com.shadow.supports.framework.ScheduleService&tlFieldName=JOB_PARAMETERS_THREAD_LOCAL |
| 145 | + java -javaagent:本地路径(替换为自己的)\javaagent\target\SuperAgent-jar-with-dependencies.jar=ctlClass=com.shadow.controller.AgentBaseController,tlClass=com.shadow.supports.framework.ScheduleService,tlFieldName=JOB_PARAMETERS_THREAD_LOCAL,debug=true,logger=true,crudFieldName=xxxx,crud=true,proxyType=asm,jobType=quartz -jar shadow.jar |
72 | 146 | ```
|
73 | 147 |
|
74 | 148 | ```sh
|
75 | 149 | # 执行定时任务 [quartzTriggerKey] 为 Trigger Bean,[quartzJob]为 job detail ID
|
76 |
| - localhost:50084/shadow/api/system/agent/run/quartzTriggerKey?params=quartzJob |
| 150 | + curl -H "Content-Type:application/json" -X POST --data "{\"name\":\"shadow\",\"age\":20}" localhost:50084/shadow/api/system/agent/run/quartzTriggerKey?params=quartzJob |
| 151 | + # 修改 cron 表达式为 0/3 * * * * ? |
| 152 | + curl localhost:50084/shadow/api/system/agent/crud/1/quartzTriggerKey@quartzJob?cron=0/3%20*%20*%20*%20*%20? |
77 | 153 | ```
|
78 | 154 |
|
79 | 155 | > Quartz 定时任务存在特殊性:
|
|
85 | 161 |
|
86 | 162 | ```sh
|
87 | 163 | # 启动项目
|
88 |
| - -javaagent:F:\source\javaagent\target\SuperAgent-jar-with-dependencies.jar=jobType=SPRING&ctlClass=com.shadow.controller.AgentBaseController&debug=true&tlClass=com.shadow.supports.framework.ScheduleService&tlFieldName=JOB_PARAMETERS_THREAD_LOCAL |
| 164 | + java -javaagent:本地路径(替换为自己的)\javaagent\target\SuperAgent-jar-with-dependencies.jar=ctlClass=com.shadow.controller.AgentBaseController,tlClass=com.shadow.supports.framework.ScheduleService,tlFieldName=JOB_PARAMETERS_THREAD_LOCAL,debug=true,logger=true,crudFieldName=xxxx,crud=true,proxyType=asm,jobType=spring -jar shadow.jar |
89 | 165 | ```
|
90 | 166 |
|
91 | 167 | ```sh
|
92 | 168 | # 执行定时任务 [spring2] 为 spring 定时任务的方法名称
|
93 |
| - localhost:50084/shadow/api/system//agent/run/spring2?params=王钦测试Spring |
| 169 | + # 立即执行 请求参数为 test Spring |
| 170 | + curl localhost:50084/shadow/api/system/agent/run/spring2?params=test%20Spring |
| 171 | + # 修改 cron 表达式为 0/3 * * * * ? |
| 172 | + curl localhost:50084/shadow/api/system/agent/crud/1/spring1?cron=0/3%20*%20*%20*%20*%20? |
94 | 173 | ```
|
95 | 174 |
|
96 | 175 |
|
|
99 | 178 |
|
100 | 179 | ```sh
|
101 | 180 | # 启动项目
|
102 |
| - -javaagent:F:\source\javaagent\target\SuperAgent-jar-with-dependencies.jar=jobType=SIMPLE&ctlClass=com.shadow.controller.AgentBaseController&debug=true&tlClass=com.shadow.supports.framework.ScheduleService&tlFieldName=JOB_PARAMETERS_THREAD_LOCAL |
| 181 | + java -javaagent:本地路径(替换为自己的)\javaagent\target\SuperAgent-jar-with-dependencies.jar=ctlClass=com.shadow.controller.AgentBaseController,tlClass=com.shadow.supports.framework.ScheduleService,tlFieldName=JOB_PARAMETERS_THREAD_LOCAL,debug=true,logger=true,crudFieldName=xxxx,crud=true,proxyType=asm,jobType=simple -jar shadow.jar |
103 | 182 | ```
|
104 | 183 |
|
105 | 184 | ```sh
|
106 | 185 | # 执行定时任务 [simple1] 为 spring 容器 Bean 名称
|
107 |
| - localhost:50084/shadow/api/system//agent/run/simple1?王钦测试Simple |
| 186 | + curl localhost:50084/shadow/api/system//agent/run/simple1?testSimple |
| 187 | + # 修改 cron 表达式为 0/3 * * * * ? |
| 188 | + curl localhost:50084/shadow/api/system/agent/crud/1/simple2?cron=0/3%20*%20*%20*%20*%20? |
108 | 189 | ```
|
109 | 190 |
|
110 | 191 |
|
111 |
| -#### 4、最全的 agent 请求参数传递 |
| 192 | +#### 7、最全的 agent 请求参数传递 |
112 | 193 |
|
113 | 194 | > ```sh
|
114 |
| -> -javaagent:F:\source\javaagent\target\SuperAgent-jar-with-dependencies.jar=jobType=XXL&ctlClass=com.shadow.controller.AgentBaseController&tlClass=com.shadow.supports.framework.ScheduleService&tlFieldName=JOB_PARAMETERS_THREAD_LOCAL&methodName=testMethod&iocFieldName=ioc&httpUri=/test/run&debug=true |
| 195 | +> java -javaagent:本地路径(替换为自己的)\javaagent\target\SuperAgent-jar-with-dependencies.jar=ctlClass=com.shadow.controller.AgentBaseController,tlClass=com.shadow.supports.framework.ScheduleService,tlFieldName=JOB_PARAMETERS_THREAD_LOCAL,debug=true,logger=true,httpUri=/test/run,methodName=testMethod,iocFieldName=ioc,crudFieldName=xxxx,crud=true,proxyType=asm,jobType=simple -jar shadow.jar |
115 | 196 | > ```
|
116 | 197 | >
|
117 | 198 | > 说明:
|
118 | 199 | >
|
119 |
| -> - 定时任务类型为 XXL |
120 |
| -> - 暴露 HTTP 的 controller 是 com.shadow.controller.AgentBaseController |
121 |
| -> - ThreadLocal 参数传递的类是 com.shadow.supports.framework.ScheduleService |
122 |
| -> - ThreadLocal 对象的字段名称是 JOB_PARAMETERS_THREAD_LOCAL |
123 |
| -> - 暴露 HTTP 接口方法名称是 testMethod |
124 |
| -> - 注入到 controller 类中的 Spring IOC 容器字段名称是 ioc |
125 |
| -> - HTTP 请求路径是 当前 controller 的 path + /test/run/{taskKey} |
126 |
| -> - debug 是 true,会在当前位置生成所增强的 controller 的字节码文件 |
127 |
| -
|
128 |
| -#### 5、postman 相关测试在同目录下的 `javaagent+schedule项目POSTMAN测试.json` |
129 |
| -
|
130 |
| -
|
131 |
| -
|
132 |
| -#### 6、Attach 机制 - 可动态替换定时任务类型 |
| 200 | +> - jobType :定时任务类型为 XXL |
| 201 | +> - proxyType :asm 表示使用 ASM 代理,可选 buddy 和 javassist |
| 202 | +> - crud :true 表示暴露 CRUD 接口 |
| 203 | +> - crudFieldName :crud 需要使用的字段名称,避免和代理类字段冲突使用 xxxx |
| 204 | +> - iocFieldName :spring ioc 的字段名称,避免和代理类中 spring ioc 名称的冲突使用 ioc |
| 205 | +> - methodName :立即执行接口的方法名称,避免和代理类方法名称冲突使用 testMethod |
| 206 | +> - httpUri :立即执行接口的路径,避免和代理类路径冲突使用 /test/run/{taskKey} |
| 207 | +> - logger :true 表示打印代理过程中的提示信息 |
| 208 | +> - debug :true 表示生成代理后的 class 文件 |
| 209 | +> - tlFieldName :定义的 ThreadLocal 的字段名称 JOB_PARAMETERS_THREAD_LOCAL |
| 210 | +> - tlClass :定义 ThreadLocal 的类名称 com.shadow.supports.framework.ScheduleService |
| 211 | +> - ctlClass :代理的 controller 类 com.shadow.controller.AgentBaseController |
| 212 | +
|
| 213 | +
|
| 214 | +
|
| 215 | +#### 8、Attach 机制 - 可动态替换定时任务类型 TODO |
133 | 216 |
|
134 | 217 | > 测试类:com.shadow.agent.AttachAgentTest
|
135 | 218 | >
|
|
0 commit comments