故障注入原理探究
随着渠道API接入的渠道越来越多,用户量也在日益递增,由于渠道API本身的业务复杂性,以及依赖的中台服务之多,很有可能出现的问题会带来巨大的影响;只是通过常用的单元测试,集成测试,性能测试等来验证服务的稳定性已经远远不够;因此去年在平台的混沌工程基础上完成了渠道API和流量变现平台在mysql延迟,mq延迟,请求延迟,异常等场景下的故障注入演练;在演练中提前识别了潜在的问题并加以解决,同时在使用过程中对故障注入的原理进行了解并记录,方便后续根据业务本身的独特性对故障演练场景进行定制化生成。
1.chaosblade整体介绍实际上chaosblade是一个聚合的父项目,只是把所有实验场景入口封装到一起实现一个命令行工具,底层又去调用了各种场景下的具体实现,它将场景按领域实现封装成一个个单独的项目,这也符合不同平台、语言存在实现差异的情况,不仅可以使领域内场景标准化实现,而且非常方便场景水平和垂直扩展,通过遵循混沌实验模型,实现chaosbladecli统一调用。目前包含的项目如下:

Chaosblade-exec-jvm是通过JavaAgentattach方式来实现类的transform注入故障,底层使用了jvm-sandbox实现,通过插件的可拔插设计来扩展对不同java应用的支持。所以chaosblade-exec-jvm其实只是一个javaagent模块,不是一个可执行的工程,必须依赖jvm-sandbox。
2.2工程架构2.3模块管理2.4实现原理以servlet,api的/test接口延迟为例
2.5实验步骤2.5.1Agent挂载该命令下发后,将在目标jvm进程挂载Agent,触发SandboxModuleonLoad()事件,初始化PluginLifecycleListener来管理插件的生命周期,同时也触发SandboxModuleonActive()事件,加载部分插件,加载插件对应的ModelSpec.
publicvoidonLoad()throwsThrowable{("loadchaosblademodule");().setPluginLifecycleListener(this);();();//ChansBlade模块激活实现}publicvoidonActive()throwsThrowable{("activechaosblademodule");loadPlugins();}Plugin加载方式:•SandboxModuleonActive()事件•bladecreate命令CreateHandler;SandboxModuleonActive()事件,会注册ModelSpec;Plugin加载时,创建事件监听器(plugin),监听器会监听感兴趣的事件,如BeforeAdvice、AfterAdvice等,具体实现如下:
//加载插件publicvoidadd(PluginBeanplugin){PointCutpointCut=();if(pointCut==null){return;}StringenhancerName=().getClass().getSimpleName();//创建filterPointCut匹配Filterfilter=(enhancerName,pointCut);if(()){//事件监听intwatcherId=(filter,(plugin),,);((plugin),watcherId);}else{intwatcherId=(filter,(plugin),);((plugin),watcherId);}}PointCut匹配SandboxModuleonActive()事件触发Plugin加载后,SandboxEnhancerFactory创建filter,filter内部通过PointCut的ClassMatcher和MethodMatcher过滤。
触发Enhancer如果已经加载插件,此时目标应用匹配能匹配到filter后,EventListener已经可以被触发,但是chaosblade-exec-jvm内部通过StatusManager管理状态,所以故障能力不会被触发。
例如BeforeEventListener触发调用BeforeEnhancer的beforeAdvice方法,在().expExists(targetName)判断时候被中断,具体的实现如下:
(StringtargetName,ClassLoaderclassLoader,StringclassName,Objectobject,Methodmethod,Object[]methodArguments)throwsException{//StatusManagerif(!().expExists(targetName)){return;}EnhancerModelmodel=doBeforeAdvice(classLoader,className,object,method,methodArguments);if(model==null){return;}(targetName).setMethod(method).setObject(object).setMethodArguments(methodArguments);(model);}2.5.2创建混沌实验./bladecreateservlet--requestpath=/topicdelay--time=3000该命令下发后,触发SandboxModule@Http("/create")注解标记的方法,将事件分发给处理在判断必要的uid、target、action、model参数后调用handleInjection,handleInjection通过状态管理器注册本次实验,如果插件类型是PreCreateInjectionModelHandler的类型,将预处理一些东西。同时如果Action类型是DirectlyInjectionAction,那么将直接进行故障能力注入,如jvmoom等,如果不是将加载插件。如果ModelSpec是PreCreateInjectionModelHandler类型,且ActionSpec的类型是DirectlyInjectionAction类型,将直接进行故障能力注入,比如JvmOom故障能力,ActionSpec的类型不是DirectlyInjectionAction类型,将直接加载插件。
privateResponsehandleInjection(Stringsuid,Modelmodel,ModelSpecmodelSpec){//注册RegisterResultresult=(suid,model);if(()){//handleinjectiontry{applyPreInjectionModelHandler(suid,modelSpec,model);}catch(ExperimentExceptionex){(suid);(_ERROR,());}(());}(_INJECTION,"theexperimentexists");}注册成功后返回uid,如果本阶段直接进行故障能力注入了,或者自定义Enhanceradvice返回null,那么不通过Inject类触发故障。
2.5.3故障能力注入故障能力注入可以通过Inject注入,也可以通过DirectlyInjectionAction直接注入,直接注入不经过Inject类调用阶段,如jvmoom等;匹配参数包装自定义的Enhancer,如ServletEnhancer,把一些需要与命令行匹配的参数包装在MatcherModel里面,然后包装EnhancerModel返回,比如--requestpath=/index,那么requestpath等于requestURI去除contextPath。参数匹配在(model)阶段判断。
publicclassServletEnhancerextsBeforeEnhancer{privatestaticfinalLoggerLOOGER=();@OverridepublicEnhancerModeldoBeforeAdvice(ClassLoaderclassLoader,StringclassName,Objectobject,Methodmethod,Object[]methodArguments,StringtargetName)throwsException{//获取原方法的一些参数Objectrequest=methodArguments[0];StringqueryString=(request,"getQueryString",newObject[]{},false);StringcontextPath=(request,"getContextPath",newObject[]{},false);StringrequestURI=(request,"getRequestURI",newObject[]{},false);StringrequestMethod=(request,"getMethod",newObject[]{},false);StringrequestPath=(contextPath)?requestURI:(contextPath,"");//MatcherModelmatcherModel=newMatcherModel();(_STRING_KEY,queryString);(_KEY,requestMethod);(_PATH_KEY,requestPath);returnnewEnhancerModel(classLoader,matcherModel);}}参数匹配和能力注入(Inject调用)inject阶段首先获取StatusManager注册的实验,compare(model,enhancerModel)经常参数比对,失败后return,limitAndIncrease(statusMetric)判断--effect-count--effect-percent来控制影响的次数和百分比
publicstaticvoidinject(EnhancerModelenhancerModel)throwsInterruptProcessException{Stringtarget=();ListStatusMetricstatusMetrics=().getExpByTarget(target);for(StatusMetricstatusMetric:statusMetrics){Modelmodel=();if(!compare(model,enhancerModel)){continue;}try{booleanpass=limitAndIncrease(statusMetric);if(!pass){("Limitedby:{}",(model));break;}("Matchrule:{}",(model));(model);ModelSpecmodelSpec=().getModelSpec(target);ActionSpecactionSpec=(());().run(enhancerModel);}catch(InterruptProcessExceptione){throwe;}catch(UnsupportedReturnTypeExceptione){("unsupportedreturntypeforreturnexperiment",e);();}catch(Throwablee){("injectexception",e);();}break;}}故障触发由Inject触发,或者由DirectlyInjectionAction直接触发,最后调用自定义的ActionExecutor生成故障,如DefaultDelayExecutor,此时故障能力已经生效了。
publicvoidrun(EnhancerModelenhancerModel)throwsException{Stringtime=(());IntegersleepTimeInMillis=(time);intoffset=0;StringoffsetTime=(());if(!(offsetTime)){offset=(offsetTime);}TimeoutExecutortimeoutExecutor=();if(timeoutExecutor!=null){longtimeoutInMillis=();if(timeoutInMillis0timeoutInMillissleepTimeInMillis){sleep(timeoutInMillis,0);(enhancerModel);return;}}sleep(sleepTimeInMillis,offset);}publicvoidsleep(longtimeInMillis,intoffsetInMillis){Randomrandom=newRandom();intoffset=0;if(offsetInMillis0){offset=(offsetInMillis);}if(offset%2==0){timeInMillis=timeInMillis+offset;}else{timeInMillis=timeInMillis-offset;}if(timeInMillis=0){timeInMillis=offsetInMillis;}try{//触发延迟(timeInMillis);}catch(InterruptedExceptione){("runningdelayactioninterrupted",e);}}2.5.4销毁./bladedestroy52a27bafc252beee该命令下发后,触发SandboxModule@Http("/destory")注解标记的方法,将事件分发给处理。注销本次故障的状态。如果插件的ModelSpec是PreDestroyInjectionModelHandler类型,且ActionSpec的类型是DirectlyInjectionAction类型,停止故障能力注入,ActionSpec的类型不是DirectlyInjectionAction类型,将卸载插件。
publicResponsehandle(Requestrequest){Stringuid=("suid");Stringtarget=("target");Stringaction=("action");if((uid)){if((target)||(action)){(_PARAMETER,"lessnecessaryparameters,suchasuid,targetand"+"action");}//注销statusreturndestroy(target,action);}returndestroy(uid);}2.5.5卸载Agent./bladerevoke98e792c9a9a5dfea该命令下发后,触发SandboxModuleunload()事件,同时插件卸载。publicvoidonUnload()throwsThrowable{("unloadchaosblademodule");();();();("unloadchaosblademodulesuccessfully");}
总结以上便是chaosblade-exec-jvm的总体流程,同时也支持在chaosblade-exec-plugin模块下自定义自己的插件,结合自己的项目来定制化演练场景,通过模拟各种可能的故障情况,及早发现存在的漏洞和弱点,从而进行改进和完善。
作者介绍Hippo,信也科技服务端研发专家
出处:
推荐阅读
-
常听到的物联网是什么?它和我们的生活有什么关系?一文带你看懂
何为物联网?冰箱如何感知苹果数量?手机如何控制冰箱?冰箱、手机、快递公司,如何建立起联系?答案是:物联网(IOT)。一、物联网概述、涵义物联网=互联网+传感器利用条码、射频识别(RFID)、传感器、全球定位系统、激光扫描器等信息传感设备,按约定的协议,实现人与人、人与物、物与物(anything)在...
-
电机转速大小
作为电动车的核心,电机的性能至关重要,尤其是电机转速的大小直接影响电机的输出功率和效率。但是电机转速的大小受到多种因素的影响,包括电机的型号、电压、电流、负载等,在当下来看,也算是一项卡脖子技术了。但是,是不是转速越高并就意味着电机的性能越好?答案是NO,一台好的性能好的电机而是与具体应用需求和电机...
-
龙骨线图(主图叠加)
价位:MA(CLOSE,5),COLORYELLOW;X3:=ZSTJJ;VAR1:=CLOSE;VAR2:=VAR1*1.3;VAR3:=CLOSEREF(CLOSE,1)ANDCLOSE/X31+VAR1/100;VAR4:=CLOSEREF(CLOSE,1)ANDCLOSE/X31-VAR1/...
-
运动蚂蚁数字潮玩运动馆,带你穿越时空体验科技运动魅力!
在数字化浪潮的推动下,科技与生活深度融合,催生出前所未有的全新体验。科技与运动的结合,更是将传统的运动模式推向了新的高度。在这股创新潮流中,运动蚂蚁数字潮玩运动馆崭露头角,凭借其独特的科技理念和创新的数字内容,为运动爱好者们构建了一个充满乐趣和惊喜的数字运动世界。作为科技与运动完美结合的典范,运动蚂...