故障注入原理探究
随着渠道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,信也科技服务端研发专家
出处:
推荐阅读
-
中国六氟钛酸行业现状分析及投资趋势预测报告2024-2030年
中国六氟钛酸行业现状分析及投资趋势预测报告2024-2030年【出版机构】:中赢信合研究网【内容部分有删减·详细可参中赢信合研究网出版完整信息!】1六氟钛酸市场概述1.1六氟钛酸行业概述及统计范围1.2按照不同产品类型,六氟钛酸主要可以分为如下几个类别1.2.1不同产品类型六氟钛酸增长趋势2019V...
-
你知道室分系统中常用无源器件有哪些?一起了解下
上回我们说了有源器件,这回聊聊无源器件,作为通信最重要组网连接器件,无源器件好坏,直接影响到信号的稳定,特别在当下,错综复杂的信号网络,2、3、4G网络在同一个天馈系统里,也有多家运营商的网络同时存在一个天馈系统中,所以相对来说,信源多源化,直接影响到信号的稳定,高性的无源器件成为当下主流,今天我了...
-
紫罗兰家纺:科技创新 生机无限
当家纺业仍被冠以“传统”“低端”与“劳动密集型”的标签时,紫罗兰家纺科技股份有限公司已悄然完成了向创新型科技企业的转型。2016年9月29日,全国家纺行业首个“企业院士工作站”、首个“众创空间”同步落户紫罗兰家纺,成为全国家纺企业转型升级的成功样本。“其实,我们与院士的合作早在数年前就开始了,紫罗兰...
-
混合动力汽车的“串”、“并”、“混”
混合动力汽车主要由以下三种结构。第一种:并联式混合动力,是指燃油发动机和电机可以分别独立地向汽车提供驱动力,两种动力耦合后共同驱动汽车。即使一种动力停止工作,也不会影响另一种动力继续驱动汽车。两种动力装置之间比较独立,都有自己单独的车载能量源,即燃油箱和动力蓄电池,因此称其为并联式混合动力。并联式混...