快放开那些捣乱的猴子!

粗看标题你可能感觉莫名其妙,什么「捣乱的猴子」,还要放开。不急,且听我说说为什么不光要放开这些捣乱的猴子,还要欢迎他们。

灵山ssl适用于网站、小程序/APP、API接口等需要进行数据传输应用场景,ssl证书未来市场广阔!成为创新互联建站的ssl证书销售渠道,可以享受市场价格4-6折优惠!如果有意向欢迎电话联系或者加微信:028-86922220(备注:SSL证书合作)期待与您的合作!

0.背景信息

在构建高可用性软件架构领域,有个词叫「混沌工程」,对应的英文是Chaos Engineering,通过 Chaos 的测试,可以发现系统的潜在风险,特别对于分布式系统,找出脆弱的地方进行增强,提升可用性,避免系统间级联影响。

混沌工程是在分布式系统上进行实验的学科, 目的是建立对系统抵御生产环境中失控条件的能力以及信心。

大规模分布式软件系统的发展正在改变软件工程。作为一个行业,我们很快采用了提高开发灵活性和部署速度的实践。紧随着这些优点的一个迫切问题是:我们对投入生产的复杂系统有多少信心?

即使分布式系统中的所有单个服务都正常运行, 这些服务之间的交互也会导致不可预知的结果。 这些不可预知的结果, 由影响生产环境的罕见且破坏性的事件复合而成,令这些分布式系统存在内在的混沌。

https://principlesofchaos.org/zh/

后来Netflix 开源了其关于混沌工程的实现 ChaosMonkey,以猴子的形象来代表在系统里出其不意的破坏者。

比如

  • 机器或者一个机房挂了
  • 一部分网络延迟严重
  • CPU、内存占用严重
  • 随机让某些服务异常或者响应延迟

再看Chaos 原则里提到的这些:

  • 当服务不可用时的不正确回滚设置;
  • 不当的超时设置导致的重试风暴;
  • 由于下游依赖的流量过载导致的服务中断;
  • 单点故障时的级联失败等。

我们自己在代码层面,在部署层面仅能关注应用的功能正常,但上述这些意想不到的出错,是我们在代码层面不太容易控制,也不易去测试的。

而ChaosMonkey 就是用来做这个的。所以,对于这些捣乱的猴子,我们是应该欢迎的,是不是像犀牛鸟之于犀牛?

关于ChaosMonkey,各个语言,各个公司也都有一些实现,其中Netflix的最出名。是go语言实现的。

在 Java Spring Boot 技术栈中,我发现一个容易理解和上手的实现。

https://github.com/codecentric/chaos-monkey-spring-boot

我们一起来看下如何上手以及它是怎样实现的。

1. 上手

添加maven 依赖

 
 
 
 
  1.   de.codecentric
  2.   chaos-monkey-spring-boot
  3.   2.3.0-SNAPSHOT

application.yml 中增加关于chaosmonkey的配置:

 
 
 
 
  1. chaos:
  2.   monkey:
  3.     enabled: true
  4.     assaults:
  5.       level: 1
  6.       latencyRangeStart: 1000
  7.       latencyRangeEnd: 10000
  8.       exceptionsActive: true
  9.       killApplicationActive: true
  10.     watcher:
  11.       repository: true
  12.       controller: true
  13. #      restController: true
  14. #      service: true

应用启动时,记得激活chaosmonkey的配置:

 
 
 
 
  1. java -jar your-app.jar --spring.profiles.active=chaos-monkey

再去请求你应用的controller,是不是发现异常产生了?这就是猴子在努力的捣乱中...

关于上面这些配置,再简单解释下:

你会发现chaos - monkey 配置下,除了 enabled,还有两项比较大的配置项,一个是Assault,一个是Watcher。

其中Assault代表是搞什么破坏,比如破坏类型有超时、内存占用、杀死进程、抛出异常等等

  • Latency Assault
  • Exception Assault
  • AppKiller Assault
  • Memory Assault

而Watcher 表示都要在哪些地方搞破坏。一个是What,一个是Where。

Watcher支持多种类型,比如Spring 常用的组件:

  • @Controller
  • @RestController
  • @Service
  • @Repository
  • @Component

那你说都 What 和 Where 了,怎么没有When?还真有Level就是。

chaos.monkey.enabled 用来打开和关闭ChaosMonkey。对应的配置中,除了设置Assault之外,不同的Assault也可以设置攻击的频率,配置项是chaos.monkey.assaults.level比如1代表每次请求都攻击,10代表每十次请求攻击一次。

chaos.monkey.assaults.latencyRangeStart 和chaos.monkey.assaults.latencyRangeEnd 这两个配置项用来配置LatencyAssault这个攻击的延迟时间值范围。

如下图所示,实际部署之后,每个ChaosMonkey会藏身于各个服务中,出其不意进行攻击。

这下子配置和使用就明白了。我们再来看看实现。

2.实现原理

aaa实际我们想一下,前面配置Watcher,后面决定进行攻击,那必须得是Watcher把它拦下来再攻击,所以在Spring 里拦截常用的,就是它:AOP。

原理如图所示:

以Controller 的拦截为例

 
 
 
 
  1. /** @author Benjamin Wilms */
  2. @Aspect
  3. @AllArgsConstructor
  4. @Slf4j
  5. public class SpringControllerAspect extends ChaosMonkeyBaseAspect {
  6.   private final ChaosMonkeyRequestScope chaosMonkeyRequestScope;
  7.   private MetricEventPublisher metricEventPublisher;
  8.   private WatcherProperties watcherProperties;
  9.   @Pointcut("within(@org.springframework.stereotype.Controller *)")
  10.   public void classAnnotatedWithControllerPointcut() {}
  11.   @Around(
  12.       "classAnnotatedWithControllerPointcut() && allPublicMethodPointcut() && !classInChaosMonkeyPackage()")
  13.   public Object intercept(ProceedingJoinPoint pjp) throws Throwable {
  14.     if (watcherProperties.isController()) {
  15.       log.debug("Watching public method on controller class: {}", pjp.getSignature());
  16.       if (metricEventPublisher != null) {
  17.         metricEventPublisher.publishMetricEvent(
  18.             calculatePointcut(pjp.toShortString()), MetricType.CONTROLLER);
  19.       }
  20.       MethodSignature signature = (MethodSignature) pjp.getSignature();
  21.       chaosMonkeyRequestScope.callChaosMonkey(createSignature(signature));
  22.     }
  23.     return pjp.proceed();
  24.   }
 
 
 
 
  1. public void callChaosMonkey(String simpleName) {
  2.     if (isEnabled() && isTrouble()) {
  3.       if (metricEventPublisher != null) {
  4.         metricEventPublisher.publishMetricEvent(MetricType.APPLICATION_REQ_COUNT, "type", "total");
  5.       }
  6.       // Custom watched services can be defined at runtime, if there are any, only
  7.       // these will be attacked!
  8.       if (chaosMonkeySettings.getAssaultProperties().isWatchedCustomServicesActive()) {
  9.         if (chaosMonkeySettings
  10.             .getAssaultProperties()
  11.             .getWatchedCustomServices()
  12.             .contains(simpleName)) {
  13.           // only all listed custom methods will be attacked
  14.           chooseAndRunAttack();
  15.         }
  16.       } else {
  17.         // default attack if no custom watched service is defined
  18.         chooseAndRunAttack();
  19.       }
  20.     }
  21.   }

这里是 Controller AOP的代码,基本没门槛。先判断 Controller 的开关是否打开,然后再看是否需要事件通知,紧接着,就是重头戏,召唤 Chaos Monkey 来搞破坏了。

注意这里,从激活的几种攻击方式里,选择一种去调用。

 
 
 
 
  1. private void chooseAndRunAttack() {
  2.     List activeAssaults =
  3.         assaults.stream().filter(ChaosMonkeyAssault::isActive).collect(Collectors.toList());
  4.     if (isEmpty(activeAssaults)) {
  5.       return;
  6.     }
  7.     getRandomFrom(activeAssaults).attack();  // 注意这里,从激活的几种攻击方式里,选择一种去调用。
  8.     if (metricEventPublisher != null) {
  9.       metricEventPublisher.publishMetricEvent(
  10.           MetricType.APPLICATION_REQ_COUNT, "type", "assaulted");
  11.     }
  12.   }

延迟攻击

比如LatencyAssault,就是要执行延迟攻击,此时,会生成一个随机的延迟时间

 
 
 
 
  1. public void attack() {
  2.     Logger.debug("Chaos Monkey - timeout");
  3.     atomicTimeoutGauge.set(determineLatency());
  4.     // metrics
  5.     if (metricEventPublisher != null) {
  6.       metricEventPublisher.publishMetricEvent(MetricType.LATENCY_ASSAULT);
  7.       metricEventPublisher.publishMetricEvent(MetricType.LATENCY_ASSAULT, atomicTimeoutGauge);
  8.     }
  9.     assaultExecutor.execute(atomicTimeoutGauge.get());
  10.   }

然后把这个值传在线程池中进行这个时间的

sleep。 assaultExecutor.execute(atomicTimeoutGauge.get());

 
 
 
 
  1. public class LatencyAssaultExecutor implements ChaosMonkeyLatencyAssaultExecutor {
  2.  public void execute(long durationInMillis) {
  3.    try {
  4.      Thread.sleep(durationInMillis);
  5.   } catch (InterruptedException e) {
  6.   }
  7. }
  8. }

Exception攻击

再来看Exception 攻击,攻击的时候,则是构造一个Exception 直接抛出

 
 
 
 
  1. @Override
  2.   public void attack() {
  3.     Logger.info("Chaos Monkey - exception");
  4.     AssaultException assaultException = this.settings.getAssaultProperties().getException();
  5.     assaultException.throwExceptionInstance();
  6.   }
 
 
 
 
  1. @SneakyThrows
  2.   public void throwExceptionInstance() {
  3.     Exception instance;
  4.     try {
  5.       Class exceptionClass = getExceptionClass();
  6.       if (arguments == null) {
  7.         Constructor constructor = exceptionClass.getConstructor();
  8.         instance = constructor.newInstance();
  9.       } else {
  10.         Constructor constructor =
  11.             exceptionClass.getConstructor(this.getExceptionArgumentTypes().toArray(new Class[0]));
  12.         instance =
  13.             constructor.newInstance(this.getExceptionArgumentValues().toArray(new Object[0]));
  14.       }
  15.     } catch (ReflectiveOperationException e) {
  16.       Logger.warn(
  17.           "Cannot instantiate the class for provided type: {}. Fallback: Throw RuntimeException",
  18.           type);
  19.       instance = new RuntimeException("Chaos Monkey - RuntimeException");
  20.     }
  21.     throw instance;  // 哈哈,直接抛出
  22.   }

KillApp 就直接执行应用的退出操作,System.exit.

本文转载自微信公众号「 Tomcat那些事儿」,可以通过以下二维码关注。转载本文请联系 Tomcat那些事儿公众号。

当前文章:快放开那些捣乱的猴子!
网站链接:http://www.hantingmc.com/qtweb/news32/256382.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联