原标题:去哪儿系统高可用之法:搭建故障演练平台
作者介绍
王鹏,2017年加入去哪儿机票事业部,主要从事后端研发工作,目前在机票事业部负责行程单和故障演练平台以及公共服务ES、数据同步中间件等相关的研发工作。
去哪儿网2005年成立至今,随着系统规模的逐步扩大,已经有成百上千个应用系统,这些系统之间的耦合度和链路的复杂度不断加强,对于我们构建分布式高可用的系统架构具有极大挑战。我们需要一个平台在运行期自动注入故障,检验故障预案是否起效——故障演练平台。
一、背景
这是某事业部的系统拓扑图:
系统之间的依赖非常复杂、调用链路很深、服务之间没有分层。在这种复杂的依赖下,系统发生了几起故障:
三个故障原因:
各种各样的问题,在这种复杂的依赖结构下被放大,一个依赖30个SOA服务的系统,每个服务99.99%可用。99.99%的30次方≈99.7%。0.3%意味着一亿次请求会有3,000,00次失败,换算成时间大约每月有2个小时服务不稳定。随着服务依赖数量的变多,服务不稳定的概率会呈指数性提高,这些问题最后都会转化为故障表现出来。
二、系统高可用的方法论
如何构建一个高可用的系统呢?首先要分析一下不可用的因素都有哪些:
高可用系统典型实践
理论上来说,当图中所有的事情都做完,我们就可以认为系统是一个真正的高可用系统。但真是这样吗?
那么故障演练平台就隆重登场了。当上述的高可用实践都做完,利用故障演练平台做一次真正的故障演练,在系统运行期动态地注入一些故障,从而来验证下系统是否按照故障预案去执行相应的降级或者熔断策略。
三、故障演练平台
故障演练平台:检验故障预案是否真正的起作用的平台。
故障类型:主要包括运行期异常、超时等等。通过对系统某些服务动态地注入运行期异常来达到模拟故障的目的,系统按照预案执行相应的策略验证系统是否是真正的高可用。
1、故障演练平台的整体架构
故障演练平台架构主要分为四部分:
2、 Agent整体架构
目前AOP的实现有两种方式:
静态编织的问题是如果想改变字节码必须重启,这给开发和测试过程造成了很大的不便。动态的方式虽然可以在运行期注入字节码实现动态增强,但没有统一的API很容易操作错误。基于此,我们采用动态编织的方式、规范的API来规范字节码的生成——Agent组件。
Agent组件:通过JDK所提供的Instrumentation-API实现了利用HotSwap技术在不重启JVM的情况下实现对任意方法的增强,无论我们是做故障演练、调用链追踪(QTrace)、流量录制平台(Ares)以及动态增加日志输出BTrace,都需要一个具有无侵入、实时生效、动态可插拔的字节码增强组件。
Agent的事件模型
如图所示,事件模型主要可分为三类事件:
BEFORE在方法执行前事件、THROWS抛出异常事件、RETURN返回事件。这三类事件可以在方法执行前、返回和抛出异常这三种情况做字节码编织。
如下代码:
// BEFORE
try {
/*
* do something...
*/
foo();
// RETURN
return;
} catch (Throwable e) {
// THROWS
}
事件模型可以完成三个功能:
Agent如何防止“类污染”
在开发Agent的时候,第一个应用是故障演练平台,那么这个时候其实我们并不需要Agent执行的过程中有自定义结果对象的返回,所以第一个版本的Agent采用硬编码的方式进行动态织入:
故障类加载模型
首先介绍下几个类加载器:
Agent和相关的lib会放到AppClassLoader这一层去加载,利用Javasist做字节码的织入,所以Javasist的加载器就是AppClassLoader。
但是想改变的是Tomcat WebClassLoader所加载的com.xxx.InvocationHandler这个类的Invoke方法,不同的ClassLoader之间的类是不能相互访问的,做字节码的变换并不需要这个类的实例,也不需要返回结果,所以可以通过Instrument API拿到这个类加载器,并且可以根据类名称获取到这个类的字节码进行字节码变换。故障类Drill.class和变形后的com.xxx.InvocationHandler.class重新load到JVM中,完成了插桩操作。
以Dubbo为例说明下如何注入故障和解除故障:
Dubbo调用的注入过程
遇到的问题
上边的方式貌似很完美的解决了问题,但是随着平台的使用业务线要对很多接口和方法同时进行故障演练,那么我们生成的Drill类里面就会有各种:
if method==业务线定义方法
do xxx
而且很容易拼接出错并且难以调试,只能把生成的类输出为文件,查看自己写的字节码编译成class文件是否正确,简直太痛苦了!
怎么解决?
新的架构需要解决三个问题:
下一版本的Agent实现就产生了,把所有Agent的类和实现的功能抽象出来,放到一个自定义的AgentClassLoader里面,字节码注入到目标APP后可以通过反射的方式来调用具体的事件实现。
类加载模型
Agent的整体架构
Agent的整体架构如图所示:
四、如何使用
使用的好处是很明显的:
但是如何才能正确使用呢?如下图所示:
使用方法
步骤一、输入AppCode;
步骤二、选择故障方法;
步骤三、指定机器;
步骤四、注入故障。
五、总结
故障演练平台最核心的就是Agent组件——字节码编织框架,这个框架是纯Java的基于Instrumentation-API的AOP解决方案。它可以方便研发人员对于字节码插桩拆桩操作,可以很容易的实现故障演练、流量录制以及其他的应用模块。
作者:王鹏
来源:Qunar技术沙龙订阅号(ID:QunarTL)
dbaplus社群欢迎广大技术人员投稿,投稿邮箱:editor@dbaplus.cn返回搜狐,查看更多
责任编辑:
本文由永利集团304网址发布于科技互联网,转载请注明出处:去哪儿系统高可用之法