介绍
编写 java 或者 groovy 脚本实现复杂的故障场景,比如篡改参数、修改返回值、抛自定义异常等
参数
以下是此场景特有参数,通用参数详见:[blade create jvm](blade create jvm.md)
--effect-count string 影响的请求条数
--effect-percent string 影响的请求百分比
--script-content string 脚本内容,是 Base64 编码后的内容,相关工具类 [Base64Util](https://github.com/chaosblade-io/chaosblade-exec-jvm/blob/master/chaosblade-exec-plugin/chaosblade-exec-plugin-jvm/src/main/java/com/alibaba/chaosblade/exec/plugin/jvm/Base64Util.java)。注意,不能和 script-file 同时使用。
--script-file string 脚本文件,文件绝对路径
--script-name string 脚本名称,日志记录用,可不填写。
--script-type string 脚本类型,取值为 java 或 groovy,默认为 java。
使用 script-content 指定演练脚本内容,不添加 script-type 参数,默认为 java 脚本,将调用 java 引擎解析器。
blade c jvm script --classname com.example.controller.DubboController --methodname call --script-content aW1wb3J0IGphdmEudXRpbC5NYXA7CgppbXBvcnQgY29tLmV4YW1wbGUuY29udHJvbGxlci5DdXN0b21FeGNlcHRpb247CgovKioKICogQGF1dGhvciBDaGFuZ2p1biBYaWFvCiAqLwpwdWJsaWMgY2xhc3MgRXhjZXB0aW9uU2NyaXB0IHsKICAgIHB1YmxpYyBPYmplY3QgcnVuKE1hcDxTdHJpbmcsIE9iamVjdD4gcGFyYW1zKSB0aHJvd3MgQ3VzdG9tRXhjZXB0aW9uIHsKICAgICAgICBwYXJhbXMucHV0KCIxIiwgMTExTCk7CiAgICAgICAgLy9yZXR1cm4gIk1vY2sgVmFsdWUiOwogICAgICAgIC8vdGhyb3cgbmV3IEN1c3RvbUV4Y2VwdGlvbigiaGVsbG8iKTsKICAgICAgICByZXR1cm4gbnVsbDsKICAgIH0KfQo= --script-name exception
使用 script-file 参数指定文件演练:
blade c jvm script --classname com.example.controller.DubboController --methodname call --script-file /Users/Shared/IdeaProjects/Workspace_WebApp/dubbodemo/src/main/java/com/example/controller/ExceptionScript.java --script-name exception
执行 groovy 脚本实验场景,参数同上,但必须添加 --script-type groovy 参数。如
blade c jvm script --classname com.example.controller.DubboController --methodname call --script-file /Users/Shared/IdeaProjects/Workspace_WebApp/dubbodemo/src/main/java/com/example/controller/GroovyScript.groovy --script-name exception --script-type groovy
脚本规范
必须创建一个类,对类名和包名没有要求,其中所依赖的类,必须是目标应用所具备的类。
同包下的类引用,必须写全包名,比如故障脚本类是 com.example.controller.ExceptionScript,类中引入了同包下的 DubboController 类,则 DubboController 必须添加 com.example.controller.DubboController。引入非同包下的类,无需写全包名。
必须添加 public Object run(Map<String, Object> params) 方法,其中 params 对象中包含目标方法参数,key 是参数索引下标,从 0 开始,比如目标方法是 public String call(Object obj1, Object obj2){},则 params.get("0")则返回的是 obj1 对象,可以执行params.put("0", ) 来修改目标方法参数(目标方法及 --classname 和 --methodname 所指定的类方法)。
上述方法返回的对象如果不为空,则会根据脚本中返回的对象来修改目标方法返回值,注意类型必须和目标方法返回值一致。如果上述方法返回 null,则不会修改目标方法返回值。
案例
对以下业务类做修改返回值实验场景:
@RestController
@RequestMapping("/pet")
public class PetController {
@GetMapping("/list")
public Result<List<PetVO>> getPets() {
Map<Long, Discount> petDiscount = discountManager
.getPetDiscounts()
.stream()
.filter(discount -> discount.getExpired() == 0)
.collect(Collectors.toMap(
Discount::getPetId,
Function.identity()
));
List<PetVO> pets = petManager
.getPets()
.stream()
.map(pet -> {
PetVO petVO = PetVO.from(pet);
Discount discount = petDiscount.get(pet.getId());
if (null != discount && null != discount.getDiscountPrice() && discount.getDiscountPrice() > 0L) {
petVO.setDiscountPrice(discount.getDiscountPrice());
}
return petVO;
})
.collect(Collectors.toList());
return Result.success(pets);
}
则编写 Java 脚本,实现对 getPets 方法做返回值修改:
package com.alibaba.csp.monkeyking.controller;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.alibaba.csp.monkeyking.demo.model.Pet;
import com.alibaba.csp.monkeyking.model.PetVO;
import com.alibaba.csp.monkeyking.model.Result;
public class ChaosController {
public Object run(Map<String, Object> params) {
ArrayList<PetVO> petVOS = new ArrayList<>();
for (int i = 0; i < 3; i++) {
Pet pet = new Pet();
pet.setName("test_" + i);
PetVO petVO = PetVO.from(pet);
petVOS.add(petVO);
}
Result<List<PetVO>> results = Result.success(petVOS);
return results;
}
}
保存文件后,通过上面 使用方式 部分的命令来调用,也可以将其进行 Base64 编码,通过指定 script-content 参数来指定编码后的内容。
blade c jvm script --classname com.alibaba.csp.monkeyking.controller.PetController --methodname getPets --script-file /Users/Shared/IdeaProjects/Workspace_WebApp/dubbodemo/src/main/java/com/alibaba/csp/monkeyking/controller/ChaosController --script-name specifyReturnObj
常见问题
Java 实验场景的日志在 进程用户下 logs/chaosblade/chaosblade.log 中。执行脚本成功,但不生效,原因可能是脚本编译错误(因为脚本编译方法调用时触发,所以下发脚本,不会进行编译),可查看此日志进行排查。