故障注入原理探究
随着渠道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,信也科技服务端研发专家
出处:
推荐阅读
-
高质量发展调研行|福州:科技助力“海洋牧场”提质增效
在福州市连江县定海湾广袤的海面上,一台长92米、宽36米的“大块头”引人注目。它就是半潜式渔旅融合深远海养殖平台“闽投1号”,也是我国海水养殖业向深远海、机械化、智能化转变的“探路者”。“闽投1号”运营方福州福鱼荟海洋科技有限公司总经理许航介绍说,与传统网箱7至8米的养殖深度不同,“闽投1号”采用海...
-
原来逆变器电感线圈是有这些作用
逆变器电感线圈也叫逆变器扼流圈,一体成型电感、绕线电感、磁环电感。它是用绝缘导线,例如漆包线、纱包线等绕制而成的电磁感应元件。在板卡设计中,逆变器电感线圈的作用也是起EMI滤波的作用,用于抑制高速信号线产生的电磁波向外辐射发射。滤波电感一般是采用铁氧体材料制成,所以又称铁氧体磁环,简称磁环。磁环在不...
-
兴业银行将调整组织架构 重组科技、零售、企金三大条线
21世纪经济报道记者辛继召深圳报道“经过前期数月集思广益,我们开始实施科技、零售、企金三大条线的改革。”3月25日上午,在兴业银行2021年度业绩发布会上,兴业银行董事长吕家进说。吕家进说,科技改革重在强化科技的支撑和引领作用,提升科技规划、建设、管理、安全能力,把原有的“一办、一部门、一公司”组织...
-
诠网科技 | 整站优化怎么做?优化思路是什么?
企业在移动互联网的发展,明白只有依靠有力的优化方式才可以真正的实现盈利。但是,对于互联网可利用的手段非常多,让企业这些门外汉有点不知所措。如整站优化,知道可以帮助企业网站提高排名、带来流量,却不知道具体应该如何做好整站优化。因此,接下来的内容不妨一起来看看关于整站优化的相关知识点吧!一、整站优化怎么...