定义:异常场景的目标是在出现故障(操作系统、数据库、中间件、进程等等故障)时,验证业务受到的影响。异常场景就是为了模拟生产故障出现时的场景,以此来验证业务系统能不能自动调整。
在性能的领域中,异常场景一直都处在薄弱的环节,大家都觉得异常场景应该做,但是又不知道怎么做才能把异常问题覆盖全面。异常范围之所以比较难确定,是因为有很多问题都被归纳到了“异常”中,比如说高可用、可靠性、灾备等等。当然,也有些企业把这部分归纳到非功能中来做,因此在性能的项目中就不会有异常场景了。在性能工程理论中,异常场景是必须要做的,这主要是因为异常场景需要压力背景。
性能测试中的异常场景需要明确两个关键点:一是异常场景的范围,二是异常场景的设计逻辑,我们就来看看如何确定异常场景的范围和设计逻辑。
异常场景范围
在以前的异常场景中,基本上采用的是宕主机、断网络、宕应用这几种测试手段,此外,从主机、网络、应用等视角出发,还会有一些细分操作,比如说:
-
主机:断电、reboot、shutdown 等
-
网络:ifdown 命令关网卡、模拟抖动丢包延时重传等
-
应用:kill、stop 等
上述这些操作在当前新的技术架构中仍然有效,只不过现在会有更多的细分操作。因为现在微服务的应用多了之后,又多出了几层,比如虚拟机层、容器层、网关层等等。我在这里画一张图,大概罗列一下异常场景测试的不同角度:
异常场景设计
从逻辑上来说,异常场景的设计主要分为两步:
-
分析架构:把技术架构中的组件全部列出来,并分析可能产生异常的点
-
列出异常场景:根据分析的异常点设计对应的场景。
这样的逻辑看起来并不复杂,如果我们只从组件级来考虑,那就可以设计通用的异常场景了,如果从业务逻辑异常的视角来看,就没有通用的异常场景了,我们需要针对不同业务设计不同的异常场景。
应用异常
在当前微服务发展迅猛的今天,微服务的限流和降级熔断是必不可少的,用这两个示例来讲解。
-
限流
我们先用压力工具产生700~800TPS,之后把限流设置在 100 以下,然后看压力工具中的报告,只有约 20% 的请求通过了,其他的都被拒绝了,说明这个限流产生了效果。对应的系统资源使用率也下降了,这就达到了限流的效果,至少保证了这个服务自己不会死。
当我们把限流规则删掉之后,看到请求也是可以恢复的,说明限流是生效的,并且效果还挺明显。
-
降级熔断
我们先把压力场景跑起来并持续运行,然后把 服务A的最大响应时间设置为 10ms,由于响应时间大于降级规则中的最大响应时间 10ms,所有请求随后被熔断 10s,在这期间TPS 是直接掉到零的,并且也报了错。说明降级熔断规则确实生效了,当我们删除了规则之后,TPS 也就恢复了。
CPU 异常
CPU异常是性能分析中的一大要点,在生产环境中出现CPU问题的话,最重要的是cpu使用降下来之后,系统能不能快速恢复
使用 stress 命令模拟 CPU 被消耗完,我打算把 6 个 C 全都占完:
stress -c 6 -t 100 模拟6个cpu运行100s,真实环境有几个cpu就模拟几个
使用 top 命令查看一下, 可以发现us cpu使用率很高了
%Cpu0 : 97.3 us, 2.3 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.3 si, 0.0 st %Cpu1 : 93.5 us, 2.4 sy, 0.0 ni, 2.4 id, 0.0 wa, 0.0 hi, 1.7 si, 0.0 st %Cpu2 : 98.0 us, 1.3 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.7 si, 0.0 st %Cpu3 : 98.0 us, 1.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 1.0 si, 0.0 st %Cpu4 : 97.7 us, 1.3 sy, 0.0 ni, 0.3 id, 0.0 wa, 0.0 hi, 0.7 si, 0.0 st %Cpu5 : 94.2 us, 3.1 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 2.7 si, 0.0 st
从 TPS 和响应时间上来看,业务确实是变慢了。但是,没有报错,这个场景就是典型的 CPU 不足把应用拖慢的情况了。
内存异常
内存异常是性能分析中的一大要点,在生产环境中出现内存问题的话,最重要的是系统能不能快速恢复
我们用下面这个命令来模拟一下,我们用 30 个工作线程模拟分配 10G 的内存并持续 50 秒:
stress --vm 30 --vm-bytes 10G --vm-hang 50 --timeout 50s
在压力持续期间,可以看到应用照样没有报错,只是响应时间增加了很多。操作系统还是比较坚强的,即便是资源不够用了,它也在努力为你服务。
因此,如果你在执行异常场景时,看到 CPU 或内存消耗高、TPS 下降或者响应时间增加时,并且在模拟异常停止的时候,TPS 和响应时间也一直没有恢复,那就是 Bug 了,因为我们期望的是业务能恢复。
网络异常
常见的网络异常就是时延和丢包,用 tc 命令网络的丢包和时延举例一下
10%:
-
丢包
tc qdisc add dev eth0 root netem loss 10% 模拟网卡丢包10%
在模拟丢包的过程中,我们只丢包了 10%,并不是全丢,TPS 就会大幅下降。之所以丢包会导致TPS下降,是因为TCP是一个可靠协议,丢包会触发TCP 重传机制。
这时候抓包的话,会看到 retransmission 的包,在这种情况之下,响应时间就会增加,但是业务还没到报错的程度,为了看到当前的网络健康状态,我们在丢包过程中,使用 ping 命令来查看。
从逻辑上来看,丢包重传会导致 TPS 下降和响应时间增加,但是并不会报错,这就是 TCP 功劳了。不过,要是在模拟的整个过程中,业务都没有自动恢复,那你就要报 Bug 了。
-
时延
时延问题很常见,偶尔出现的大流量或者网络设备资源争用,都有可能导致网络延迟。我们在延迟高的时候,要关注一下网络设备,比如路由器、交换机,看看它们有没有出现延迟高的问题。
tc qdisc add dev eth0 root netem delay 100ms 给网络加上100ms的延迟
时延也会导致 TPS 下降和响应时间增加,但是并不会报错。
总结
我们对异常场景最基础的预期就是,在异常出现的时候,系统能快速恢复,这也是我们做异常场景的价值。如果不能快速恢复,业务也就随着异常掉下去了,这时候我们就要提 Bug、提风险。
在这篇文章中,我们模拟了应用级异常、操作系统内部异常、容器级异常和整个操作系统级异常,在当前的微服务架构中,这些都是经常出现的异常场景。当然,它们并不能覆盖微服务架构中的全部异常场景。你可以根据上节课讲的异常范围图,把缺少的异常场景设计出来,以覆盖全面。