当前位置:首页 >  互联网 >  如果本阶段直接进行故障能力注入了

如果本阶段直接进行故障能力注入了

发布时间:2020-09-16 11:52编辑:小狐阅读: 482次 手机阅读

简介:总以为工程离你很远?但发生故障的那一刻不是由你来选择的,而是那一刻来选择你,你能做的就是为之做好准备。工程在阿里内部已经应用多年,而ChaosBlade这个开源项目是阿里多年来通过注入故障来对抗故障的经验结晶。为使大家更深入的了解其实现原理以及如何扩展自己所需要的组件故障注入,我们准备了一个系列对其做详细技术剖析:架构篇、模型篇、协议篇、字节码篇、插件篇以及实战篇。

如果本阶段直接进行故障能力注入了(图1)

前言

在分布式架构下,服务间的依赖日益复杂,很难评估单个服务故障对整个的影响,并且请求链路长,监控告警的不完善导致发现问题、定位问题难度增大,同时业务和技术迭代快,如何持续保障的稳定性和高可用性受到很大的。

我们知道发生故障的那一刻不是由你来选择的,而是那一刻来选择你,你能做的就是为之做好准备。所以构建稳定性很重要的一环是工程,在可控范围或环境下,通过故障注入,来持续提升的稳定性和高可用能力。

ChaosBlade是一款遵循工程实验原理,丰富故障场景实现,帮助分布式提升容错性和可恢复性的工程工具,可实现底层故障的注入,特点是操作简洁、无侵入、扩展性强。 其中 chaosblade-exec-jvm项目实现了零成本对 Java 应用服务故障注入。其不仅支持主流的框架组件,如 Dubbo、Servlet、RocketMQ 等,还支持指定任意类和方法注入延迟、异常以及通过编写 Java 和 Groovy 脚本来实现复杂的实验场景。

为使大家更深入的了解其实现原理以及如何扩展自己所需要的组件故障注入,分为六篇文章对其做详细技术剖析:架构篇、模型篇、协议篇、字节码篇、插件篇以及实战篇。本文将详细介绍 chaosblade-exec-jvm 的整体架构设计,使用户对 chaosblade-exec-jvm 有一定的了解。

设计

如果本阶段直接进行故障能力注入了(图2)

原理剖析

在日常后台应用中,我们经常需要 API 接口给客户端,而这些 API 接口不可避免的由于网络、负载等原因存在超时、异常等情况。使用 Java 语言时,HTTP 协议我们通常使用 Servlet 来 API 接口,chaosblade-exec-jvm 支持 Servlet 插件,注入超时、自定义异常等故障能力。本篇将通过给 Servlet API 接口 注入延迟故障能力为例,分析 chaosblade-exec-jvm 故障注入的流程。

对 Servlet API 接口/topic延迟3秒,步骤如下:

// 挂载 Agent

// 注入故障能力

blade create servlet --requestpath=/topic delay --time=3000 --method=post

// 撤销故障能力

blade destroy 52a27bafc252beee

// 卸载 Agent

blade revoke 98e792c9a9a5dfea

1. 执行过程

以下通过 Servlet 请求延迟为例,详细介绍故障注入的过程。

如果本阶段直接进行故障能力注入了(图3)

ChaosBlade 下发挂载命令,挂载 Sandbox 到应用进程,激活 Java Agent,例如blade p jvm --pid 888。

挂载 Sandbox 后加载 chaosblade-exec-jvm 模块,加载插件,如 ServletPlugin、DubboPlugin 等。

匹配 ServletPlugin 插件的切点、注册事件监听,HttpServlet 的 doPost、doGet 方法。

ChaosBlade 下发故障规则命令blade create servlet --requestpath=/topic delay --time=3000 --method=post。

匹配故障规则成功后,触发故障,如延迟故障、自定义异常抛出等。

ChaosBlade 下发命令卸载 JavaAgent,如blade revoke 98e792c9a9a5dfea。

2. 代码剖析

1挂载 Agent

该命令下发后,将在目标 Java 应用进程挂在 Agent ,触发 SandboxModule 事件,初始化 PluginLifecycleListener 来插件的生命周期,同时也触发 SandboxModule onActive 事件,加载部分插件,加载插件对应的 ModelSpec。

// Agent 加载事件

public void throws Throwable

ManagerFactory.getListenerManager.setPluginLifecycleListener(this)

dispatchService.load。

ManagerFactory.load。

// ChaosBlade 模块激活实现

public void onActive throws Throwable

loadPlugins。

2加载 Plugin

如果本阶段直接进行故障能力注入了(图4)

Plugin 加载时,创建事件 SandboxEnhancerFactory.createAfterEventListener(plugin) ,会监听感兴趣的事件,如 BeforeAdvice、AfterAdvice 等,具体实现如下:

// 加载插件

public void add(PluginBean plugin)

PointCut pointCut = plugin.getPointCut。

if (pointCut == null)

return。

String enhancerName = plugin.getEnhancer.getClass.getSimpleName。

// 创建filter PointCut匹配

Filter filter = SandboxEnhancerFactory.createFilter(enhancerName, pointCut)

// 事件监听

int watcherId = moduleEventWatcher.watch(filter, SandboxEnhancerFactory.createBeforeEventListener(plugin) Event.Type.BEFORE)

watchIds.put(PluginUtil.getIdentifier(plugin) watcherId)

3匹配 PointCut

SandboxModule onActive 事件触发 Plugin 加载后,SandboxEnhancerFactory 创建 Filter,Filter 内部通过 PointCut 的 ClassMatcher 和 MethodMatcher 过滤。

public static Filter createFilter(final String enhancerClassName, final PointCut pointCut)

return new Filter

@Override

public boolean doClassFilterint access, String javaClassName, String superClassTypeJavaClassName。

String interfaceTypeJavaClassNameArray。

String annotationTypeJavaClassNameArray

// ClassMatcher 匹配

ClassMatcher classMatcher = pointCut.getClassMatcher。

@Override

public boolean doMethodFilterint access, String javaMethodName。

String parameterTypeJavaClassNameArray。

String throwsTypeJavaClassNameArray。

String annotationTypeJavaClassNameArray

// MethodMatcher 匹配

MethodMatcher methodMatcher = pointCut.getMethodMatcher。

4触发 Enhancer

如果已经加载插件,此时目标应用匹配能匹配到 Filter 后,EventListener 已经可以被触发,但是 chaosblade-exec-jvm 内部通过 StatusManager 状态,所以故障能力不会被触发。

例如 BeforeEventListener 触发调用 BeforeEnhancer 的 beforeAdvice 方法,在ManagerFactory.getStatusManager.expExists(targetName) 判断时候被中断,具体的实现如下:

public void beforeAdviceString targetName。

ClassLoader classLoader。

String className。

Object object。

Method method。

Object methodArguments throws Exception

// 判断实验的状态

if ManagerFactory.getStatusManager.expExiststargetName

return。

EnhancerModel model = doBeforeAdvice(classLoader, className, object, method, methodArguments)

if (model == null)

return。

// 注入阶段

Injector.inject(model)

5创建实验

该命令下发后,触发 SandboxModule @Http“/create” 注解标记的方法,将事件分发给 com.alibaba.chaosblade.exec.service.handler.CreateHandler 处理

在判断必要的 uid、target、action、model 参数后调用 handleInjection,handleInjection 通过状态器注册本次实验,如果插件类型是 PreCreateInjectionModelHandler 类型,将预处理一些东西。同是如果 Action 类型是 DirectlyInjectionAction,那么将直接进行故障能力注入,且不需要走 Enhancer,如 JVM OOM 故障能力等。

public Response handle(Request request)

if (unloaded)

return Response.ofFailureCode.ILLEGAL_STATE, “the agent is uninstalling”。

// 检查 suid,suid 是一次实验的上下文ID

String suid = request.getParam“suid”。

return handleInjection(suid, model, modelSpec)

private Response handleInjection(String suid, Model model, ModelSpec modelSpec)

RegisterResult result = this.statusManager.registerExp(suid, model)

if (result.isSuccess)

// 判断是否预创建

applyPreInjectionModelHandler(suid, modelSpec, model)

ModelSpec

private void applyPreInjectionModelHandler(String suid, ModelSpec modelSpec, Model model)

throws ExperimentException

if (modelSpec instanceof PreCreateInjectionModelHandler)

PreCreateInjectionModelHandler)modelSpec).preCreate(suid, model)

DirectlyInjectionAction

如果 ModelSpec 是 PreCreateInjectionModelHandler 类型,且 ActionSpec 的类型是 DirectlyInjectionAction 类型,将直接进行故障能力注入,比如 JvmOom 故障能力,ActionSpec 的类型不是 DirectlyInjectionAction 类型,将加载插件。

如果本阶段直接进行故障能力注入了(图5)

private Response handleInjection(String suid, Model model, ModelSpec modelSpec)

// 注册

RegisterResult result = this.statusManager.registerExp(suid, model)

if (result.isSuccess)

// handle injection

try

applyPreInjectionModelHandler(suid, modelSpec, model)

} catch (ExperimentException ex) {

this.statusManager.removeExp(suid)

return Response.ofFailure(Response.Code.SERVER_ERROR, ex.getMessage)

return Response.ofSuccess(model.toString)

return Response.ofFailureResponse.Code.DUPLICATE_INJECTION, “the experiment exists”。

注册成功后返回 uid,如果本阶段直接进行故障能力注入了,或者自定义 Enhancer advice 返回 null,那么后不通过Inject 类触发故障。

6注入故障能力

故障能力注入的方式,最终都是调用 ActionExecutor 执行故障能力。

通过 Injector 注入。

DirectlyInjectionAction 直接注入,直接注入不进过 Inject 类调用阶段,如果 JVM OOM 故障能力等。

DirectlyInjectionAction 直接注入不经过Enhancer参数包装匹配直接到故障触发 ActionExecutor 执行阶段,如果是Injector 注入此时因为 StatusManager 已经注册了实验,当事件再次出发后ManagerFactory.getStatusManager.expExists(targetName) 的判断不会被中断,继续往下走,到了自定义的 Enhancer ,在自定义的 Enhancer 里面可以拿到原方法的参数、类型等,甚至可以反射调原类型的其他方法,这样做风险较大,一般在这里往往是取一些成员变量或者 get 方法等,用于 Inject 阶段参数匹配。

7包装匹配参数

自定义的 Enhancer,如 ServletEnhancer,把一些需要与命令行匹配的参数 包装在 MatcherMode 里面,包装 EnhancerModel 比如 --requestpath = /index ,那么requestpath 等于 requestURI;--querystring=“name=xx” 做自定义匹配。参数包装好后,在 Injector.inject(model) 阶段判断。

public EnhancerModel doBeforeAdviceClassLoader classLoader, String className, Object object。

Method method, Object methodArguments

throws Exception

Object request = methodArguments【0】

String requestURI = ReflectUtil.invokeMethod(request, ServletConstant.GET_REQUEST_URI, new Object{}, false)

String requestMethod = ReflectUtil.invokeMethod(request, ServletConstant.GET_METHOD, new Object{}, false)

MatcherModel matcherModel = new MatcherModel。

matcherModel.add(ServletConstant.METHOD_KEY, requestMethod)

matcherModel.add(ServletConstant.REQUEST_PATH_KEY, requestURI)

MapqueryString = getQueryString(requestMethod, request)

EnhancerModel enhancerModel = new EnhancerModel(classLoader, matcherModel)

// 自定义参数匹配

enhancerModel.addCustomMatcher(ServletConstant.QUERY_STRING_KEY, queryString, ServletParamsMatcher.getInstance)

return enhancerModel。

8判断前置条件

Inject 阶段首先获取 StatusManage 注册的实验,compare(model, enhancerModel) 做参数比对,比对失败limitAndIncrease(statusMetric) 判断 --effect-count --effect-percent 来控制影响的次数和百分比

public static void inject(EnhancerModel enhancerModel) throws InterruptProcessException

String target = enhancerModel.getTarget。

ListstatusMetrics = ManagerFactory.getStatusManager.getExpByTarget

target

for (StatusMetric statusMetric : statusMetrics)

Model model = statusMetric.getModel。

// 匹配命令行输入参数

if comparemodel, enhancerModel

continue。

// 累加攻击次数和判断攻击次数是否到达 effect count

boolean pass = limitAndIncrease(statusMetric)

if (pass)

break。

enhancerModel.merge(model)

ModelSpec modelSpec = ManagerFactory.getModelSpecManager.getModelSpec(target)

ActionSpec actionSpec = modelSpec.getActionSpec(model.getActionName)

// ActionExecutor执行故障能力

actionSpec.getActionExecutor.run(enhancerModel)

break。

9触发故障能力

由 Inject 触发,或者由 DirectlyInjectionAction 直接触发,最后调用自定义的 ActionExecutor 生成故障,如 DefaultDelayExecutor ,此时故障能力已经生效了。

public void run(EnhancerModel enhancerModel) throws Exception

String time = enhancerModel.getActionFlag(timeFlagSpec.getName)

Integer sleepTimeInMillis = Integer.valueOf(time)

// 触发延迟

TimeUnit.MILLISECONDS.sleep(sleepTimeInMillis)

3. 销毁实验

blade destroy 52a27bafc252beee

该命令下发后,触发 SandboxModule @Http“/destory” 注解标记的方法,将事件分发给 com.alibaba.chaosblade.exec.service.handler.DestroyHandler 处理,注销本次故障的状态,此时再次触发 Enchaner 后,StatusManger判定实验状态已经销毁,不会在进行故障能力注入

// StatusManger 判断实验状态

if ManagerFactory.getStatusManager.expExiststargetName

return。

如果插件的 ModelSpec 是 PreDestroyInjectionModelHandler 类型,且 ActionSpec 的类型是 DirectlyInjectionAction 类型,停止故障能力注入,ActionSpec 的类型不是 DirectlyInjectionAction 类型,将卸载插件。

// DestroyHandler 注销实验状态

public Response handle(Request request)

String uid = request.getParam“suid”。

// 判断 uid

if StringUtil.isBlankuid

if StringUtil.isBlanktarget StringUtil.isBlankaction

return false。

// 注销status

return destroy(target, action)

return destroy(uid)

4. 卸载 Agent

blade revoke 98e792c9a9a5dfea

该命令下发后,触发 SandboxModule unload 事件,同时插件卸载,完全回收 Agent 创建的各种资源。

public void onUnload throws Throwable

dispatchService.unload。

ManagerFactory.unload。

watchIds.clear。

总结

本文以 Servlet 场景为例,详细介绍了 chaosblade-exec-jvm 项目架构设计和实现原理,后续将通过模型篇、协议篇、字节码篇、插件篇以及实战篇深入介绍此项目,使读者达到可以快速扩展自己所需插件的目的。

ChaosBlade 项目作为一个工程实验工具,不仅使用简洁,而且还支持丰富的实验场景且扩展场景简单,支持的场景领域如下:

基础资源:比如 CPU、内存、网络、磁盘、进程等实验场景。

Java 应用:比如数据库、缓存、JVM 本身、微服务等,还可以指定任意类方法注入各种复杂的实验场景。

C++ 应用:比如指定任意方法或某行代码注入延迟、变量和返回值篡改等实验场景。

Docker 容器:比如杀容器、容器内 CPU、内存、网络、磁盘、进程等实验场景。

Kubernetes 平台:比如节点上 CPU、内存、网络、磁盘、进程实验场景,Pod 网络和 Pod 本身实验场景如杀 Pod,Pod IO 异常,容器的实验场景如上述的 Docker 容器实验场景。

云资源:比如阿里云 ECS 宕机等实验场景。

本文相关词条概念解析:

故障

故障(Failure,fault),设备在工作过程中,因某种原因“丧失规定功能”或危害安全的现象。失效有时也被称为一种故障,也可能是设备工作中丢失也是一种故障,但这些故障却是可修复的。规定功能是指在设备的技术文件中明确规定的功能。徐迟《入峡记》:“又一次,调速器出了故障,机匠在黑板上写了‘调速器跳舞’五个字。孙犁《秀露集·耕堂读书记(二)》:“但究竟发生了什么故障,他从不具体说明。

标签:
  • 网友评论
相关文章:

互联网本月排行

互联网精选