目录
- 为什么要做异常测试
- 我们要做什么
- 异常测试实践
- 总结与展望
摘要
TC资源中心是交易平台业务链路中客户购买相关服务后将相关服务资源托管的管理中心,是我们进行资源管理及后续收入核算的核心服务。每天过亿的调用量,TC资源服务轻微的异常带来的损失也是非常巨大的。基于此,本文主要是针对TC资源中心的降级预案,做的一些异常测试相关的尝试和实践。
正文
- 为什么要做异常测试
基于交易平台TC资源中心,我们曾经遇到的线上事故: (1)2019年 交易平台TC系统依赖异常(最终导致线上数据异常,影响下游财务的正常结算) (2)2020年 交易平台TC摊销数据异常(最终导致线上数据异常,影响下游财务的正常结算) 总结:任何基础设施(主要是生产系统)、任何流程(业务流、数据流等)都可能出现问题,没有经过重大灾难验证的容灾设施都是耍流氓。
-
基于交易平台TC资源中心,我们的痛点: (1)服务多:微服务多,依赖关系复杂,分布式,高可用等 (2)构造复杂场景困难:服务依赖异常构造复杂,多个异常故障同时模拟困难,多节点操作等 (3)测试成本高:人工执行异常,异常效果比对困难,异常测试回归效率低,不同时段对服务进行异常测试,无法精准性操作等 (4)学习成本高:需要学习各种异常测试相关的知识、熟练掌握各种系统工具/命令、网络工具等,且需要具备开发相关异常脚本的能力等 -
基于交易平台TC资源中心,我们的目标: 希望通过技术手段来保证TC服务的高可用和高稳定性。即大于4个9的公司标准。 -
基于交易平台TC资源中心,实现服务高可用/高稳定性的一些可用的技术手段:(本篇文章重点在“降级”的异常测试实践,其他暂未涉及) 1)负载均衡与反向代理(如:Nginx) 2)隔离或解耦(如:线程隔离、进程隔离、集群隔离、机房隔离、读写隔离、快慢隔离、动静隔离、爬虫隔离、热点隔离、资源隔离等) 3)限流(如:应用级限流、分布式限流等) 4)降级(如:降级预案、人工开关降级、自动开关降级、读服务降级、写服务降级、多级降级等) 5)超时与重试机制(如:代理层超时、Web容器超时、中间件超时、数据库超时、NoSQL超时、业务超时、前端超时等) 6)回滚机制(如:事务回滚、代码库回滚、部署版本回滚、数据版本回滚、静态资源版本回滚等) 7)压测与预案(如:系统压测(线上或线下)、系统容灾、应急预案等)
-
我们要/能做什么 异常测试流程/方法论(个人总结仅供参考) (1)异常知识or工具学习(常见的异常详见上面) (2)异常模拟脚本/命令开发(其实就是通过脚本或命令模拟上面的常见异常) (3)构造压测数据模拟压力(有些异常的模拟,需要通过压测来触发) (4)初始化环境(异常测试需要的环境) (5)手工或半自动化执行异常脚本/命令 (6)服务日志正确性(触发异常后业务代码通常会捕获并写log,线上环境可以对比有降级和没降级的情况线上流量/调用量变化曲线) -
异常测试实践 【实践一】:A服务第三方依赖服务降级测试
-
背景: 当前A服务的交易情况: A服务当前每日流量4亿左右,高峰QPS为15000次/s,接口平均响应时间为2ms左右。因此A服务的任何一次异常带来的影响都可能是灾难级的。 比如: 下图为某次调用依赖服务超时的场景,带来的影响就是那段时间内丢失平时近三分之一的流量。 -
降级技术选型:Sentinel 为什么选Sentinel? Sentinel简介:https://sentinelguard.io/zh-cn/ https://github.com/alibaba/Sentinel/wiki 具体技术细节略,有兴趣可以私聊! -
如何做异常测试呢?直接套用上面提到的【异常测试流程】 1、学习代码–需要学习基于Sentinel的代码实现(每一种技术的落地可能都有些自定义或差异化的东西,所以代码肯定是要撸的) 2、读接口和写接口降级原则不同,如何操作才能触发降级? a.对于大多数的查询接口来说,降级后可以直接快速失败,在应用层不用做过多的处理,比如直接返回查询异常给应用层,上层应用收到查询结果后会可能会进行其他处理。 b.对于写接口在降级后就得按不同的业务场景进行处理,比如继续提供有损服务或者拒绝服务。 i) 继续提供有损服务:比如支付系统的充值场景里在充值推广现金时需要去查询VIP进行优惠的获得,此时对查询优惠的接口降级后可让充值操作继续进行,但是记录降级的日志,可以后续等服务恢复后再把充值的优惠补回来,此时相当于提供了有损的服务。 ii) 拒绝服务:在nresource的资源添加操作时,需要根据payId查询该笔充值操作是否真正的在支付成功过,若查询不到则要拒绝添加,防止异常的payId来进行资源添加。 3、降级阈值的设置 a.偶发的网络抖动不能触发降级; b.降级尽可能的迅速; 阈值的设置是降级里边最重要的一环,设置的不合适的话会造成该降级的时候没降级,偶尔的网络抖动又进行了降级(偶尔的网络抖动不应该进行降级,可以对超时进行重试)。 4、降级粒度尽可能的细,若RPC服务里边同时存在A和B的重载方法,不能因为对A的降级而影响B的降级。 (现在很多依赖服务里边会有方法的重载,当对A方法的降级不能把对B方法的调用也一并降级了,因为此时假设A和B方法在服务方不同的分组,此时可能只是A所在的分组有问题,B应该仍能正常调用的。这里多说一句,scf自带的Hystrix框架也是对依赖进行降级的,但问题是没有考虑到重载方法,所以可能在对A降级的时候也把B降级了。解决方案是将Sentinel降级时的resource key设置了最细粒度,使用 ServiceName() + “.” +Lookup() + “.” + MethodName()+方法各个参数类型来保证降级资源的唯一性。这样就保证了只对唯一的方法进行降级) 5、基于上面的1~4,我们已经了解了代码逻辑的实现细节,开始针对性的做异常模拟及测试。 a. 影响范围(基于上面的分析),影响接口数量9个(具体接口就不一一列出了) b. 触发降级的方式3种: i) unionbrokerscf调用超时降级 ii) nresource调用超时降级 iii) matrix超时触发降级 c. 如何模拟3种异常并触发降级(此处只以第1种降级来说明,其他两种类似操作即可) unionbrokerscf调用超时降级 n种方式: (1)把测试环境对应的依赖服务unionbrokerscf所在机器关闭 --相对暴力! (2)把测试环境对应的依赖服务unionbrokerscf所在机器网卡关闭 --相对暴力! (3)把测试环境对应的依赖服务unionbrokerscf所在机器资源打满 --相对暴力! (4)把测试环境对应的依赖服务unionbrokerscf本身关闭 --相对暴力! (5)把测试环境对应的依赖服务unionbrokerscf压力打满 --相对暴力! (6)把测试环境对应的依赖服务unionbrokerscf调用超时设为1ms --显然这种更符合线上异常场景! 这里我们选择第(6)种方式,在manager测试平台设置第三方依赖unionbrokerscf服务超时时间为1ms,如下图: 设置完成后,部署我们的被测服务A(也可以先部署,后设置超时时间)。 6、编写压测脚本(比如:getOrders接口的jmeter压测脚本如下) 略—代码比较简单,就不赘述了! 7、然后执行压测时,需要根据sentinel注解中的参数(详见如下代码)来设置合理的并发数来触发降级。
@DemotionSettings(
count = 0.2d,
statIntervalMs = 1000,
timeWindow = 1,
serviceName = "unionbrokerscf",
lookUp = "UnionbrokerServiceImpl",
minRequestAmount = 20
)
@Override
public UnionBrokerAccount queryUnionBrokerAccount(Long aLong, Integer integer) {
logger.error("queryUnionBrokerAccount 触发降级,userId {} userType {}", aLong, integer);
return null;
}
其中:timeWindow=1表示统计窗口1秒;minRequestAmount = 20表示每秒最少的请求数是20个,然后count = 0.2d表示在每秒n个(n>=20)请求时,超时达到或超过n的0.2个(比如:20*0.2=4)就会触发降级!serviceName=unionbrokerscf即触发unionbrokerscf个服务降级! 综上,我们就可以设置jmeter的并发数了,比如:如下图 备注:不同服务的降级参数即注解可能value都不一样,所以针对不同服务的降级,对应影响接口的压测脚本也不同!切记先撸代码,再写脚本,最后调并发(别把服务打挂了)! 8、怎么确认是否降级了呢?我们可以通过日志里看(前提让开发加上触发降级的日志)
[work(work)@tjtxcs99-11-22 AAAAAAA]$ grep 降级 * | wc -l
4345
值得一提的是:线上环境还可以对比有降级和没降级的情况线上流量/调用量变化曲线,以确认降级的效果是否达到预期! 综上,我们就完成了基于sentinel的超时降级异常测试!
- 总结与展望
总之,对于异常测试也是初次尝试,目前能做的异常测试(手工测试)还是比较单一的。
|