本发明涉及微服务系统中的故障根因定位领域,具体涉及一种基于故障特征比较的微服务系统故障根因定位方法。
技术背景
微服务系统(microservicesystem)是一种架构概念,它将单一应用程序划分为一组小的服务节点(简称节点),每个服务节点是一个独立的进程,只针对特定的业务和功能,可以独立部署、编译和运行。服务节点之间采用轻量级的通信机制进行通信,相互协调、互相配合,为用户提供完整的服务。
微服务架构能够减少系统的耦合性,提高了系统的模块性、抽象性和拓展性,提高web程序的开发效率,因此被广泛使用。在微服务系统中,由于节点数量、种类多,而且节点之间存在频繁的服务调用,容易存在漏洞,诸如dos攻击这样的破坏系统可用性的攻击会造成节点cpu负载高、网络延迟高,导致节点响应时间加长,影响系统运行。
由于服务节点之间存在服务调用关系,当系统中的某个节点出现故障时,故障会沿着服务调用链反向传播,影响到上游节点,最终扩散到整个系统,影响用户使用,该节点称为故障根节点。运维人员需要快速定位到故障根节点,以减少故障排除时间,减少由于系统不可用导致的经济损失。但是由于微服务系统中服务节点众多,监控数据量巨大,且服务节点之间服务调用关系复杂,难以快速定位到故障根节点。目前已经有elk[elasticsearch网址:
https://www.elastic.co/cn/elasticsearch.logstash网址:
https://www.elastic.co/cn/logstash.kibana网址:
https://www.elastic.co/cn/kibana.]这样的日志分析工具,但它们只能起到简单的辅助功能,帮助运维人员进行可视化的日志分析工作。
有许多相关研究致力于开发一种自动化的故障根节点定位系统,目前主流的故障定位方法通过构造系统模型来实现对复杂系统的表示,在系统模型的基础上通过人工智能和机器学习相关算法进行故障定位,根据系统模型的不同类型和特点,可以将相关研究分为如下三类。
(1)基于跟踪的故障定位方法
基于跟踪的故障定位方法从事件和函数层次对单体程序进行分析,它们通过跟踪事件和函数之间的关系,构造系统的总体模型,从而辅助运维人员进行故障定位。magpie[barhamp,donnellya,isaacsr,etal.usingmagpieforrequestextractionandworkloadmodelling[c]//osdi.2004,4:18-18.译为:使用mappie进行请求提取和工作负载建模]监控整个系统,记录内核、中间件和应用组件产生的事件,通过将这些事件联系起来并捕获控制流,它能构建一个代表整个系统的简洁的模型,并以此用于系统性能测试,找出性能瓶颈。x-trace[fonsecar,porterg,katzrh,etal.x-trace:apervasivenetworktracingframework[c]//4th{usenix}symposiumonnetworkedsystemsdesign&implementation({nsdi}07).2007.译为:x-trace:一个普适的网络跟踪框架]能够分析跨应用程序、管理域和网络机制的系统,并给出概略模型。darc[traegera,derasi,zadoke.darc:dynamicanalysisofrootcausesoflatencydistributions[c]//proceedingsofthe2008acmsigmetricsinternationalconferenceonmeasurementandmodelingofcomputersystems.2008:277-288.译为:darc:延迟分布根本原因的动态分析]能够通过运行时延分析来构建系统的服务调用图,并在此基础上进行根因定位。insight[nguyenh,deandj,kck,etal.insight:in-situonlineservicefailurepathinferenceinproductioncomputinginfrastructures[c]//2014{usenix}annualtechnicalconference({usenix}{atc}14).2014:269-280.译为:洞察:生产计算基础设施中的在线服务故障路径推断]能够在检测到失败用户请求之后创建失败请求的执行路径,并根据此路径定位到失败根因。总的来说,这些方法深入到系统内部来进行详细的分析,但也给系统带来了较大的负担,同时运维人员需要十分了解系统才能有效地部署这些方法,因此这些方法都不具有通用性,且对运维人员的专业水平有很高要求。
(2)静态的故障定位方法
静态方法依赖于静态的信息来对系统进行分析,例如系统模型、人为制定的阈值和事先生成的系统拓扑图。ε-diagnosis[shanh,cheny,liuh,etal.ε-diagnosis:unsupervisedandreal-timediagnosisofsmall-windowlong-taillatencyinlarge-scalemicroserviceplatforms[c]//theworldwidewebconference.2019:3215-3222.译为:ε-诊断:大型微服务平台小窗口长尾延迟的无监督实时诊断]通过人为确定的阈值来判断系统是否出现故障,以此启动故障定位。gao[gaoj,jiangg,chenh,etal.modelingprobabilisticmeasurementcorrelationsforproblemdeterminationinlarge-scaledistributedsystems[c]//200929thieeeinternationalconferenceondistributedcomputingsystems.ieee,2009:623-630.译为:大规模分布式系统问题确定的概率度量相关性建模]等人基于马尔可夫特征构造系统度量之间的转换模型,以此来表示系统。vscope[wangc,rayania,eisenhauerg,etal.vscope:middlewarefortroubleshootingtime-sensitivedatacenterapplications[c]//acm/ifip/usenixinternationalconferenceondistributedsystemsplatformsandopendistributedprocessing.springer,berlin,heidelberg,2012:121-141.译为:vscope:解决对时间敏感的数据中心应用程序中发生的故障的中间件]基于事先生成的拓扑图和函数,在系统运行期间不间断的监控和追踪系统各部分之间的交互。总的来说,静态方法依赖于特定时间内保持不变的静态信息来对系统进行分析,但是微服务系统是高度动态的,因此静态方法难以应用于微服务系统。
(3)动态的故障定位方法
动态的故障定位方法依赖于实时跟新的系统模型。automap[mam,xuj,wangy,etal.automap:diagnoseyourmicroservice-basedwebapplicationsautomatically[c]//proceedingsofthewebconference2020.2020:246-258.译为:automap:自动诊断基于微服务的web应用程序]根据动态生成并实时跟新的异常行为图来分析故障在微服务系统中的传播。causeinfer[chenp,qiy,houd.causeinfer:automatedend-to-endperformancediagnosiswithhierarchicalcausalitygraphincloudenvironment[j].ieeetransactionsonservicescomputing,2016,12(2):214-230.译为:causeinfer:云环境下基于层次因果图的端到端性能自动诊断]进一步生成系统的双层因果图,从而在定位的时间和粒度间找到平衡。但是这些方法都只分析单节点本身的特征,所以它们的故障定位都是单节点层次的,难以完全表达涉及到多节点的故障的特征。与此相对,weng[wengj,wangjh,yangj,etal.rootcauseanalysisofanomaliesofmultitierservicesinpublicclouds[j].ieee/acmtransactionsonnetworking,2018,26(4):1646-1659.译为:公共云中多层服务异常的根本原因分析]等人首次将节点之间的资源争夺关系纳入到系统模型中,从而对同一物理主机上的节点进行了综合考虑。li[liz,luoc,zhaoy,etal.genericandrobustlocalizationofmulti-dimensionalrootcauses[c]//2019ieee30thinternationalsymposiumonsoftwarereliabilityengineering(issre).ieee,2019:47-57.译为:多维根源的通用和健壮定位]等人根据涟漪效应将受到同一根因影响的故障节点进行聚类,从而缩小根因定位范围。这两种方法涉及到了对故障的多节点分析,但是它们都只是针对某一特定情况,缺乏普遍性。
综上所述,如何适应微服务系统高复杂性、高动态性的特点,提供一种准确、负担低的故障根因定位方法是本领域技术人员正在探讨的热点问题。针对目前方法的缺陷,需要提供一种基于节点间服务调用数据的、动态的、基于多节点分析的故障根因定位方法。
技术实现要素:
本发明要解决的技术问题是提供一种基于故障特征比较的微服务系统故障根因定位方法。针对微服务系统动态性强、故障涉及节点多导致根因定位困难的问题,提供一种根因定位方法,在实时生成的微服务系统拓扑的基础上,从多节点级别提取故障特征,从而实现对微服务系统故障根因的节点级别定位,帮助运维人员实现对故障的定点排除。
为了解决上述技术问题,针对微服务系统存在高度的动态性,静态模型难以应用的特点,本发明的技术方案为:首先通过微服务系统服务调用数据实时生成系统拓扑图。然后在系统拓扑图的基础上,分别提取已知故障和未知故障的特征,同时对提取的故障特征进行形式化表示。最后通过将未知故障的特征与已知故障的特征进行比较分析,得出未知故障的根节点和故障种类。
本发明包括以下步骤:
第一步,构建故障根因定位系统,故障根因定位系统由微服务系统监控数据文件、数据读取模块、服务依赖图构建模块、异常判断模块、显式、隐式节点确认模块、编码模块、深度搜索模块、拓扑模块、编码比较与分析模块、编码读写模块、已知故障记录文件、标准编码文件组成。
微服务系统监控数据文件存储微服务系统监控数据,微服务系统监控数据文件为.csv文件,包括trace_csf.csv,trace_fly_remote.csv,trace_jdbc.csv,trace_local.csv,trace_osb.csv,trace_remote_process.csv六个文件,这六个文件分别包含了不同类型的服务调用(不同类型的服务调用的区别在于服务调用的服务节点不同),这六个文件中的每一条监控数据表示一次服务调用,每一条监控数据包含calltype,servicename,starttime,elapsedtime,success,id,traceid,pid,cmdb_id九个字段。微服务系统监控数据包括已知故障数据和未知故障数据,已知故障和未知故障发生在不同的时间段,已知故障为经过运维人员人工排查并处理的故障,其故障根节点和故障类型已知,未知故障是未经过运维人员人工排查及处理的故障,其故障根节点和故障类型未知,需要使用本发明进行故障根节点定位。
由于监控数据包含九个字段,使用数据特征九元组表示每条监控数据,数据特征九元组为(calltype,servicename,starttime,elapsedtime,success,id,traceid,pid,cmdb_id),其中calltype表示服务调用的类型,servicename表示服务调用的服务名称,starttime表示服务调用发起的时间,为时间戳格式,elapsedtime表示服务调用的延迟时间,即发起该服务调用到收到服务调用结果所花费的时间,success等于0或1,表示该次服务调用是否成功,id表示该次服务调用的编号,traceid表示该次服务调用所对应的用户请求(当用户发起一次用户请求时,微服务系统内的节点间会发起多次服务调用,以提供完整的服务,由于对应于同一个用户请求,这些服务调用的traceid字段的值相同),pid表示该次服务调用的父服务调用的编号,cmdb_id表示发起该次服务调用的微服务节点的编号。
数据读取模块与微服务系统监控数据文件、服务依赖图构建模块相连,数据读取模块从微服务系统监控数据文件中读取异常发生相应时间段(如15:00系统产生异常,无法正常响应用户请求,数据读取模块从微服务系统监控数据文件读取14:55-15:05时间段内的监控数据,以定位故障根节点,所述异常发生相应时间段指:t-▲t至t+▲t,t为异常发生时间,▲t一般为5-10分钟)的监控数据,将监控数据发送给服务依赖图构建模块。
服务依赖图构建模块与数据读取模块、异常判断模块相连,服务依赖图构建模块从数据读取模块获得异常发生相应时间段内的监控数据,将监控数据组织成服务依赖图列表。服务依赖图的定义为:当用户发起一次用户请求时,微服务系统内的节点间会发起多次服务调用,以提供完整的服务,这些服务调用的traceid字段的数值相同,将这些服务调用组织为一个服务依赖图,该服务依赖图由二元组(traceid,calls)表示,其中traceid与数据特征九元组中的traceid意义相同,表示该服务依赖图中的服务调用对应的用户请求,calls为该服务依赖图中包含的服务调用的集合。假设该时间段内发生了m次用户请求,则会产生m个服务依赖图,服务依赖图构建模块将这m个服务依赖图储存在服务依赖图列表中,并将服务依赖图列表发送给异常判断模块,m为正整数。
异常判断模块与服务依赖图构建模块、显式、隐式节点确认模块相连。异常判断模块从服务依赖图构建模块获取服务依赖图列表,筛选服务依赖图列表中存在异常的服务依赖图,并构造为异常服务依赖图四元组(traceid,calls,abnormal_calls_abstract,abnormal_calls),其中traceid为该存在异常的服务依赖图中的服务调用(异常服务依赖图既包含异常服务调用也包含正常服务调用,受到故障根节点影响的服务调用而导致响应时间大于正常值的服务调用为异常服务调用,未受到故障根节点影响的服务调用为正常服务调用)的traceid字段的值,calls为该存在异常的服务依赖图中包含的服务调用的集合,abnormal_calls_abstract为该存在异常的服务依赖图中异常服务调用的抽象表示,abnormal_calls为该存在异常的服务依赖图中异常服务调用的集合,最终获得异常服务依赖图列表,将异常服务依赖图列表传送给显式、隐式节点确认模块。
显式、隐式节点确认模块与异常判断模块、编码模块、深度搜索模块相连。显示节点指异常服务调用的发起节点,隐式节点指异常服务调用的被服务调用节点。显式、隐式节点确认模块从异常判断模块获得异常服务依赖图列表,根据异常服务依赖图列表判断显式节点和隐式节点,然后将显式节点列表和隐式节点列表发送给编码模块和深度搜索模块。
已知故障记录文件与编码读写模块相连,已知故障记录文件存储已知故障的信息,已知故障记录文件中记录了通过人工排查得到的已知故障信息,每一条信息对应一次已知故障,每一条信息可以表示为已知故障信息四元组(cmdb_id,content,fault_start_time,fault_end_time)其中cmdb_id与数据特征九元组中的cmdb_id字段含义相同,为服务节点的编号,用以记录该次已知故障的故障根节点,content为故障种类,fault_start_time为故障开始时间,fault_end_time为故障结束时间。
深度搜索模块与显式、隐式节点确认模块、编码模块相连,当进行未知故障分析时,深度搜索模块从显式、隐式节点确认模块出获得显式、隐式节点列表,找出其中处于底层的显式、隐式节点,形成潜在故障根节点列表,传送给编码模块。
编码模块与显式、隐式节点确认模块、深度搜索模块、拓扑模块、编码读写模块、编码比较与分析模块相连。当进行已知故障分析时,编码模块从显式、隐式节点确认模块获得显式节点列表和隐式节点列表,从拓扑模块获得系统拓扑信息,从已知故障记录文件获取已知故障信息。编码模块根据显式节点列表、隐式节点列表和已知故障信息对故障根节点进行编码得到标准编码,将标准编码发送给编码读写模块。当进行未知故障分析时,编码模块从深度搜索模块获取潜在故障根节点列表,从显式、隐式节点确认模块获得显式节点列表、隐式节点列表,从拓扑模块获得系统拓扑信息,对所有的潜在故障根节点进行编码,然后将潜在故障根节点编码发送给编码比较和分析模块。
标准编码文件与编码读写模块相连,标准编码文件存储标准编码。标准编码存储形式为字典,形式为{code1:[cmdb_id1,content1],code2:[cmdb_id2,content2]......},其中code1,code2.....为故障编码,cmdb_id1,cmdb_id2......为故障根节点(属于微服务节点)的编号(cmdb_id为服务节点的编号,是服务节点的固有属性,所有节点都有cmdb_id,因此故障根节点也有cmdb_id),content1,content2......为故障种类。
编码读写模块与编码模块、标准编码文件、编码比较与分析模块相连,在进行已知故障分析时,编码读写模块从编码模块获得标准编码,储存在标准编码文件中。当进行未知故障分析时,编码读写模块从标准编码文件获取标准编码,发送给编码比较与分析模块。
拓扑模块与编码模块相连,拓扑模块采用字典topology存储微服务系统的拓扑信息,包含所有节点的子节点、父节点、双向节点信息,topology是一个python字典类型数据,形式为{node1_c:node1_child_nodes,node1_p:node1_parent_nodes,node1_two:node1_bidirection_nodes,node2_c:node2_child_nodes,node2_p:node2_parent_nodes,node2_two:node2_bidirection_nodes......},其中node1_c,node1_p,node1_two,node2_c,node2_p,node2_two......为字符串类型数据,node1_child_nodes,node1_parent_nodes,node1_bidirection_nodes,node2_child_nodes,node2_parent_nodes,node2_bidirection_nodes......为节点列表,例如,node1_c是由node1的编号(cmdb_id)和字符串”c”拼接得到的字符串,node1_child_nodes为包含node1节点的子节点的列表。
编码比较与分析模块与编码读写模块、编码模块相连,当进行未知故障分析时,编码比较与分析模块从编码模块获得潜在故障根节点编码,从编码读写模块获得标准编码,然后将潜在故障根节点编码与标准编码进行比较分析,得到分析结果。
第二步,数据读取模块从微服务系统监控数据文件获取已知故障数据,数据读取模块使用pythonpandas库的read_csv()方法从微服务系统监控数据文件包含的六个文件中读取数据,并用pythonpandas库的dataframe()方法将读取的数据转化为dataframe数据类型,然后使用pythonpandas库的concat()方法将来自六个文件的dataframe类型的数据合为一个dataframe数据data,将data传送给服务依赖图构建模块。
第三步,服务依赖图构建模块从数据读取模块获得data,根据data构建服务依赖图。服务依赖图构建模块只构建故障分析目标时间窗口(该时间窗口由start_time和end_time来确定)内的服务依赖图,得到服务依赖图列表,将服务依赖图列表service_map发送给异常判断模块,start_time为时间窗口开始时间,end_time为时间窗口结束时间。具体步骤如下:
3.1服务依赖图构建模块从键盘接收用户设定的start_time和end_time;
3.2服务依赖图构建模块采用服务依赖图构建方法使用data中start_time和end_time之间的数据构建服务依赖图,得到服务依赖图列表service_map。
方法为:
3.2.1使用n表示data中的一条监控数据,将data中满足公式一的数据n筛选出来储存在use_data中:
start_time<n.starttime<end_time公式一
n.starttime表示数据n的starttime字段,将use_data中的所有数据n的traceid字段的值存储在服务依赖图标识列表trace_id中。
3.2.2去除服务依赖图标识列表trace_id中的重复数据,得到去除了重复数据的服务依赖图标识列表trace_id_no_repeat,令trace_id_no_repeat的总元素个数为n_trace_id_no_repeat。
3.2.3根据trace_id_no_repeat构建服务依赖图列表service_map,方法是:
3.2.3.1初始化服务依赖图列表service_map为空,初始化遍历服务依赖图标识列表循环变量i_trace_id_no_repeat=0。
3.2.3.2令trace_id=trace_id_no_repeat[i_trace_id_no_repeat](表示trace_id_no_repeat的第i_trace_id_no_repeat+1个元素)。
3.2.3.3在service_map添加一个空列表元素new_element,new_element用以存储一个服务依赖图,new_element存在两个域,第一个域存储该服务依赖图的traceid字段的值,第二个域用于存储该服务依赖图的服务调用。令new_element第一个域等于trace_id。
3.2.3.4将use_data中满足公式二的数据n筛选出来,储存在dataframe类型数据call_all中。
n.traceid=trace_id公式二
n.traceid表示数据n的traceid字段。
3.2.3.5令new_element的第二个域等于call_all。
3.2.3.6若i_trace_id_no_repeat<n_trace_id_no_repeat,令i_trace_id_no_repeat=i_trace_id_no_repeat+1,转步骤3.2.3.2,否则说明service_map构造完毕,转步骤3.3。
3.3服务依赖图构建模块将服务依赖图列表service_map发送给异常判断模块,令service_map中的元素总数为n_service_map。
第四步,异常判断模块从服务依赖图构建模块获得service_map,根据service_map,采用异常服务依赖图列表构建方法构建异常服务依赖图列表service_map_anomal,将异常服务依赖图列表service_map_anomal发送给显式、隐式节点确认模块,方法是:
4.1初始化异常服务依赖图列表service_map_anomal为空,service_map_anomal用于储存异常服务依赖图。
4.2异常判断模块从service_map筛选出start_time,end_time之间头服务调用存在异常的服务依赖图,构造异常服务依赖图列表service_map_anomal,方法为:
4.2.1初始化服务依赖图头服务调用异常阈值字典ave_var为空,ave_var用于储存相应种类服务调用的延迟时间的阈值,其形式为{type_cmdb_id1:t1,type_cmdb_id2:t2,type_cmdb_id3:t3......},type_cmdb_id1,type_cmdb_id2,type_cmdb_id3......为字符串类型数据,由服务调用的种类和节点编号拼接得到,t1,t2,t3......是列表类型数据,内容为:[use_data_mean,use_data_std,sigma],其中use_data_mean表示相应种类服务调用的延迟时间的平均值,use_data_std表示相应种类服务调用的延迟时间的标准差,sigma表示相应种类服务调用的延迟时间的异常阈值,其中sigma=use_data_mean+use_data_std×3。
4.2.2根据service_map构造异常服务依赖图列表service_map_anomal,方法是:
4.2.2.1初始化服务依赖图遍历循环变量i_service_map=0。
4.2.2.2令服务依赖图map=service_map[i_service_map],map存在四个域,第一个域存储该服务依赖图的traceid字段的值,第二个域用于存储该服务依赖图的服务调用,后两个域先空着作为备用。trace_id=map[0],令服务调用集合calls=map[1],表示令trace_id等于map第一个域的值,令calls等于map第二个域的值。
4.2.2.3将calls中满足公式三的唯一一条(由于每个服务依赖图中的头调用只有一条,所以满足公式三的服务调用数据只有一条)服务调用数据(令为n_calls),筛选出来储存在服务依赖图头服务调用列表headcall中。
n_calls.pid=="none"公式三
4.2.2.4令type等于headcall中"calltype"字段的值,cmdb_id等于headcall中"cmdb_id"字段的值,elapsedtime等于headcall中"elapsedtime"字段的值,将type和cmdb_id拼接得到type_cmdb_id。
4.2.2.5如果ave_var中包含了键type_cmdb_id,跳转到4.2.2.8,否则跳转到4.2.2.6。
4.2.2.6将data中满足以下条件的服务调用数据(令为n):
n.calltype=type且n.cmdb_id=cmdb_id且n.pid="none"且n.starttime>start_time-25×60×1000且n.starttime<start_time.
筛选出来储存在相应时间段内n_calls同类型调用use_data_2中,计算use_data_2中数据的elapsedtime字段数据的平均值和标准差,分别存储在平均值use_data_mean和标准差use_data_std,计算异常阈值sigma=use_data_mean+use_data_std×3,将键值对type_cmdb_id:[use_data_mean,use_data_std,sigma](type_cmdb_id是键,[use_data_mean,use_data_std,sigma]是值)存储在ave_var中。
4.2.2.7如果elapsedtime>ave_var[type_cmdb_id][2](即该服务依赖图的头服务调用的响应时间大于ave_var中存储的异常阈值),将map添加到service_map_anomal中,转4.2.2.8;如果elapsedtime≤ave_var[type_cmdb_id][2],直接转4.2.2.8。
4.2.2.8令i_service_map=i_service_map+1,如果i_service_map<n_service_map,跳转到4.2.2.2,否则说明异常服务依赖图列表service_map_anomal构造完毕,跳转到4.4。
4.3令service_map_anomal的总元素个数为n_service_map_anomal。
4.4异常判断模块筛选出service_map_anomal内部的异常服务调用,将筛选后的service_map_anomal发送给显式、隐式节点确认模块,方法为:
4.4.1初始化服务依赖图服务调用异常阈值字典ave_var_2为空,ave_var_2的结构同4.2中的ave_var。
4.4.2异常判断模块筛选出service_map_anomal内部的异常服务调用,方法是:
4.4.2.1初始化异常服务依赖图列表遍历循环变量i_service_map_anomal=0。
4.4.2.2令map=service_map_anomal[i_service_map_anomal]。
4.4.2.3初始化map的第三个域为一个空元素列表,即令map[2]为一个列表,该列表用于存储map包含的异常服务调用的抽象表示,列表中的元素的形式为[cmdb_id,cmdb_next],其中cmdb_id为发起异常服务调用的节点的编号,cmdb_next为响应该异常服务调用的节点的编号;初始化map的第四个域为一个空dataframe类型元素,即map[3]为一个dataframe类型数据,该dataframe类型元素用于存储map的异常服务调用,列为‘calltype',‘starttime',‘elapsedtime',‘success',‘traceid',‘id',‘pid',‘cmdb_id',‘servicename'。
4.4.2.4筛选出map中的异常调用,方法是:
4.4.2.4.1初始化服务调用遍历循环变量n_map=0,令服务调用遍历循环上界m_map等于map[1]的长度。
4.4.2.4.2令服务调用call等于map[1]第n_map+1行的数据。
4.4.2.4.3取call中的‘calltype’字段数据储存在type中,提取call中的‘cmdb_id’字段数据储存在cmdb_id中,提取call中的‘elapsedtime’字段数据储存在elapsedtime中,将type和cmdb_id组合为type_cmdb_id。
4.4.2.4.4如果ave_var_2已包含键type_cmdb_id,跳转到4.4.2.4.6,否则跳转到4.4.2.4.5。
4.4.2.4.5将data中满足以下条件的服务调用数据(令为n_use_data):
n_use_data.calltype=type且n_use_data.cmdb_id=cmdb_id且n_use_data.pid="none"且n_use_data.starttime>start_time-25×60×1000且n_use_data.starttime<start_time.
提取出来储存在use_data_2中,计算use_data_2中数据的elapsedtime字段数据的平均值和标准差,分别存储在平均值use_data_mean_2和标准差use_data_std_2,计算异常阈值sigma_2=use_data_mean_2+use_data_std_2×3,将键值对type_cmdb_id_2:[use_data_mean_2,use_data_std_2,sigma_2]存储在ave_var_2中。
4.4.2.4.6如果elapsedtime>ave_var_2[type_cmdb_id][2],跳转到4.4.2.4.7,否则跳转到4.4.2.4.8。
4.4.2.4.7将相应时间大于阈值的调用call添加到map的第四个域map[3]中,即令map[3]的内容为call,同时判断call及其子调用call_next组成的调用的抽象表示是否存在于map的第三个域map[2]中,如果不存在则将该调用的抽象表示添加到map[2]中,方法是:
4.4.2.4.7.1将call添加到map[3]中,令id等于call中"id"字段的值,令calls_next为map[1]中满足公式四的服务调用数据(令为data_map)的集合:
data_map.pid=id公式四
data_map.pid表示data_map的pid域。令calls_next中包含的元素的数量为t_calls_next。
4.4.2.4.7.2判断call及call_next组成的调用的抽象表示是否存在于map的第三个域map[2]中,如果不存在则将该调用的抽象表示添加到map[2]中,方法是:
4.4.2.4.7.2.1令子服务调用集合遍历循环变量k_calls_next=0。
4.4.2.4.7.2.2令call_next等于calls_next中的第_calls_next+1条数据,cmdb_next等于call_next中"cmdb_id"字段的值,令already_in_map2=0。
4.4.2.4.7.2.3将cmdb_id和cmdb_next组合为[cmdb_id,cmdb_next],如果map[2]中不包含[cmdb_id,cmdb_next],将[cmdb_id,cmdb_next]添加到map[2]中,转4.4.2.4.7.2.4;如果map[2]中包含[cmdb_id,cmdb_next],直接转4.4.2.4.7.2.4。
4.4.2.4.7.2.4令k_calls_next=k_calls_next+1,如果k_calls_next<t_calls_next,跳转到4.4.2.4.7.2.2,否则跳转到4.4.2.4.8。
4.4.2.4.8令n_map=n_map+1,如果n_map<m_map,跳转到4.4.2.4.2,否则跳转到4.4.2.4.9。
4.4.2.4.9令i_service_map_anomal=i_service_map_anomal+1,如果i_service_map_anomal<n_service_map_anomal,跳转到4.4.2.2,否则,说明service_map_anomal内部的异常服务调用筛选完毕,跳转到4.4.3。
4.4.3将内部的异常服务调用筛选完毕后的service_map_anomal发送给显式、隐式节点确认模块,令此时的service_map_anomal的总元素个数为n_service_map_anomal_2。
第五步,显式、隐式节点确认模块从异常判断模块获得内部的异常服务调用筛选完毕后的service_map_anomal,根据service_map_anomal确定显式、隐式节点,得到显式节点列表a_node和隐式节点列表b_node,将a_node,b_node发送给编码模块,具体过程为:
5.1初始化显式节点列表a_node和隐式节点列表b_node为空,a_node和b_node中的元素结构相同,有两个域,第一个域储存该显式/隐式节点的编号(即发起该次服务调用的微服务节点的编号cmdb_id),第二个域储存该显式/隐式节点的可信度,设置可信度是为了避免误差,当构造完显式、隐式节点列表之后,可信度低于某个阈值的节点将被从显式、隐式节点列表中删除。
5.2构造a_node和b_node的内容,具体步骤如下:
5.2.1初始化异常服务依赖图列表遍历循环变量i_service_map_anomal_2=0。
5.2.2令map=service_map_anomal[i_service_map_anomal_2];
5.2.3遍历map的第3个域map[2]中存储的异常调用的抽象表示,确定a_node,方法是:
5.2.3.1初始化异常服务调用抽象列表遍历循环变量n_map2=0,令异常服务调用抽象列表遍历循环上界m_map2等于map[2]的元素个数。
5.2.3.2令第一异常服务调用节点对first_next=map[2][n_map2],(first_next由一对节点组成,分别是异常调用的发起节点和响应节点,map[2]是一个列表,map[2]中的每个元素是一个包含两个元素的列表,first_next=map[2][n_map2]表示取map[2]第n_map2+1个元素,所以first_next是一个包含两个元素的列表。first_next用来表示一对节点,其中first_next[0]是异常调用的发起节点,first_next[1]是异常调用的响应节点。)first_in_a=0,first_in_a变量用于记录first_next中的异常服务调用发起节点是否在显式节点列表中。
5.2.3.3遍历a_node以进行判断first_next[0]是否已经存在于a_node中,方法是:
5.2.3.3.1初始化显式节点列表遍历循环变量k_a_node=0,令显式节点列表遍历循环上界t_a_node等于a_node的长度(在5.2.3.3.1至5.2.3.3.3表示的循环中,a_node的长度是不变的,只有在循环结束后,在5.2.3.4步骤中,向a_node中添加元素之后,a_node的长度才会改变)。
5.2.3.3.2令w=a_node[k_a_node](表示显式节点列表中的第k_a_node+1项),如果w[0]=first_next[0],说明异常服务调用节点对中的异常服务调用发起节点存在于显示节点列表中,令first_in_a=1,令w[1]=w[1]+1。
5.2.3.3.3令k_a_node=k_a_node+1,如果k_a_node<t_a_node,跳转到5.2.3.3.2;否则转5.2.3.4。
5.2.3.4如果first_in_a=0,说明异常服务调用节点对中的异常服务调用发起节点不存在于显示节点列表中,需要将该节点添加到显式节点列表中,将[first,1]添加到a_node中。
5.2.3.5令n_map2=n_map2+1,如果n_map2<m_map2,跳转到5.2.3.2,否则跳转到5.2.4。
5.2.4构造隐式节点列表b_node,方法是:
5.2.4.1初始化隐式节点列表遍历循环变量n_map2_2=0,令隐式节点列表遍历循环上界m_map2_2等于map[2]的长度。
5.2.4.2令第一异常服务调用节点对first_next=map[2][n_map2_2](first_next=map[2][n_map2_2]表示取map[2]第n_map2_2+1个元素,所以first_next是一个包含两个元素的列表),初始化异常服务调用发起节点变量a_exist=0,a_exist用于记录first_next中的异常服务调用响应节点是否作为异常服务调用发起节点出现过,如果某节点作为异常服务调用发起节点出现过则该节点不能成为隐式节点。a_exist=0表示first_next中的异常服务调用响应节点未作为异常服务调用发起节点出现过。
5.2.4.3遍历异常服务调用列表,判断first_next中的异常服务调用响应节点是否作为异常服务调用发起节点出现过,方法是:
5.2.4.3.1初始化异常服务调用列表遍历循环变量k_map2_2=0,令异常服务调用列表遍历循环上界t_map2_2等于map[2]的长度。
5.2.4.3.2令第二异常服务调用节点对first_next2=map[2][k_map2_2](first_next2由一对节点组成,分别是异常调用的发起节点和响应节点,first_next2=map[2][k_map2_2]表示取map[2]第k_map2_2+1个元素,所以first_next2也是一个包含两个元素的列表),如果first_next2[0]==first_next[1],说明first_next[1]这个节点发起过异常调用,不能成为隐式节点,令a_exist=1,跳转到5.2.4.4,否则跳转到5.2.4.3.3。
5.2.4.3.3令k_map2_2=k_map2_2+1,如果k_map2_2<t_map2_2,跳转到5.2.4.3.2,否则跳转到5.2.4.4。
5.2.4.4如果a_exist=1,跳转到5.2.5,否则跳转到5.2.4.5。
5.2.4.5令异常服务调用响应节点next=first_next[1],令判断变量next_in_b=0,next_in_b用于判断next是否存在于b_node中。
5.2.4.6将next添加到b_node中,方法是:
5.2.4.6.1初始化隐式节点列表遍历循环变量k_b_node=0,令隐式节点列表遍历循环上界t_b_node等于b_node的长度(在5.2.4.6.1至5.2.4.6.3表示的循环中,b_node的长度是不变的,只有在循环结束后,在5.2.4.6.4步骤中,向b_node中添加元素之后,b_node的长度才会改变)。
5.2.4.6.2令w=b_node[k_b_node],如果w[0]=next,令next_in_b=1,w[1]=w[1]+1。
5.2.4.6.3令k_b_node=k_b_node+1,如果k_b_node<t_b_node,跳转到5.2.4.6.2,否则跳转到5.2.4.6.4。
5.2.4.6.4如果next_in_b=0,说明next不在b_node中,需要将其添加到b_node中,即将二元组[next,1]添加到b_node中。
5.2.5令i_service_map_anomal=i_service_map_anomal+1,如果i_service_map_anomal<n_service_map_anomal,跳转到5.2.2,否则跳转到5.3。
5.3采用节点相抵消方法将a_node和b_node中重复的节点相抵消,方法是:
5.3.1初始化显式节点列表循环变量i_a_node_2=0,令显式节点列表循环上界n_a_node_2等于a_node的长度。
5.3.2令显示节点a=a_node[i_a_node_2]。
5.3.3遍历隐式节点列表,将与a节点相同的隐式节点从隐式节点列表中删除,方法是:
5.3.3.1初始化隐式节点列表循环变量n_b_node_2=0,隐式节点列表循环上界m_b_node_2等于b_node的长度。
5.3.3.2令隐式节点b=b_node[n_b_node_2]。
5.3.3.3如果a[0]=b[0],跳转到5.3.3.4,否则跳转到5.3.3.5。
5.3.3.4如果a[1]>b[1],令a[1]=a[1]-b[1],从b_node中移除隐式节点b,跳转到5.3.3.5。如果a[1]=b[1],从a_node中移除显式节点a,从b_node中移除隐式节点b,跳转到5.3.3.5。如果a[1]<b[1],令b[1]=b[1]-a[1],从a_node中移除显式节点a,跳转到5.3.3.5。
5.3.3.5令n_b_node_2=n_b_node_2+1,如果n_b_node_2<m_b_node_2,跳转到5.3.3.2,否则跳转到5.3.4。
5.3.4令i_a_node_2=i_a_node_2+1,如果i_a_node_2<n_a_node_2,跳转到5.3.2,否则跳转到5.4。
5.4使用低可信度节点消除方法消除显式节点列表a_node中的低可信度节点,方法为:
5.4.1初始化显式节点列表循环变量i_a_node_3=0,令显式节点列表循环上界n_a_node_3等于a_node的长度。
5.4.2令显示节点a=a_node[i_a_node_3]。
5.4.3如果a[1]<30,将a从a_node中移除。
5.4.4令i_a_node_3=i_a_node_3+1,如果i_a_node_3<n_a_node_3,跳转到5.4.2,否则说明显式节点列表a_node中的低可信度节点消除完毕,跳转到5.5。
5.5使用5.4中所述的低可信度节点消除方法消除隐式节点列表b_node中的低可信度节点。
5.6将a_node,b_node发送给编码模块。
第六步,编码模块从显式、隐式节点确认模块获得列表a_node,b_node,读取拓扑模块中的字典topology获取节点间拓扑信息,采用编码方法对用户指定的故障根节点进行编码,得到编码code,将code发送给编码读写模块,方法为:
6.1编码模块从键盘接收用户给定的故障根节点的编号cmdb_id;
6.2编码模块根据a_node,b_node、故障根节点的编号cmdb_id和字典topology进行编码,得到编码code,code用以记录故障的特征,前7位有意义第一位用于记录故障根节点本身的类型,如果故障根节点是显式节点,第一位为1,如果故障根节点是隐式节点,第一位为0。第二位用于记录故障根节点的子结点中的显式节点分布情况,如果子节点全部是显式节点,第二位为1,如果子节点部分为显式节点,第二位为0,如果子节点都不是显式节点,第二位为-1。第三位用于记录故障根节点的子结点中的隐式节点分布情况,如果子节点全部是隐式节点,第三位为1,如果子节点部分为隐式节点,第三位为0,如果子节点都不是隐式节点,第三位为-1;第四位用于记录故障根节点的双向节点中的显式节点分布情况,如果父节点全部是显式节点,第四位为1,如果父节点部分为显式节点,第四位为0,如果父节点都不是显式节点,第四位为-1;第五位用于记录故障根节点的双向节点中的隐式节点分布情况,如果父节点全部是隐式节点,第五位为1,如果父节点部分为隐式节点,第五位为0,如果父节点都不是隐式节点,第五位为-1;第六位用于记录故障根节点的双向节点中的显式节点分布情况,如果双向节点全部是显式节点,第六位为1,如果双向节点部分为显式节点,第六位为0,如果双向节点都不是显式节点,第六位为-1;第七位用于记录故障根节点的双向节点中的隐式节点分布情况,如果双向节点全部是隐式节点,第七位为1,如果双向节点部分为隐式节点,第七位为0,如果双向节点都不是隐式节点,第七位为-1;第8位暂时保留不做分配,具体过程为:
6.2.1初始化编码code=[none,none,none,none,none,none,none,none]。
6.2.2设置编码第一位,方法是:
6.2.2.1如果a_node中包含cmdb_id,令code[0]=1;
6.2.2.2如果b_node中包含cmdb_id,令code[0]=0;
6.2.3设置编码第二位,方法是:
6.2.3.1读取topology中键[cmdb_id+"_c"]对应的值,储存在列表cmdb_id_c中,初始化a_number=0,a_number记录a_node中包含的子节点的数量,令cmdb_id_c的长度为n_cmdb_id_c_2。
6.2.3.2计算a_number的值,方法是:
6.2.3.2.1令子节点列表遍历循环变量i_cmdb_id_c_2=0。
6.2.3.2.2令子节点c=cmdb_id_c[i_cmdb_id_c_2]。
6.2.3.2.3如果a_node中包含c,令a_number=a_number+1,转6.2.3.2.4;如果a_node中不包含c,直接转6.2.3.2.4。
6.2.3.2.4令i_cmdb_id_c_2=i_cmdb_id_c_2+1,如果i_cmdb_id_c_2<n_cmdb_id_c_2,跳转到6.2.3.2.2,否则跳转到6.2.3.3。
6.2.3.3如果a_number=n_cmdb_id_c_2,令code[1]=1,如果a_number>0且a_number<n_cmdb_id_c_2,令code[1]=0;如果a_number=0,令code[1]=-1。
6.2.4设置编码第三位,执行如下操作:
6.2.4.1初始化b_number=0,b_number记录b_node中包含的子节点的数量,令cmdb_id_c的长度为n_cmdb_id_c_3。
6.2.4.2计算b_number的值,方法是:
6.2.4.2.1令子节点列表遍历循环变量i_cmdb_id_c_3=0。
6.2.4.2.2令c=cmdb_id_c[i_cmdb_id_c_3]。
6.2.4.2.3如果b_node中包含c,令b_number=b_number+1,转6.2.4.2.4;如果b_node中不包含c,直接转6.2.4.2.4。
6.2.4.2.4令i_cmdb_id_c_3=i_cmdb_id_c_3+1,如果i_cmdb_id_c_3<n_cmdb_id_c_3,跳转到6.2.4.2.2,否则跳转到6.2.4.3。
6.2.4.3如果b_number=n_cmdb_id_c_3,令code[2]=1,如果b_number>0且b_number<n_cmdb_id_c_3,令code[2]=0,如果b_number=0,令code[2]=-1。
6.2.5设置编码第四位,方法是:
6.2.5.1读取topology中键[cmdb_id+"_p"]对应的值,储存在列表cmdb_id_p中,初始化a_number=0,a_number记录a_node中包含的父节点的数量,令cmdb_id_p的长度为n_cmdb_id_p_4。
6.2.5.2计算a_number的值,方法是:
6.2.5.2.1令父节点列表遍历循环变量i_cmdb_id_p_4=0。
6.2.5.2.2令c=cmdb_id_p[i_cmdb_id_p_4]。
6.2.5.2.3如果a_node中包含c,令a_number=a_number+1,转6.2.5.2.4;如果a_node中不包含c,直接转6.2.5.2.4。
6.2.5.2.4令i_cmdb_id_p_4=i_cmdb_id_p_4+1,如果i_cmdb_id_p_4<n_cmdb_id_p_4,跳转到6.2.5.2.2,否则跳转到6.2.5.3。
6.2.5.3如果a_number=n_cmdb_id_p_4,令code[3]=1,如果a_number>0且a_number<n_cmdb_id_p_4,令code[3]=0,如果a_number=0,令code[3]=-1。
6.2.6设置编码第五位,执行如下操作:
6.2.6.1初始化b_number=0,b_number记录b_node中包含的父节点的数量,令cmdb_id_p的长度为n_cmdb_id_p_5。
6.2.6.2计算b_number的值,方法是:
6.2.6.2.1令父节点列表遍历循环变量i_cmdb_id_p_5=0。
6.2.6.2.2令c=cmdb_id_p[i_cmdb_id_p_5]。
6.2.6.2.3如果b_node中包含c,令b_number=b_number+1,转6.2.6.2.4;如果b_node中不包含c,直接转6.2.6.2.4。
6.2.6.2.4令i_cmdb_id_p_5=i_cmdb_id_p_5+1,如果i_cmdb_id_p_5<n_cmdb_id_p_5,跳转到6.2.6.2.2,否则跳转到6.2.6.3。
6.2.6.3如果b_number=n_cmdb_id_p_5,令code[4]=1,如果b_number>0且b_number<n_cmdb_id_p_5,令code[4]=0,如果b_number=0,令code[4]=-1。
6.2.7设置编码第六位,执行如下操作:
6.2.7.1读取topology中键[cmdb_id+"_two"]对应的值,储存在列表cmdb_id_two中,初始化a_number=0,a_number记录a_node中包含的双向节点的数量,令于cmdb_id_two的长度为n_cmdb_id_two_6。
6.2.7.2计算a_number的值,方法是:
6.2.7.2.1令双向节点列表遍历循环变量i_cmdb_id_two_6=0。
6.2.7.2.2令c=cmdb_id_two[i_cmdb_id_two_6]。
6.2.7.2.3如果a_node中包含c,令a_number=a_number+1,转6.2.7.2.4,如果a_node中不包含c,直接转6.2.7.2.4。
6.2.7.2.4令i_cmdb_id_two_6=i_cmdb_id_two_6+1,如果i_cmdb_id_two_6<n_cmdb_id_two_6,跳转到6.2.7.2.2,否则跳转到6.2.7.3。
6.2.7.3如果a_number=n_cmdb_id_two_6,令code[5]=1,如果a_number>0且a_number<n_cmdb_id_two_6,令code[5]=0,如果a_number=0,令code[5]=-1。
6.2.8设置编码第七位,执行如下操作:
6.2.8.1初始化b_number=0,b_number记录b_node中包含的双向节点的数量,令cmdb_id_two的长度为n_cmdb_id_two_7。
6.2.8.2计算a_number的值,方法是:
6.2.8.2.1令双向节点列表遍历循环变量i_cmdb_id_two_7=0。
6.2.8.2.2令c=cmdb_id_two[i_cmdb_id_two_7]。
6.2.8.2.3如果b_node中包含c,令b_number=b_number+1,转6.2.8.2.4;如果b_node中不包含c,直接转6.2.8.2.4。
6.2.8.2.4令i_cmdb_id_two_7=i_cmdb_id_two_7+1,如果i_cmdb_id_two_7<n_cmdb_id_two_7,跳转到6.2.8.2.2,否则跳转到6.2.8.3。
6.2.8.3如果b_number=n_cmdb_id_two_7,令code[6]=1,如果b_number>0且b_number<n_cmdb_id_two_7,令code[6]=0,如果b_number=0,令code[6]=-1。
6.3将code发送给编码读写模块。
第七步,编码读写模块从编码模块获取code,从已知故障文件中获取已知故障信息,将这些信息储存在标准编码文件code.npy。具体步骤如下:
7.1编码读写模块将已知故障信息中的cmdb_id,content,code以字典形式储存在本地文件code.npy,其中cmdb_id为已知故障信息中的故障根节点,content为已知故障信息中的故障种类。
7.2构造内容为[cmdb_id,content]的已知故障信息列表。初始化字符串变量code_string=‘',‘'表示为空字符串,code_string用于存储转化为字符串的code。
7.3将code中的int类型的元素转化为string类型,并将转换后的string类型的元素拼接在一起,中间以“,”相隔,储存在字符串变量code_string中。
7.4如果code.npy文件不存在,跳转到7.5,否则跳转到7.6
7.5构造第二字典write_dictionary,令write_dictionary的code_string键对应的值为cmdbid_content,创建文件code.npy并将write_dictionary储存在文件code.npy,跳转到第八步。
7.6读取code.npy文件的内容,储存在write_dictionary中,更新write_dictionary,将write_dictionary中code_string键对应的值设为cmdbid_content。将更新后的write_dictionary再次写入到文件code.npy中。跳转到第八步。
第八步,数据读取模块,服务依赖图构建模块,异常判断模块,显式、隐式节点确认模块对未知故障数据进行预处理,方法与对已知故障数据的处理类似,具体步骤如下:
8.1数据读取模块从微服务系统监控数据文件中获取未知故障数据data_unknown,数据读取模块使用pythonpandas库的read_csv()方法从微服务系统监控数据存储文件包含的六个文件中读取数据,并用pythonpandas库的dataframe()方法将读取的数据转化为dataframe数据类型,然后使用pythonpandas库的concat()方法将来自六个文件的dataframe类型的数据合为一个dataframe数据data_unknown。数据读取模块将data_unknown传送给服务依赖图构建模块。
8.2服务依赖图构建模块从数据读取模块获得data_unknown,根据data_unknown构建故障分析目标时间窗口(该时间窗口由用户通过初始化参数start_time_unknown和end_time_unknown来确定)内的服务依赖图,得到第二服务依赖图列表service_map_unknown,start_time_unknown为时间窗口开始时间,end_time_unknown为时间窗口结束时间,start_time_unknown和end_time_unknown均由用户设定。
具体步骤如下:
8.2.1服务依赖图构建模块从键盘接收用户设定的start_time_unknown和end_time_unknown;
8.2.2服务依赖图构建模块采用3.2步所述服务依赖图构建方法,使用data_unknown中start_time_unknown和end_time_unknown之间的数据构建服务依赖图,得到第二服务依赖图列表service_map_unknown。
8.2.3服务依赖图构建模块将第二服务依赖图列表service_map_unknown传送给异常判断模块,令service_map_unknown的总元素个数为n_service_map_unknown。
8.3异常判断模块从服务依赖图构建模块获得service_map_unknown,根据service_map_unknown,采用第四步所述异常服务依赖图列表构建方法构建异常服务依赖图列表service_map_anomal_unknown。将service_map_anomal_unknown发送给显式、隐式节点确认模块,令service_map_anomal_unknown的总元素个数为n_service_map_anomal_unknown_2。
8.4显式、隐式节点确认模块从异常判断模块获得异常服务依赖图列表service_map_anomal_unknown,根据service_map_anomal_unknown采用第五步所述显式、隐式节点确定方法确定显式、隐式节点,得到第二显式节点列表a_node_unknown和第二隐式节点列表b_node_unknown。
8.5显式、隐式节点确认模块将a_node_unknown和b_node_unknown传送给深度搜索模块和编码模块。
第九步,深度搜索模块从显式隐式节点确认模块获取a_node_unknown和b_node_unknown,搜索底层显式、隐式节点(在一个服务依赖图中,节点之间存在服务调用关系,按照节点间的服务调用关系可以将它们组织为一个类似于多叉树的结构,服务调用发起节点在上层,服务调用响应节点在下层,底层显式、隐式节点分别表示在多叉树中位于最底层的显式、隐式节点),构成潜在故障根节点列表candidate_root_nodes,发送给编码模块。具体步骤如下:
9.1找出a_node_unknown中深度最大的节点,储存在列表bottom_a中,找出b_node_unknown中深度最大的节点,储存在列表bottom_b中。
9.2如果bottom_b的长度大于1,将bottom_b置为空。
9.3将bottom_a和bottom_b合并,得到潜在故障根节点列表candidate_root_nodes,将candidate_root_nodes发送给编码模块,令candidate_root_nodes包含的元素个数为n_candidate_root_nodes。
第十步,编码模块从显式、隐式节点确认模块获得a_node_unknown和b_node_unknown,从深度搜索模块获得潜在故障根节点列表candidate_root_nodes,对candidate_root_nodes中的每一个潜在根节点进行编码,得到潜在根节点编码列表candidate_nodes_codes,将candidate_nodes_codes发送给编码比较和分析模块,具体方法如下:
10.1初始化潜在根节点编码列表candidate_nodes_codes为空。
10.2对candidate_root_nodes中的每一个潜在根节点进行编码,并将得到的编码存入candidate_nodes_codes中,方法是:
10.2.1令潜在根节点遍历循环变量i_candidate_root_nodes=0,。
10.2.2令潜在根节点candidate_node=candidate_root_nodes[i_candidate_root_nodes]。
10.2.3编码模块采用第六步所述编码方法根据a_node_unknown和b_node_unknown、字典topology中的节点间拓扑信息,对潜在根节点candidate_node进行编码,得到潜在根节点编码candidate_node_code。
10.2.4将candidate_node_code存入到潜在根节点编码列表candidate_nodes_codes中。
10.2.5令i_candidate_root_nodes=i_candidate_root_nodes+1,如果i_candidate_root_nodes<n_candidate_root_nodes,跳转到10.2.2,否则得到candidate_nodes_codes,令candidate_nodes_codes的包含的元素的个数为n_candidate_nodes_codes,跳转到10.3。
10.3将candidate_nodes_codes发送给编码比较和分析模块。
第十一步,编码读写模块读取标准编码信息,生成已知故障字典,编码比较和分析模块对已知和未知故障的编码进行比较分析,得到故障根节点,具体步骤如下:
11.1编码读写模块读取标准编码文件code.npy(在第七步产生)中存储的标准编码信息,储存在已知故障字典exit_codes中,exit_codes的每一项为二元组(key,value),其中key为键,是列表形式存储的八位编码,value为对应的值,形式为列表[cmdbid,content],cmdbid,content分别表示故障根节点和故障类型,令exit_codes的包含的元素的个数为m_exit_codes。编码读写模块将exit_codes传送给编码比较和分析模块。
11.2编码比较和分析模块从编码读写模块获得exit_codes,从编码模块获得潜在根节点编码列表candidate_nodes_codes,将candidate_nodes_codes中的编码与exit_codes中的编码进行比较和分析,得出全局最优分数grade_most。具体步骤如下:
11.2.1初始化全局最优分数grade_most=0。
11.2.2计算全局最优分数grade_most,具体步骤如下:
11.2.2.1初始化潜在根节点编码列表遍历循环变量i_candidate_nodes_codes=0。
11.2.2.2令潜在根节点candidate_node_code=candidate_nodes_codes[i_candidate_nodes_codes]。
11.2.2.3编码读写和比较模块对candidate_node_code进行分析,得到candidate_node_code的最佳分数grade_best,分析方法如下:
11.2.2.3.1初始化最佳分数grade_best=0,最佳键key_best=none,最佳节点cmdb_id_best=none,最佳故障类型content_best=none。
11.2.2.3.2初始化标准编码遍历循环变量n_exit_codes=0。
11.2.2.3.3令exit_code等于exit_codes的第n_exit_codes+1项
11.2.2.3.4candidate_node和exit_code.key均为八位编码,每一位的取值为{-1,0,1}中的一个,编码读写和比较模块对candidate_node和exit_code.key对应位数的取值进行比较,计算candidate_node和exit_code.key相同的位数,相同的位数储存在当前分数grade中。
11.2.2.3.5如果grade>grade_best,令grade_best=grade,key_best=exit_code.key,cmdb_id_best=exit_code.value[0],content_best=exit_code.value[1]。
11.2.2.3.6令n_exit_codes=n_exit_codes+1,如果n_exit_codes<m_exit_codes,跳转到11.2.2.3.3,否则说明exit_codes遍历完毕,跳转到11.2.2.4。
11.2.2.4如果grade_best≥grade_most,令grade_most=grade_best。
11.2.2.5令i_candidate_nodes_codes=i_candidate_nodes_codes+1,如果i_candidate_nodes_codes<n_candidate_nodes_codes,跳转到11.2.2.2,否则说明所有的潜在故障根节点都已经进行了编码,跳转到11.3。
11.3寻找最佳分数等于grade_most的潜在故障根节点编码,具体步骤如下:
11.3.1初始化潜在根节点编码列表循环变量i_candidate_nodes_codes_2=0,潜在根节点编码列表循环上界n_candidate_nodes_codes_2等于candidate_nodes_codes的包含的元素的个数。
11.3.2令candidate_node_code=candidate_nodes_codes[i_candidate_nodes_codes_2]。
11.3.4编码读写和比较模块对candidate_node_code进行分析得到其最佳分数grade_best,分析方法见11.2.2.3。
11.3.5如果grade_best=grade_most,说明找到了故障根节点,跳转到11.3.6,否则跳转到11.3.7。
11.3.6如果candidate_node为和用户交互的主机的cmdb_id编号,那么故障类型一定是"主机网络故障",分析结果为[candidate_node,"主机网络故障"],跳转到11.3.8,否则分析结果为[candidate_node,content_best],跳转到11.3.8。(content_best是一个字符串变量,它的值可以表示多种故障类型)
11.3.7令i_candidate_nodes_codes_2=i_candidate_nodes_codes_2+1,如果i_candidate_nodes_codes_2<n_candidate_nodes_codes_2,跳转到11.3.2,否则跳转到11.3.8。
11.3.8故障根因定位结束,将分析结果输出或显示。
采用本发明可以达到以下技术效果:
1.本发明提供了一种基于故障特征比较的微服务系统故障根因定位方法,基于已知故障对未知故障进行分析,通过对微服务系统故障时间段内的服务调用数据进行分析,实现了故障的根节点和故障类型的准确定位。
2.本发明第三步基于服务调用数据实时生成服务依赖图列表,能够应用于动态变化的微服务系统,同时第四步能够自动设定异常阈值,从而筛选异常服务依赖图和异常服务调用,提高了本发明的自适应性。
3.本发明第五步将异常节点划分为显式、隐式节点,提高了对多种故障特征的识别能力,同时第六步使用编码的方式记录多个节点的类型信息,将故障特征进行多节点层次分析,能够准确、全面的提取故障特征,提高了故障定位的准确性。
4.本发明第七步将已知故障的的编码及故障信息组织为标准编码的形式储存在文件中,从而实现对已知故障的利用。
5.本发明第八步实现对未知故障数据的处理,方法与处理已知故障的数据相同,从而为分析未知故障打下基础。第九步搜索潜在根节点,第十步对每一个潜在根节点进行编码,第十一步将潜在根节点的编码与标准编码进行比较分析,从而准确得出故障根节点及故障内容。
附图说明
图1是本发明总体流程图。
图2是本发明第一步构建的基于故障特征比较的微服务系统故障根因定位系统逻辑结构图。
具体实施方式
图1为本发明的总体流程图。如图1所示,本发明包括以下步骤:
第一步,构建故障根因定位系统,故障根因定位系统由微服务系统监控数据文件、数据读取模块、服务依赖图构建模块、异常判断模块、显式、隐式节点确认模块、编码模块、深度搜索模块、拓扑模块、编码比较与分析模块、编码读写模块、已知故障记录文件、标准编码文件组成。
微服务系统监控数据文件存储微服务系统监控数据,微服务系统监控数据文件为.csv文件,包括trace_csf.csv,trace_fly_remote.csv,trace_jdbc.csv,trace_local.csv,trace_osb.csv,trace_remote_process.csv六个文件,这六个文件分别包含了不同类型的服务调用(不同类型的服务调用的区别在于服务调用的服务节点不同),这六个文件中的每一条监控数据表示一次服务调用,每一条监控数据包含calltype,servicename,starttime,elapsedtime,success,id,traceid,pid,cmdb_id九个字段。微服务系统监控数据包括已知故障数据和未知故障数据,已知故障和未知故障发生在不同的时间段,已知故障为经过运维人员人工排查并处理的故障,其故障根节点和故障类型已知,未知故障是未经过运维人员人工排查及处理的故障,其故障根节点和故障类型未知,需要使用本发明进行故障根节点定位。
由于监控数据包含九个字段,使用数据特征九元组表示每条监控数据,数据特征九元组为(calltype,servicename,starttime,elapsedtime,success,id,traceid,pid,cmdb_id),其中calltype表示服务调用的类型,servicename表示服务调用的服务名称,starttime表示服务调用发起的时间,为时间戳格式,elapsedtime表示服务调用的延迟时间,即发起该服务调用到收到服务调用结果所花费的时间,success等于0或1,表示该次服务调用是否成功,id表示该次服务调用的编号,traceid表示该次服务调用所对应的用户请求,pid表示该次服务调用的父服务调用的编号,cmdb_id表示发起该次服务调用的微服务节点的编号。
数据读取模块与微服务系统监控数据文件、服务依赖图构建模块相连,数据读取模块从微服务系统监控数据文件中读取异常发生相应时间段(如15:00系统产生异常,无法正常响应用户请求,数据读取模块从微服务系统监控数据文件读取14:55-15:05时间段内的监控数据,以定位故障根节点,所述异常发生相应时间段指:t-▲t至t+▲t,t为异常发生时间,▲t一般为5-10分钟)的监控数据,将监控数据发送给服务依赖图构建模块。
服务依赖图构建模块与数据读取模块、异常判断模块相连,服务依赖图构建模块从数据读取模块获得异常发生相应时间段内的监控数据,将监控数据组织成服务依赖图列表。服务依赖图的定义为:当用户发起一次用户请求时,微服务系统内的节点间会发起多次服务调用,以提供完整的服务,这些服务调用的traceid字段的数值相同,将这些服务调用组织为一个服务依赖图,该服务依赖图由二元组(traceid,calls)表示,其中traceid与数据特征九元组中的traceid意义相同,表示该服务依赖图中的服务调用对应的用户请求,calls为该服务依赖图中包含的服务调用的集合。假设该时间段内发生了m次用户请求,则会产生m个服务依赖图,服务依赖图构建模块将这m个服务依赖图储存在服务依赖图列表中,并将服务依赖图列表发送给异常判断模块,m为正整数。
异常判断模块与服务依赖图构建模块、显式、隐式节点确认模块相连。异常判断模块从服务依赖图构建模块获取服务依赖图列表,筛选服务依赖图列表中存在异常的服务依赖图,并构造为异常服务依赖图四元组(traceid,calls,abnormal_calls_abstract,abnormal_calls),其中traceid为该存在异常的服务依赖图中的服务调用(异常服务依赖图既包含异常服务调用也包含正常服务调用,受到故障根节点影响的服务调用而导致响应时间大于正常值的服务调用为异常服务调用,未受到故障根节点影响的服务调用为正常服务调用)的traceid字段的值,calls为该存在异常的服务依赖图中包含的服务调用的集合,abnormal_calls_abstract为该存在异常的服务依赖图中异常服务调用的抽象表示,abnormal_calls为该存在异常的服务依赖图中异常服务调用的集合,最终获得异常服务依赖图列表,将异常服务依赖图列表传送给显式、隐式节点确认模块。
显式、隐式节点确认模块与异常判断模块、编码模块、深度搜索模块相连。显示节点指异常服务调用的发起节点,隐式节点指异常服务调用的被服务调用节点。显式、隐式节点确认模块从异常判断模块获得异常服务依赖图列表,根据异常服务依赖图列表判断显式节点和隐式节点,然后将显式节点列表和隐式节点列表发送给编码模块和深度搜索模块。
已知故障记录文件与编码读写模块相连,已知故障记录文件存储已知故障的信息,已知故障记录文件中记录了通过人工排查得到的已知故障信息,每一条信息对应一次已知故障,每一条信息可以表示为已知故障信息四元组(cmdb_id,content,fault_start_time,fault_end_time)其中cmdb_id与数据特征九元组中的cmdb_id字段含义相同,为服务节点的编号,用以记录该次已知故障的故障根节点,content为故障种类,fault_start_time为故障开始时间,fault_end_time为故障结束时间。
深度搜索模块与显式、隐式节点确认模块、编码模块相连,当进行未知故障分析时,深度搜索模块从显式、隐式节点确认模块出获得显式、隐式节点列表,找出其中处于底层的显式、隐式节点,形成潜在故障根节点列表,传送给编码模块。
编码模块与显式、隐式节点确认模块、深度搜索模块、拓扑模块、编码读写模块、编码比较与分析模块相连。当进行已知故障分析时,编码模块从显式、隐式节点确认模块获得显式节点列表和隐式节点列表,从拓扑模块获得系统拓扑信息,从已知故障记录文件获取已知故障信息。编码模块根据显式节点列表、隐式节点列表和已知故障信息对故障根节点进行编码得到标准编码,将标准编码发送给编码读写模块。当进行未知故障分析时,编码模块从深度搜索模块获取潜在故障根节点列表,从显式、隐式节点确认模块获得显式节点列表、隐式节点列表,从拓扑模块获得系统拓扑信息,对所有的潜在故障根节点进行编码,然后将潜在故障根节点编码发送给编码比较和分析模块。
标准编码文件与编码读写模块相连,标准编码文件存储标准编码。标准编码存储形式为字典,形式为{code1:[cmdb_id1,content1],code2:[cmdb_id2,content2]......},其中code1,code2.....为故障编码,cmdb_id1,cmdb_id2......为故障根节点(属于微服务节点)的编号(cmdb_id为服务节点的编号,是服务节点的固有属性,所有节点都有cmdb_id,因此故障根节点也有cmdb_id),content1,content2......为故障种类。
编码读写模块与编码模块、标准编码文件、编码比较与分析模块相连,在进行已知故障分析时,编码读写模块从编码模块获得标准编码,储存在标准编码文件中。当进行未知故障分析时,编码读写模块从标准编码文件获取标准编码,发送给编码比较与分析模块。
拓扑模块与编码模块相连,拓扑模块采用字典topology存储微服务系统的拓扑信息,包含所有节点的子节点、父节点、双向节点信息,topology是一个python字典类型数据,形式为{node1_c:node1_child_nodes,node1_p:node1_parent_nodes,node1_two:node1_bidirection_nodes,node2_c:node2_child_nodes,node2_p:node2_parent_nodes,node2_two:node2_bidirection_nodes......},其中node1_c,node1_p,node1_two,node2_c,node2_p,node2_two......为字符串类型数据,node1_child_nodes,node1_parent_nodes,node1_bidirection_nodes,node2_child_nodes,node2_parent_nodes,node2_bidirection_nodes......为节点列表,例如,node1_c是由node1的编号(cmdb_id)和字符串”c”拼接得到的字符串,node1_child_nodes为包含node1节点的子节点的列表。
编码比较与分析模块与编码读写模块、编码模块相连,当进行未知故障分析时,编码比较与分析模块从编码模块获得潜在故障根节点编码,从编码读写模块获得标准编码,然后将潜在故障根节点编码与标准编码进行比较分析,得到分析结果。
第二步,数据读取模块从微服务系统监控数据文件获取已知故障数据,数据读取模块使用pythonpandas库的read_csv()方法从微服务系统监控数据文件包含的六个文件中读取数据,并用pythonpandas库的dataframe()方法将读取的数据转化为dataframe数据类型,然后使用pythonpandas库的concat()方法将来自六个文件的dataframe类型的数据合为一个dataframe数据data,将data传送给服务依赖图构建模块。
第三步,服务依赖图构建模块从数据读取模块获得data,根据data构建服务依赖图。服务依赖图构建模块只构建故障分析目标时间窗口(该时间窗口由start_time和end_time来确定)内的服务依赖图,得到服务依赖图列表,将服务依赖图列表service_map发送给异常判断模块,start_time为时间窗口开始时间,end_time为时间窗口结束时间。具体步骤如下:
3.1服务依赖图构建模块从键盘接收用户设定的start_time和end_time;
3.2服务依赖图构建模块采用服务依赖图构建方法使用data中start_time和end_time之间的数据构建服务依赖图,得到服务依赖图列表service_map。
方法为:
3.2.1使用n表示data中的一条监控数据,将data中满足公式一的数据n筛选出来储存在use_data中:
start_time<n.starttime<end_time公式一
n.starttime表示数据n的starttime字段,将use_data中的所有数据n的traceid字段的值存储在服务依赖图标识列表trace_id中。
3.2.2去除服务依赖图标识列表trace_id中的重复数据,得到去除了重复数据的服务依赖图标识列表trace_id_no_repeat,令trace_id_no_repeat的总元素个数为n_trace_id_no_repeat。
3.2.3根据trace_id_no_repeat构建服务依赖图列表service_map,方法是:
3.2.3.1初始化服务依赖图列表service_map为空,初始化遍历服务依赖图标识列表循环变量i_trace_id_no_repeat=0。
3.2.3.2令trace_id=trace_id_no_repeat[i_trace_id_no_repeat](表示trace_id_no_repeat的第i_trace_id_no_repeat+1个元素)。
3.2.3.3在service_map添加一个空列表元素new_element,new_element用以存储一个服务依赖图,new_element存在两个域,第一个域存储该服务依赖图的traceid字段的值,第二个域用于存储该服务依赖图的服务调用。令new_element第一个域等于trace_id。
3.2.3.4将use_data中满足公式二的数据n筛选出来,储存在dataframe类型数据call_all中。
n.traceid=trace_id公式二
n.traceid表示数据n的traceid字段。
3.2.3.5令new_element的第二个域等于call_all。
3.2.3.6若i_trace_id_no_repeat<n_trace_id_no_repeat,令i_trace_id_no_repeat=i_trace_id_no_repeat+1,转步骤3.2.3.2,否则说明service_map构造完毕,转步骤3.3。
3.3服务依赖图构建模块将服务依赖图列表service_map发送给异常判断模块,令service_map中的元素总数为n_service_map。
第四步,异常判断模块从服务依赖图构建模块获得service_map,根据service_map,采用异常服务依赖图列表构建方法构建异常服务依赖图列表service_map_anomal,将异常服务依赖图列表service_map_anomal发送给显式、隐式节点确认模块,方法是:
4.1初始化异常服务依赖图列表service_map_anomal为空,service_map_anomal用于储存异常服务依赖图。
4.2异常判断模块从service_map筛选出start_time,end_time之间头服务调用存在异常的服务依赖图,构造异常服务依赖图列表service_map_anomal,方法为:
4.2.1初始化服务依赖图头服务调用异常阈值字典ave_var为空,ave_var用于储存相应种类服务调用的延迟时间的阈值,其形式为{type_cmdb_id1:t1,type_cmdb_id2:t2,type_cmdb_id3:t3......},type_cmdb_id1,type_cmdb_id2,type_cmdb_id3......为字符串类型数据,由服务调用的种类和节点编号拼接得到,t1,t2,t3......是列表类型数据,内容为:[use_data_mean,use_data_std,sigma],其中use_data_mean表示相应种类服务调用的延迟时间的平均值,use_data_std表示相应种类服务调用的延迟时间的标准差,sigma表示相应种类服务调用的延迟时间的异常阈值,其中sigma=use_data_mean+use_data_std×3。
4.2.2根据service_map构造异常服务依赖图列表service_map_anomal,方法是:
4.2.2.1初始化服务依赖图遍历循环变量i_service_map=0。
4.2.2.2令服务依赖图map=service_map[i_service_map],map存在四个域,第一个域存储该服务依赖图的traceid字段的值,第二个域用于存储该服务依赖图的服务调用,后两个域先空着作为备用。trace_id=map[0],令服务调用集合calls=map[1],表示令trace_id等于map第一个域的值,令calls等于map第二个域的值。
4.2.2.3将calls中满足公式三的唯一一条(由于每个服务依赖图中的头调用只有一条,所以满足公式三的服务调用数据只有一条)服务调用数据(令为n_calls),筛选出来储存在服务依赖图头服务调用列表headcall中。
n_calls.pid=="none"公式三
4.2.2.4令type等于headcall中"calltype"字段的值,cmdb_id等于headcall中"cmdb_id"字段的值,elapsedtime等于headcall中"elapsedtime"字段的值,将type和cmdb_id拼接得到type_cmdb_id。
4.2.2.5如果ave_var中包含了键type_cmdb_id,跳转到4.2.2.8,否则跳转到4.2.2.6。
4.2.2.6将data中满足以下条件的服务调用数据(令为n):
n.calltype=type且n.cmdb_id=cmdb_id且n.pid="none"且n.starttime>start_time-25×60×1000且n.starttime<start_time.
筛选出来储存在相应时间段内n_calls同类型调用use_data_2中,计算use_data_2中数据的elapsedtime字段数据的平均值和标准差,分别存储在平均值use_data_mean和标准差use_data_std,计算异常阈值sigma=use_data_mean+use_data_std×3,将键值对type_cmdb_id:[use_data_mean,use_data_std,sigma](type_cmdb_id是键,[use_data_mean,use_data_std,sigma]是值)存储在ave_var中。
4.2.2.7如果elapsedtime>ave_var[type_cmdb_id][2](即该服务依赖图的头服务调用的响应时间大于ave_var中存储的异常阈值),将map添加到service_map_anomal中,转4.2.2.8;如果elapsedtime≤ave_var[type_cmdb_id][2],直接转4.2.2.8。
4.2.2.8令i_service_map=i_service_map+1,如果i_service_map<n_service_map,跳转到4.2.2.2,否则说明异常服务依赖图列表service_map_anomal构造完毕,跳转到4.4。
4.3令service_map_anomal的总元素个数为n_service_map_anomal。
4.4异常判断模块筛选出service_map_anomal内部的异常服务调用,将筛选后的service_map_anomal发送给显式、隐式节点确认模块,方法为:
4.4.1初始化服务依赖图服务调用异常阈值字典ave_var_2为空,ave_var_2的结构同4.2中的ave_var。
4.4.2异常判断模块筛选出service_map_anomal内部的异常服务调用,方法是:
4.4.2.1初始化异常服务依赖图列表遍历循环变量i_service_map_anomal=0。
4.4.2.2令map=service_map_anomal[i_service_map_anomal]。
4.4.2.3初始化map的第三个域为一个空元素列表,即令map[2]为一个列表,该列表用于存储map包含的异常服务调用的抽象表示,列表中的元素的形式为[cmdb_id,cmdb_next],其中cmdb_id为发起异常服务调用的节点的编号,cmdb_next为响应该异常服务调用的节点的编号;初始化map的第四个域为一个空dataframe类型元素,即map[3]为一个dataframe类型数据,该dataframe类型元素用于存储map的异常服务调用,列为‘calltype',‘starttime',‘elapsedtime',‘success',‘traceid',‘id',‘pid',‘cmdb_id',‘servicename'。
4.4.2.4筛选出map中的异常调用,方法是:
4.4.2.4.1初始化服务调用遍历循环变量n_map=0,令服务调用遍历循环上界m_map等于map[1]的长度。
4.4.2.4.2令服务调用call等于map[1]第n_map+1行的数据。
4.4.2.4.3取call中的‘calltype’字段数据储存在type中,提取call中的‘cmdb_id’字段数据储存在cmdb_id中,提取call中的‘elapsedtime’字段数据储存在elapsedtime中,将type和cmdb_id组合为type_cmdb_id。
4.4.2.4.4如果ave_var_2已包含键type_cmdb_id,跳转到4.4.2.4.6,否则跳转到4.4.2.4.5。
4.4.2.4.5将data中满足以下条件的服务调用数据(令为n_use_data):
n_use_data.calltype=type且n_use_data.cmdb_id=cmdb_id且n_use_data.pid="none"且n_use_data.starttime>start_time-25×60×1000且n_use_data.starttime<start_time.
提取出来储存在use_data_2中,计算use_data_2中数据的elapsedtime字段数据的平均值和标准差,分别存储在平均值use_data_mean_2和标准差use_data_std_2,计算异常阈值sigma_2=use_data_mean_2+use_data_std_2×3,将键值对type_cmdb_id_2:[use_data_mean_2,use_data_std_2,sigma_2]存储在ave_var_2中。
4.4.2.4.6如果elapsedtime>ave_var_2[type_cmdb_id][2],跳转到4.4.2.4.7,否则跳转到4.4.2.4.8。
4.4.2.4.7将相应时间大于阈值的调用call添加到map的第四个域map[3]中,即令map[3]的内容为call,同时判断call及其子调用call_next组成的调用的抽象表示是否存在于map的第三个域map[2]中,如果不存在则将该调用的抽象表示添加到map[2]中,方法是:
4.4.2.4.7.1将call添加到map[3]中,令id等于call中"id"字段的值,令calls_next为map[1]中满足公式四的服务调用数据(令为data_map)的集合:
data_map.pid=id公式四
data_map.pid表示data_map的pid域。令calls_next中包含的元素的数量为t_calls_next。
4.4.2.4.7.2判断call及call_next组成的调用的抽象表示是否存在于map的第三个域map[2]中,如果不存在则将该调用的抽象表示添加到map[2]中,方法是:
4.4.2.4.7.2.1令子服务调用集合遍历循环变量k_calls_next=0。
4.4.2.4.7.2.2令call_next等于calls_next中的第_calls_next+1条数据,cmdb_next等于call_next中"cmdb_id"字段的值,令already_in_map2=0。
4.4.2.4.7.2.3将cmdb_id和cmdb_next组合为[cmdb_id,cmdb_next],如果map[2]中不包含[cmdb_id,cmdb_next],将[cmdb_id,cmdb_next]添加到map[2]中,转4.4.2.4.7.2.4;如果map[2]中包含[cmdb_id,cmdb_next],直接转4.4.2.4.7.2.4。
4.4.2.4.7.2.4令k_calls_next=k_calls_next+1,如果k_calls_next<t_calls_next,跳转到4.4.2.4.7.2.2,否则跳转到4.4.2.4.8。
4.4.2.4.8令n_map=n_map+1,如果n_map<m_map,跳转到4.4.2.4.2,否则跳转到4.4.2.4.9。
4.4.2.4.9令i_service_map_anomal=i_service_map_anomal+1,如果i_service_map_anomal<n_service_map_anomal,跳转到4.4.2.2,否则,说明service_map_anomal内部的异常服务调用筛选完毕,跳转到4.4.3。
4.4.3将内部的异常服务调用筛选完毕后的service_map_anomal发送给显式、隐式节点确认模块,令此时的service_map_anomal的总元素个数为n_service_map_anomal_2。
第五步,显式、隐式节点确认模块从异常判断模块获得内部的异常服务调用筛选完毕后的service_map_anomal,根据service_map_anomal确定显式、隐式节点,得到显式节点列表a_node和隐式节点列表b_node,将a_node,b_node发送给编码模块,具体过程为:
5.1初始化显式节点列表a_node和隐式节点列表b_node为空,a_node和b_node中的元素结构相同,有两个域,第一个域储存该显式/隐式节点的编号(即发起该次服务调用的微服务节点的编号cmdb_id),第二个域储存该显式/隐式节点的可信度,设置可信度是为了避免误差,当构造完显式、隐式节点列表之后,可信度低于某个阈值的节点将被从显式、隐式节点列表中删除。
5.2构造a_node和b_node的内容,具体步骤如下:
5.2.1初始化异常服务依赖图列表遍历循环变量i_service_map_anomal_2=0。
5.2.2令map=service_map_anomal[i_service_map_anomal_2];
5.2.3遍历map的第3个域map[2]中存储的异常调用的抽象表示,确定a_node,方法是:
5.2.3.1初始化异常服务调用抽象列表遍历循环变量n_map2=0,令异常服务调用抽象列表遍历循环上界m_map2等于map[2]的元素个数。
5.2.3.2令第一异常服务调用节点对first_next=map[2][n_map2],first_in_a=0,first_in_a变量用于记录first_next中的异常服务调用发起节点是否在显式节点列表中。
5.2.3.3遍历a_node以进行判断first_next[0]是否已经存在于a_node中,方法是:
5.2.3.3.1初始化显式节点列表遍历循环变量k_a_node=0,令显式节点列表遍历循环上界t_a_node等于a_node的长度。
5.2.3.3.2令w=a_node[k_a_node](表示显式节点列表中的第k_a_node+1项),如果w[0]=first_next[0],说明异常服务调用节点对中的异常服务调用发起节点存在于显示节点列表中,令first_in_a=1,令w[1]=w[1]+1。
5.2.3.3.3令k_a_node=k_a_node+1,如果k_a_node<t_a_node,跳转到5.2.3.3.2;否则转5.2.3.4。
5.2.3.4如果first_in_a=0,说明异常服务调用节点对中的异常服务调用发起节点不存在于显示节点列表中,需要将该节点添加到显式节点列表中,将[first,1]添加到a_node中。
5.2.3.5令n_map2=n_map2+1,如果n_map2<m_map2,跳转到5.2.3.2,否则跳转到5.2.4。
5.2.4构造隐式节点列表b_node,方法是:
5.2.4.1初始化隐式节点列表遍历循环变量n_map2_2=0,令隐式节点列表遍历循环上界m_map2_2等于map[2]的长度。
5.2.4.2令第一异常服务调用节点对first_next=map[2][n_map2_2],初始化异常服务调用发起节点变量a_exist=0,a_exist用于记录first_next中的异常服务调用响应节点是否作为异常服务调用发起节点出现过,如果某节点作为异常服务调用发起节点出现过则该节点不能成为隐式节点。a_exist=0表示first_next中的异常服务调用响应节点未作为异常服务调用发起节点出现过。
5.2.4.3遍历异常服务调用列表,判断first_next中的异常服务调用响应节点是否作为异常服务调用发起节点出现过,方法是:
5.2.4.3.1初始化异常服务调用列表遍历循环变量k_map2_2=0,令异常服务调用列表遍历循环上界t_map2_2等于map[2]的长度。
5.2.4.3.2令第二异常服务调用节点对first_next2=map[2][k_map2_2],如果first_next2[0]==first_next[1],说明first_next[1]这个节点发起过异常调用,不能成为隐式节点,令a_exist=1,跳转到5.2.4.4,否则跳转到5.2.4.3.3。
5.2.4.3.3令k_map2_2=k_map2_2+1,如果k_map2_2<t_map2_2,跳转到5.2.4.3.2,否则跳转到5.2.4.4。
5.2.4.4如果a_exist=1,跳转到5.2.5,否则跳转到5.2.4.5。
5.2.4.5令异常服务调用响应节点next=first_next[1],令判断变量next_in_b=0,next_in_b用于判断next是否存在于b_node中。
5.2.4.6将next添加到b_node中,方法是:
5.2.4.6.1初始化隐式节点列表遍历循环变量k_b_node=0,令隐式节点列表遍历循环上界t_b_node等于b_node的长度。
5.2.4.6.2令w=b_node[k_b_node],如果w[0]=next,令next_in_b=1,w[1]=w[1]+1。
5.2.4.6.3令k_b_node=k_b_node+1,如果k_b_node<t_b_node,跳转到5.2.4.6.2,否则跳转到5.2.4.6.4。
5.2.4.6.4如果next_in_b=0,说明next不在b_node中,需要将其添加到b_node中,即将二元组[next,1]添加到b_node中。
5.2.5令i_service_map_anomal=i_service_map_anomal+1,如果i_service_map_anomal<n_service_map_anomal,跳转到5.2.2,否则跳转到5.3。
5.3采用节点相抵消方法将a_node和b_node中重复的节点相抵消,方法是:
5.3.1初始化显式节点列表循环变量i_a_node_2=0,令显式节点列表循环上界n_a_node_2等于a_node的长度。
5.3.2令显示节点a=a_node[i_a_node_2]。
5.3.3遍历隐式节点列表,将与a节点相同的隐式节点从隐式节点列表中删除,方法是:
5.3.3.1初始化隐式节点列表循环变量n_b_node_2=0,隐式节点列表循环上界m_b_node_2等于b_node的长度。
5.3.3.2令隐式节点b=b_node[n_b_node_2]。
5.3.3.3如果a[0]=b[0],跳转到5.3.3.4,否则跳转到5.3.3.5。
5.3.3.4如果a[1]>b[1],令a[1]=a[1]-b[1],从b_node中移除隐式节点b,跳转到5.3.3.5。如果a[1]=b[1],从a_node中移除显式节点a,从b_node中移除隐式节点b,跳转到5.3.3.5。如果a[1]<b[1],令b[1]=b[1]-a[1],从a_node中移除显式节点a,跳转到5.3.3.5。
5.3.3.5令n_b_node_2=n_b_node_2+1,如果n_b_node_2<m_b_node_2,跳转到5.3.3.2,否则跳转到5.3.4。
5.3.4令i_a_node_2=i_a_node_2+1,如果i_a_node_2<n_a_node_2,跳转到5.3.2,否则跳转到5.4。
5.4使用低可信度节点消除方法消除显式节点列表a_node中的低可信度节点,方法为:
5.4.1初始化显式节点列表循环变量i_a_node_3=0,令显式节点列表循环上界n_a_node_3等于a_node的长度。
5.4.2令显示节点a=a_node[i_a_node_3]。
5.4.3如果a[1]<30,将a从a_node中移除。
5.4.4令i_a_node_3=i_a_node_3+1,如果i_a_node_3<n_a_node_3,跳转到5.4.2,否则说明显式节点列表a_node中的低可信度节点消除完毕,跳转到5.5。
5.5使用5.4中所述的低可信度节点消除方法消除隐式节点列表b_node中的低可信度节点。
5.6将a_node,b_node发送给编码模块。
第六步,编码模块从显式、隐式节点确认模块获得列表a_node,b_node,读取拓扑模块中的字典topology获取节点间拓扑信息,采用编码方法对用户指定的故障根节点进行编码,得到编码code,将code发送给编码读写模块,方法为:
6.1编码模块从键盘接收用户给定的故障根节点的编号cmdb_id;
6.2编码模块根据a_node,b_node、故障根节点的编号cmdb_id和字典topology进行编码,得到编码code,code用以记录故障的特征,前7位有意义第一位用于记录故障根节点本身的类型,如果故障根节点是显式节点,第一位为1,如果故障根节点是隐式节点,第一位为0。第二位用于记录故障根节点的子结点中的显式节点分布情况,如果子节点全部是显式节点,第二位为1,如果子节点部分为显式节点,第二位为0,如果子节点都不是显式节点,第二位为-1。第三位用于记录故障根节点的子结点中的隐式节点分布情况,如果子节点全部是隐式节点,第三位为1,如果子节点部分为隐式节点,第三位为0,如果子节点都不是隐式节点,第三位为-1;第四位用于记录故障根节点的双向节点中的显式节点分布情况,如果父节点全部是显式节点,第四位为1,如果父节点部分为显式节点,第四位为0,如果父节点都不是显式节点,第四位为-1;第五位用于记录故障根节点的双向节点中的隐式节点分布情况,如果父节点全部是隐式节点,第五位为1,如果父节点部分为隐式节点,第五位为0,如果父节点都不是隐式节点,第五位为-1;第六位用于记录故障根节点的双向节点中的显式节点分布情况,如果双向节点全部是显式节点,第六位为1,如果双向节点部分为显式节点,第六位为0,如果双向节点都不是显式节点,第六位为-1;第七位用于记录故障根节点的双向节点中的隐式节点分布情况,如果双向节点全部是隐式节点,第七位为1,如果双向节点部分为隐式节点,第七位为0,如果双向节点都不是隐式节点,第七位为-1;第8位暂时保留不做分配,具体过程为:
6.2.1初始化编码code=[none,none,none,none,none,none,none,none]。
6.2.2设置编码第一位,方法是:
6.2.2.1如果a_node中包含cmdb_id,令code[0]=1;
6.2.2.2如果b_node中包含cmdb_id,令code[0]=0;
6.2.3设置编码第二位,方法是:
6.2.3.1读取topology中键[cmdb_id+"_c"]对应的值,储存在列表cmdb_id_c中,初始化a_number=0,a_number记录a_node中包含的子节点的数量,令cmdb_id_c的长度为n_cmdb_id_c_2。
6.2.3.2计算a_number的值,方法是:
6.2.3.2.1令子节点列表遍历循环变量i_cmdb_id_c_2=0。
6.2.3.2.2令子节点c=cmdb_id_c[i_cmdb_id_c_2]。
6.2.3.2.3如果a_node中包含c,令a_number=a_number+1,转6.2.3.2.4;如果a_node中不包含c,直接转6.2.3.2.4。
6.2.3.2.4令i_cmdb_id_c_2=i_cmdb_id_c_2+1,如果i_cmdb_id_c_2<n_cmdb_id_c_2,跳转到6.2.3.2.2,否则跳转到6.2.3.3。
6.2.3.3如果a_number=n_cmdb_id_c_2,令code[1]=1,如果a_number>0且a_number<n_cmdb_id_c_2,令code[1]=0;如果a_number=0,令code[1]=-1。
6.2.4设置编码第三位,执行如下操作:
6.2.4.1初始化b_number=0,b_number记录b_node中包含的子节点的数量,令cmdb_id_c的长度为n_cmdb_id_c_3。
6.2.4.2计算b_number的值,方法是:
6.2.4.2.1令子节点列表遍历循环变量i_cmdb_id_c_3=0。
6.2.4.2.2令c=cmdb_id_c[i_cmdb_id_c_3]。
6.2.4.2.3如果b_node中包含c,令b_number=b_number+1,转6.2.4.2.4;如果b_node中不包含c,直接转6.2.4.2.4。
6.2.4.2.4令i_cmdb_id_c_3=i_cmdb_id_c_3+1,如果i_cmdb_id_c_3<n_cmdb_id_c_3,跳转到6.2.4.2.2,否则跳转到6.2.4.3。
6.2.4.3如果b_number=n_cmdb_id_c_3,令code[2]=1,如果b_number>0且b_number<n_cmdb_id_c_3,令code[2]=0,如果b_number=0,令code[2]=-1。
6.2.5设置编码第四位,方法是:
6.2.5.1读取topology中键[cmdb_id+"_p"]对应的值,储存在列表cmdb_id_p中,初始化a_number=0,a_number记录a_node中包含的父节点的数量,令cmdb_id_p的长度为n_cmdb_id_p_4。
6.2.5.2计算a_number的值,方法是:
6.2.5.2.1令父节点列表遍历循环变量i_cmdb_id_p_4=0。
6.2.5.2.2令c=cmdb_id_p[i_cmdb_id_p_4]。
6.2.5.2.3如果a_node中包含c,令a_number=a_number+1,转6.2.5.2.4;如果a_node中不包含c,直接转6.2.5.2.4。
6.2.5.2.4令i_cmdb_id_p_4=i_cmdb_id_p_4+1,如果i_cmdb_id_p_4<n_cmdb_id_p_4,跳转到6.2.5.2.2,否则跳转到6.2.5.3。
6.2.5.3如果a_number=n_cmdb_id_p_4,令code[3]=1,如果a_number>0且a_number<n_cmdb_id_p_4,令code[3]=0,如果a_number=0,令code[3]=-1。
6.2.6设置编码第五位,执行如下操作:
6.2.6.1初始化b_number=0,b_number记录b_node中包含的父节点的数量,令cmdb_id_p的长度为n_cmdb_id_p_5。
6.2.6.2计算b_number的值,方法是:
6.2.6.2.1令父节点列表遍历循环变量i_cmdb_id_p_5=0。
6.2.6.2.2令c=cmdb_id_p[i_cmdb_id_p_5]。
6.2.6.2.3如果b_node中包含c,令b_number=b_number+1,转6.2.6.2.4;如果b_node中不包含c,直接转6.2.6.2.4。
6.2.6.2.4令i_cmdb_id_p_5=i_cmdb_id_p_5+1,如果i_cmdb_id_p_5<n_cmdb_id_p_5,跳转到6.2.6.2.2,否则跳转到6.2.6.3。
6.2.6.3如果b_number=n_cmdb_id_p_5,令code[4]=1,如果b_number>0且b_number<n_cmdb_id_p_5,令code[4]=0,如果b_number=0,令code[4]=-1。
6.2.7设置编码第六位,执行如下操作:
6.2.7.1读取topology中键[cmdb_id+"_two"]对应的值,储存在列表cmdb_id_two中,初始化a_number=0,a_number记录a_node中包含的双向节点的数量,令于cmdb_id_two的长度为n_cmdb_id_two_6。
6.2.7.2计算a_number的值,方法是:
6.2.7.2.1令双向节点列表遍历循环变量i_cmdb_id_two_6=0。
6.2.7.2.2令c=cmdb_id_two[i_cmdb_id_two_6]。
6.2.7.2.3如果a_node中包含c,令a_number=a_number+1,转6.2.7.2.4,如果a_node中不包含c,直接转6.2.7.2.4。
6.2.7.2.4令i_cmdb_id_two_6=i_cmdb_id_two_6+1,如果i_cmdb_id_two_6<n_cmdb_id_two_6,跳转到6.2.7.2.2,否则跳转到6.2.7.3。
6.2.7.3如果a_number=n_cmdb_id_two_6,令code[5]=1,如果a_number>0且a_number<n_cmdb_id_two_6,令code[5]=0,如果a_number=0,令code[5]=-1。
6.2.8设置编码第七位,执行如下操作:
6.2.8.1初始化b_number=0,b_number记录b_node中包含的双向节点的数量,令cmdb_id_two的长度为n_cmdb_id_two_7。
6.2.8.2计算a_number的值,方法是:
6.2.8.2.1令双向节点列表遍历循环变量i_cmdb_id_two_7=0。
6.2.8.2.2令c=cmdb_id_two[i_cmdb_id_two_7]。
6.2.8.2.3如果b_node中包含c,令b_number=b_number+1,转6.2.8.2.4;如果b_node中不包含c,直接转6.2.8.2.4。
6.2.8.2.4令i_cmdb_id_two_7=i_cmdb_id_two_7+1,如果i_cmdb_id_two_7<n_cmdb_id_two_7,跳转到6.2.8.2.2,否则跳转到6.2.8.3。
6.2.8.3如果b_number=n_cmdb_id_two_7,令code[6]=1,如果b_number>0且b_number<n_cmdb_id_two_7,令code[6]=0,如果b_number=0,令code[6]=-1。
6.3将code发送给编码读写模块。
第七步,编码读写模块从编码模块获取code,从已知故障文件中获取已知故障信息,将这些信息储存在标准编码文件code.npy。具体步骤如下:
7.1编码读写模块将已知故障信息中的cmdb_id,content,code以字典形式储存在本地文件code.npy,其中cmdb_id为已知故障信息中的故障根节点,content为已知故障信息中的故障种类。
7.2构造内容为[cmdb_id,content]的已知故障信息列表。初始化字符串变量code_string=‘',‘'表示为空字符串,code_string用于存储转化为字符串的code。
7.3将code中的int类型的元素转化为string类型,并将转换后的string类型的元素拼接在一起,中间以“,”相隔,储存在字符串变量code_string中。
7.4如果code.npy文件不存在,跳转到7.5,否则跳转到7.6
7.5构造第二字典write_dictionary,令write_dictionary的code_string键对应的值为cmdbid_content,创建文件code.npy并将write_dictionary储存在文件code.npy,跳转到第八步。
7.6读取code.npy文件的内容,储存在write_dictionary中,更新write_dictionary,将write_dictionary中code_string键对应的值设为cmdbid_content。将更新后的write_dictionary再次写入到文件code.npy中。跳转到第八步。
第八步,数据读取模块,服务依赖图构建模块,异常判断模块,显式、隐式节点确认模块对未知故障数据进行预处理,方法与对已知故障数据的处理类似,具体步骤如下:
8.1数据读取模块从微服务系统监控数据文件中获取未知故障数据data_unknown,数据读取模块使用pythonpandas库的read_csv()方法从微服务系统监控数据存储文件包含的六个文件中读取数据,并用pythonpandas库的dataframe()方法将读取的数据转化为dataframe数据类型,然后使用pythonpandas库的concat()方法将来自六个文件的dataframe类型的数据合为一个dataframe数据data_unknown。数据读取模块将data_unknown传送给服务依赖图构建模块。
8.2服务依赖图构建模块从数据读取模块获得data_unknown,根据data_unknown构建故障分析目标时间窗口(该时间窗口由用户通过初始化参数start_time_unknown和end_time_unknown来确定)内的服务依赖图,得到第二服务依赖图列表service_map_unknown,start_time_unknown为时间窗口开始时间,end_time_unknown为时间窗口结束时间,start_time_unknown和end_time_unknown均由用户设定。
具体步骤如下:
8.2.1服务依赖图构建模块从键盘接收用户设定的start_time_unknown和end_time_unknown;
8.2.2服务依赖图构建模块采用3.2步所述服务依赖图构建方法,使用data_unknown中start_time_unknown和end_time_unknown之间的数据构建服务依赖图,得到第二服务依赖图列表service_map_unknown。
8.2.3服务依赖图构建模块将第二服务依赖图列表service_map_unknown传送给异常判断模块,令service_map_unknown的总元素个数为n_service_map_unknown。
8.3异常判断模块从服务依赖图构建模块获得service_map_unknown,根据service_map_unknown,采用第四步所述异常服务依赖图列表构建方法构建异常服务依赖图列表service_map_anomal_unknown。将service_map_anomal_unknown发送给显式、隐式节点确认模块,令service_map_anomal_unknown的总元素个数为n_service_map_anomal_unknown_2。
8.4显式、隐式节点确认模块从异常判断模块获得异常服务依赖图列表service_map_anomal_unknown,根据service_map_anomal_unknown采用第五步所述显式、隐式节点确定方法确定显式、隐式节点,得到第二显式节点列表a_node_unknown和第二隐式节点列表b_node_unknown。
8.5显式、隐式节点确认模块将a_node_unknown和b_node_unknown传送给深度搜索模块和编码模块。
第九步,深度搜索模块从显式隐式节点确认模块获取a_node_unknown和b_node_unknown,搜索底层显式、隐式节点(在一个服务依赖图中,节点之间存在服务调用关系,按照节点间的服务调用关系可以将它们组织为一个类似于多叉树的结构,服务调用发起节点在上层,服务调用响应节点在下层,底层显式、隐式节点分别表示在多叉树中位于最底层的显式、隐式节点),构成潜在故障根节点列表candidate_root_nodes,发送给编码模块。具体步骤如下:
9.1找出a_node_unknown中深度最大的节点,储存在列表bottom_a中,找出b_node_unknown中深度最大的节点,储存在列表bottom_b中。
9.2如果bottom_b的长度大于1,将bottom_b置为空。
9.3将bottom_a和bottom_b合并,得到潜在故障根节点列表candidate_root_nodes,将candidate_root_nodes发送给编码模块,令candidate_root_nodes包含的元素个数为n_candidate_root_nodes。
第十步,编码模块从显式、隐式节点确认模块获得a_node_unknown和b_node_unknown,从深度搜索模块获得潜在故障根节点列表candidate_root_nodes,对candidate_root_nodes中的每一个潜在根节点进行编码,得到潜在根节点编码列表candidate_nodes_codes,将candidate_nodes_codes发送给编码比较和分析模块,具体方法如下:
10.1初始化潜在根节点编码列表candidate_nodes_codes为空。
10.2对candidate_root_nodes中的每一个潜在根节点进行编码,并将得到的编码存入candidate_nodes_codes中,方法是:
10.2.1令潜在根节点遍历循环变量i_candidate_root_nodes=0,。
10.2.2令潜在根节点candidate_node=candidate_root_nodes[i_candidate_root_nodes]。
10.2.3编码模块采用第六步所述编码方法根据a_node_unknown和b_node_unknown、字典topology中的节点间拓扑信息,对潜在根节点candidate_node进行编码,得到潜在根节点编码candidate_node_code。
10.2.4将candidate_node_code存入到潜在根节点编码列表candidate_nodes_codes中。
10.2.5令i_candidate_root_nodes=i_candidate_root_nodes+1,如果i_candidate_root_nodes<n_candidate_root_nodes,跳转到10.2.2,否则得到candidate_nodes_codes,令candidate_nodes_codes的包含的元素的个数为n_candidate_nodes_codes,跳转到10.3。
10.3将candidate_nodes_codes发送给编码比较和分析模块。
第十一步,编码读写模块读取标准编码信息,生成已知故障字典,编码比较和分析模块对已知和未知故障的编码进行比较分析,得到故障根节点,具体步骤如下:
11.1编码读写模块读取标准编码文件code.npy(在第七步产生)中存储的标准编码信息,储存在已知故障字典exit_codes中,exit_codes的每一项为二元组(key,value),其中key为键,是列表形式存储的八位编码,value为对应的值,形式为列表[cmdbid,content],cmdbid,content分别表示故障根节点和故障类型,令exit_codes的包含的元素的个数为m_exit_codes。编码读写模块将exit_codes传送给编码比较和分析模块。
11.2编码比较和分析模块从编码读写模块获得exit_codes,从编码模块获得潜在根节点编码列表candidate_nodes_codes,将candidate_nodes_codes中的编码与exit_codes中的编码进行比较和分析,得出全局最优分数grade_most。具体步骤如下:
11.2.1初始化全局最优分数grade_most=0。
11.2.2计算全局最优分数grade_most,具体步骤如下:
11.2.2.1初始化潜在根节点编码列表遍历循环变量i_candidate_nodes_codes=0。
11.2.2.2令潜在根节点candidate_node_code=candidate_nodes_codes[i_candidate_nodes_codes]。
11.2.2.3编码读写和比较模块对candidate_node_code进行分析,得到candidate_node_code的最佳分数grade_best,分析方法如下:
11.2.2.3.1初始化最佳分数grade_best=0,最佳键key_best=none,最佳节点cmdb_id_best=none,最佳故障类型content_best=none。
11.2.2.3.2初始化标准编码遍历循环变量n_exit_codes=0。
11.2.2.3.3令exit_code等于exit_codes的第n_exit_codes+1项
11.2.2.3.4candidate_node和exit_code.key均为八位编码,每一位的取值为{-1,0,1}中的一个,编码读写和比较模块对candidate_node和exit_code.key对应位数的取值进行比较,计算candidate_node和exit_code.key相同的位数,相同的位数储存在当前分数grade中。
11.2.2.3.5如果grade>grade_best,令grade_best=grade,key_best=exit_code.key,cmdb_id_best=exit_code.value[0],content_best=exit_code.value[1]。
11.2.2.3.6令n_exit_codes=n_exit_codes+1,如果n_exit_codes<m_exit_codes,跳转到11.2.2.3.3,否则说明exit_codes遍历完毕,跳转到11.2.2.4。
11.2.2.4如果grade_best≥grade_most,令grade_most=grade_best。
11.2.2.5令i_candidate_nodes_codes=i_candidate_nodes_codes+1,如果i_candidate_nodes_codes<n_candidate_nodes_codes,跳转到11.2.2.2,否则说明所有的潜在故障根节点都已经进行了编码,跳转到11.3。
11.3寻找最佳分数等于grade_most的潜在故障根节点编码,具体步骤如下:
11.3.1初始化潜在根节点编码列表循环变量i_candidate_nodes_codes_2=0,潜在根节点编码列表循环上界n_candidate_nodes_codes_2等于candidate_nodes_codes的包含的元素的个数。
11.3.2令candidate_node_code=candidate_nodes_codes[i_candidate_nodes_codes_2]。
11.3.4编码读写和比较模块对candidate_node_code进行分析得到其最佳分数grade_best,分析方法见11.2.2.3。
11.3.5如果grade_best=grade_most,说明找到了故障根节点,跳转到11.3.6,否则跳转到11.3.7。
11.3.6如果candidate_node为和用户交互的主机的cmdb_id编号,那么故障类型一定是"主机网络故障",分析结果为[candidate_node,"主机网络故障"],跳转到11.3.8,否则分析结果为[candidate_node,content_best],跳转到11.3.8。(content_best是一个字符串变量,它的值可以表示多种故障类型)
11.3.7令i_candidate_nodes_codes_2=i_candidate_nodes_codes_2+1,如果i_candidate_nodes_codes_2<n_candidate_nodes_codes_2,跳转到11.3.2,否则跳转到11.3.8。
11.3.8故障根因定位结束,将分析结果输出或显示。