一.源码地址
https://github.com/codecentric/chaos-monkey-spring-boot.git
二.目录结构
- assaults包下是四种攻击方式,分为两类ChaosMonkeyRequestAssault和ChaosMonkeyRuntimeAssault.一个时请求攻击,另一个是应用运行时攻击(可以接口调用,也可以定时任务调用)
- compoent包下定义了两个scope,和一个定时器,还有使用micrometer记录java应用性能的自定义三个雷
- condition包下是@condition的自定义条件类
- configuration包下 是yml配置里的接收类,下面会详细解说
- endpoint 都是自定义的endpoint可以监控chaosmonkey的状态以及修改它的配置
- events包下就封装了一个事件类MetricEvent用户micrometer
- wacher包下都是aop,会去切controller,service,respository等,注意一个请求如果经历三层的话,chaosmonkey会生效三次,所以最好设置service层比较好.
三.源码
(1). ExceptionAssault,AssaultException
@Override
public void attack() {
LOGGER.info("Chaos Monkey - exception");
AssaultException assaultException = this.settings.getAssaultProperties().getException();
// metrics
if (metricEventPublisher != null)
metricEventPublisher.publishMetricEvent(MetricType.EXCEPTION_ASSAULT);
assaultException.throwExceptionInstance();
}
调用了AssaultException的throwExceptionInstance,根据配置通过反射创建异常实例;
@JsonIgnore @SneakyThrows
public void throwExceptionInstance() {
Exception instance;
try {
Class<? extends Exception> exceptionClass = getExceptionClass();
if (arguments == null) {
Constructor<? extends Exception> constructor = exceptionClass.getConstructor();
instance = constructor.newInstance();
} else {
Constructor<? extends Exception> constructor = exceptionClass.getConstructor(this.getExceptionArgumentTypes().toArray(new Class[0]));
instance = constructor.newInstance(this.getExceptionArgumentValues().toArray(new Object[0]));
}
} catch (ReflectiveOperationException e) {
LOGGER.warn("Cannot instantiate the class for provided type: {}. Fallback: Throw RuntimeException", type);
instance = new RuntimeException("Chaos Monkey - RuntimeException");
}
throw instance;
}
例如yml里配置自定义exception,就会抛出自定义异常
assaults:
level: 1
latencyActive: true
exceptionsActive: true
exception:
type: pl.piomin.services.customer.ZuoqiException
arguments[0]:
className: java.lang.String
value: 1
killApplicationActive: false
(2)KillAppAssault 杀掉应用
@Override
public void attack() {
try {
LOGGER.info("Chaos Monkey - I am killing your Application!");
if (metricEventPublisher != null)
metricEventPublisher.publishMetricEvent(MetricType.KILLAPP_ASSAULT);
int exit = SpringApplication.exit(context, (ExitCodeGenerator) () -> 0);
Thread.sleep(5000); // wait before kill to deliver some metrics
System.exit(exit);
} catch (Exception e) {
LOGGER.info("Chaos Monkey - Unable to kill the App, I am not the BOSS!");
}
}
(3)LatencyAssault 延迟攻击,
@Override
public void attack() {
LOGGER.debug("Chaos Monkey - timeout");
// 在配置的延迟攻击范围内随机取值
int timeout = ThreadLocalRandom.current().nextInt(settings.getAssaultProperties().getLatencyRangeStart(),
settings.getAssaultProperties().getLatencyRangeEnd());
atomicTimeoutGauge.set(timeout);
LOGGER.info("Chaos Monkey latency timr" + timeout);
// metrics
if (metricEventPublisher != null) {
//发布事件
metricEventPublisher.publishMetricEvent(MetricType.LATENCY_ASSAULT);
metricEventPublisher.publishMetricEvent(MetricType.LATENCY_ASSAULT, atomicTimeoutGauge);
}
try {
//线程睡眠随机数值
Thread.sleep(timeout);
} catch (InterruptedException e) {
// do nothing
}
}
(4)MemoryAssault 内存攻击,引发gc,
private void eatFreeMemory() {
//获取最终应用剩余内存最小值,比如memoryFillIncrementFraction设置为0.15,那就会吃掉剩余内存*(1-0.15)
int minimumFreeMemoryPercentage = calculatePercentIncreaseValue(settings.getAssaultProperties().getMemoryFillIncrementFraction());
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
//定义一个集合用于内存存放
Vector<byte[]> memoryVector = new Vector<>();
long stolenHere = 0L;
//计算每次放进集合的内存大小,第一次是随机获取剩余内存的0.05-memoryFillTargetFraction倍数
int percentIncreaseValue = calculatePercentIncreaseValue(calculatePercentageRandom());
LOGGER.info("Chaos Monkey - memory assault start, free memory: " + runtime.freeMemory()/1024/1024+"M");
LOGGER.info("Chaos Monkey - memory assault start, percentIncreaseValue: " + percentIncreaseValue/1024/1024+"M");
//当内存攻击开启,且剩余内存大于配置的最小值,且剩余内存大于该放进集合的内存
while (isActive() && runtime.freeMemory() >= minimumFreeMemoryPercentage && runtime.freeMemory() > percentIncreaseValue) {
// only if ChaosMonkey in general is enabled, triggers a stop if the attack is canceled during an experiment
// increase memory random percent steps
memoryVector.add(createDirtyMemorySlice(percentIncreaseValue));
stolenHere += percentIncreaseValue;
long newStolenTotal = stolenMemory.addAndGet(percentIncreaseValue);
metricEventPublisher.publishMetricEvent(MetricType.MEMORY_ASSAULT_MEMORY_STOLEN, newStolenTotal);
//每次内存提升的间隔
waitUntil(settings.getAssaultProperties().getMemoryMillisecondsWaitNextIncrease());
percentIncreaseValue = calculatePercentIncreaseValue(settings.getAssaultProperties().getMemoryFillTargetFraction());
LOGGER.info("Chaos Monkey - memory assault, percentIncreaseValue: " + percentIncreaseValue/1024/1024+"M");
}
LOGGER.info("Chaos Monkey - memory assault end, free memory: " + runtime.freeMemory()/1024/1024);
// Hold memory level and cleanUp after, only if experiment is running
if (isActive()) {
LOGGER.info("Memory fill reached, now sleeping and holding memory"+runtime.totalMemory()/1024/1024+"M");
LOGGER.info("Memory fill reached, now sleeping and holding memory");
//内存攻击后的持续时间
waitUntil(settings.getAssaultProperties().getMemoryMillisecondsHoldFilledMemory());
}