异常-异常场景的测试

在功能测试中,测试人员主要关注的是功能是否能符合预期的正常运行,比如测试一个下单的流程,关注下单、购买、发货流程是否能一气呵成。但仅仅关注正常流程是不够的,在实际的使用场景下,会出现各种非正常的情况:

一些具体的需求需要通过业务特性挖掘:

  1. 如果是秒杀这样的高并发业务场景,那么需要考虑降级、并发

  2. 依赖消息通知的业务,需要关注NSQ推送和消费相关的异常。

  3. 使用缓存的业务,需要关注缓存访问失败的情况,以及异常情况下数据一致性

  4. 如果业务流程很重要必须要保证成功,那么需要关注是否有降级、容错、重试的需求,然后在异常中验证。

  5. 异步流程需要关注时序的问题。

确定需要做哪些异常的场景的判断依据有:

  1. 根据核心链路,对重要的流程优先进行异常测试。

  2. 根据业务特性,对于大流量、高并发、资金相关的功能优先进行异常测试。

  3. 根据开发反馈线上业务的稳定情况、调用异常的情况,确认相关功能是否需要关注异常。

  4. 根据线上的错误统计和反馈的线上问题,确认是否有可以挖掘的异常场景。

异常测试的分类实施

    • 1. 功能异常
    • 2. 接口异常
    • 3. 依赖服务的异常
      • 依赖分析
      • 依赖服务的异常
    • 4. 系统异常
    • 5.页面UI异常
    • 6. MQ异常
      • 1. 什么是MQ:
      • 2. 作用是什么:
      • 3. 使用场景是什么
      • 4. 常用MQ都有哪些,区别是什么:
      • 5. 原生MQ实际代码例子, 创建生产者、消费者、queue
      • 6. 测试点:
    • 7. redis异常
      • 1. redis基础介绍:
      • 2.为什么使用redis:
      • 3.redis分片集群和哨兵集群:
      • 4. 异常测试点:

1. 功能异常

功能测试是基于用户使用场景进行设计的异常,是功能测试重要的补充部分,一般建议在功能测试阶段中就进行考虑和执行,一般采用手动的方式执行。

其中操作异常属于比较典型的功能异常,需要根据对业务的了解设计场景,拿退款的场景来说,有

  1. 重复操作:如一笔订单退款完再退款。

  2. 乱序操作:如还未支付成功就进行退款。

  3. 违规操作:如退别人的款、退款超过支付金额限制。

  4. 操作中断: 如未提交刷新/回退页面。

执行过程中需要关注:

  1. 报错信息是否恰当、准确。

  2. 错误操作后功能是否可用。

2. 接口异常

接口异常需要关注接口逻辑的正确性:

  1. 代码内部逻辑处理是否正常。

  2. 接口对外表现是否符合预期。

  3. 异常信息返回是否符合要求。

最常见的接口异常的用例就是接口参数检查的用例了。

一般接口参数需要对非法的参数进行检查,同时对于参数的类型和长度也需要做一定限制。非法的参数处理不当容易造成脏数据的问题,导致后续业务处理异常,严重的情况如SQL注入还会导致安全方面的问题,需要引起重视。

常见的处理方式有:特殊字符的转义或是报错拦截等。

除了非法参数,调用接口时的常见异常情况还有重复访问、并发访问等,这往往要求接口逻辑里采用事务机制、幂等性、锁等方式进行处理,这些都可以作为验证的点。

异常-异常场景的测试

6. MQ异常

https://www.cnblogs.com/longxok/p/10907176.html

1. 什么是MQ:

MQ 是message queue ,消息队列,也叫消息中间件,遵守JMS(java message service)规范的一种软件。(同时还有另一个叫AMQP的应用层协议,语言无关性不受产品 语言等限制,rabbitMQ支持这个 )

是类似于数据库一样需要独立部署在服务器上的一种应用,提供接口给其他系统调用。

2. 作用是什么:

队列中间件是分布式系统中重要的组件,主要解决应用解耦,异步消息,流量削锋等问题,实现高性能,高可用,可伸缩和最终一致性架构。

3. 使用场景是什么

https://www.cnblogs.com/taotaozhuanyong/p/11762995.html
在处理高并发,而且不需要立即获取结果的时候。

4. 常用MQ都有哪些,区别是什么:

目前使用较多的消息队列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ

  • activeMQ 对java支持良好,缺点是对其他语言支持不够友好,适合中小企业系统
  • rabbitMQ 对java支持良好,对其他语言也支持良好,跨平台,语言无关
  • kaffka 日志消息中间件 支持大数据场景。

5. 原生MQ实际代码例子, 创建生产者、消费者、queue

6. 测试点:

了解了上面介绍的MQ的基础使用,下面说下针对MQ的异常测试。

  1. MQ消息体中某些必填参数为NULL,或者全部必填为NULL,字段类型、长度是否不符合约定

  2. MQ消息体中参数位置错误

  3. 消息重复发送,只消费一条 —幂等性
    一般根据消息内容中唯一标识来去重

  4. 消息到达顺序不一致,导致业务异常
    比如业务是有先后顺序的
    案例1:订单下单后再取消,如果先收到取消的消息,再收到下单消息,就会有问题
    案例2:一条政策新增后马上删除,政策同步时,政策删除的消息先到达,新增的消息后到,就会导致最小价该条政策没删除,只能等全量同步的时候再删除

  5. 消息发送失败,重试次数
    1)Producer端重试
    比如网络抖动导致生产者发送消息到MQ失败,可以手动设置发送失败重试的次数
    2)Consumer端重试
    默认16次,重试时间间隔会越来越长,如果失败的多,容易堆积
    重试次数可自定义设置
    消费者端失败分2种
    A、Exception
    如反序列化失败
    B、timeout
    只有消息推送失败才需要重推,需要注意开发不要把其他失败的情况也进行重试
    如收到消息,但解析消息时,序列化失败,这种算消息发送成功的
    还比如政策同步时,消费者收到消息,但特殊情况消费异常,也去做了重试

  6. 机器重启时间段的消息,消费者能否消费到

  7. push的类型,需要测试当有生产者生成消息时,消费者是否能及时得到信息并消费

  8. Pull类型的消费者,需要测试拉取的时间间隔,间隔一段时间再有消息延迟性

  9. 消费时消费节点测试
    接线上已有的生产者,需要注意,必须设置消费开始时间,不然上线时会大批量消息过来会造成堆积,造成故障

  10. 消息丢失,业务是否兼容,是否有补偿或者监控机制
    比如政策同步消息丢失,还有全量航线同步进行补偿;
    供应商退票先发消息给供应商,退订这边会监控,临近跨退规节点,会去调供应商接口检查是否已推送供应商退票,没有退票的会自动再推一遍

  11. 消费模式注意,消息争用
    如果是集群模式,同一topic下新增新的消费组,没有申请新的group,导致一条消息投递过来,多个消费组争抢
    案例:有时候开发为了省事,预发和线上同一个topic,消费组的group也一样,上线后,可能有效消息就被预发消费组消费了
    案例:预发环境政策同步MQ的消费的grounp同一个,导致太湖和新海宜争消费

  12. 保存之后MQ比落库更快
    案例:基础数据机场接口新增或者变更一条数据后,点击发布,会同步更新数据库和发送MQ消息,测试时发现,查询收到MQ消息后去查全量接口(数据来源数据库)却没有更新的数据
    数据库更新速度没有MQ快,不应该同步,应该异步

7. redis异常

1. redis基础介绍:

Redis 是一个开源(BSD许可)的,基于内存亦可持久化的日志型、Key-Value高性能数据库

它支持存储的value类型:string(字符串)、list(链表)、set(集合)、zset(sorted set 有序集合)和hash(哈希类型)。

这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的,与memcached一样,数据都是缓存在内存中。。

redis会周期性的把更新的数据写入磁盘(rdb)或者把修改操作写入追加的记录文件(aof),并且在此基础上实现了master-slave(主从)同步。

通过 Redis哨兵(Sentinel)和自动分区(Cluster)提供高可用性

2.为什么使用redis:

redis数据库就是一款缓存数据库,用于存储使用频繁的数据,这样减少访问数据库的次数,提高运行效率。

按key设置过期时间,过期后将会自动删除。
使用场景:https://blog.csdn.net/weixin_39850167/article/details/111953693

3.redis分片集群和哨兵集群:

https://www.cnblogs.com/xuwc/p/8900717.html
哨兵的作用:
Redis-Sentinel是用于管理Redis集群,该系统执行以下三个任务:

1.监控(Monitoring):Sentinel会不断地检查你的主服务器和从服务器是否运作正常

2.提醒(Notification):当被监控的某个Redis服务器出现问题时,Sentinel可以通过API向管理员或者其他应用程序发送通知

3.自动故障迁移(Automatic failover):当一个主服务器不能正常工作时,Sentinel 会开始一次自动故障迁移操作,它会将失效主
服务器的其中一个从服务器升级为新的主服务器,并让失效主服务器的其他从服务器改为复制新的主服务器;当客户端试图连接失效的主
服务器时,集群也会向客户端返回新主服务器的地址,使得集群可以使用新主服务器代替失效服务器

分片概念:
是分割数据到多个Redis实例的处理过程,因此每个实例只保存key的一个子集。分区可以让Redis管理更大的内存,Redis将可以使用所有机器的内存。如果没有分区,你最多只能使用一台机器的内存。分区使Redis的计算能力通过简单地增加计算机得到成倍提升,Redis的网络带宽也会随着计算机和网卡的增加而成倍增长。

4. 异常测试点:

1.更新KEY异常,先删再存还是直接覆盖


案例:
查询 退改XX起 原来线上是每8小时刷新一次缓存,查询 每8小时去查退改签库拉退改签数据,根据航司作为KEY存到Redis中,再放到各机器内存中使用

这次调整为退改签有改动就发mq消息给查询,查询收到消息,则重新去拉全量数据
开发设计时有变动就全量更新,且是先删除后存入,这样可能存在大量请求过来没有拿到数据

推动修改放案:从退改数据库拉数据之后,先和Redis原来的key(航司)做hash比对,如果原来的Redis多了,则清除,剩下的再做更新操作

2.KEY删除和丢失区分


案例:
政策先同步到Redis,再发消息给数据同步build站,build站收到消息后,会去拿Redis的数据更新到mongodb,如果没有查到key会做删除操作
如果Redis 数据丢失,key不会存在,则会造成误删

解决方案:
删除key时,生成key,数据为[],查到KEY的数据为[]则删除数据;数据丢了,不生成KEY,没查到KEY去实时调接口查
build收到消息后,去更新mongdb,如果查到的key是[],则删除该条航线
build收到消息后,去更新mongdb,如果没查到该key,则在去实时调官网DSF接口

3.KEY 过期策略不当造成内存泄漏


TTL KEY TTL过期时间为秒
当 key 不存在时,返回 -2 。
当 key 存在但没有设置剩余生存时间时,返回 -1 。
否则,以秒为单位,返回 key 的剩余生存时间。
大多数业务redis都会设置过期时间,key过期时如何清理的,也需要了解下

惰性清理(被动清理):
某个KEY过期后,不会立马被删除,下次使用时检查时候过期,过期就删除

缺点:浪费内存,长期不访问没法清理,垃圾数据过多,可能引起内存泄漏

定期清理(主动清理)
Redis会定期主动淘汰一批已过期的key(随机抽取一批key检查)

缺点:KEY已过期,仍未清理,需要等待JOB扫

内存淘汰机制

当前已用内存超过maxmemory限定时,触发主动清理策略:大多数会设置一个阀值,达到一定阀值自动扩容,除非自动扩容失败,则会出问题

noeviction(默认策略):当内存不足以容纳新写入数据时,新写入操作会报错。
allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。
allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。
volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。
volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。
volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。

目前公司一般采取惰性和定期清理配合使用

4、查询Redis异常,是否实时调接口/数据库


很多情况redis只是做一个缓存机制,如果redis异常或者未取到数据,是否有实时获取数据的兜底方案,需要考虑

5、Redis击穿


某个 key 非常热点,访问非常频繁,处于集中式高并发访问的情况,当这个 key 在失效的瞬间,大量的请求就击穿了缓存,直接请求数据库,就像是在一道屏障上凿开了一个洞。

解决方案:可以将热点数据设置为永远不过期;或者基于 redis or zookeeper 实现互斥锁,等待第一个请求构建完缓存之后,再释放锁,进而其它请求才能通过该 key 访问数据;或者用Hash方法对比KEY进行增删改

6、缓存雪崩


对于系统 A,假设每天高峰期每秒 5000 个请求,本来缓存在高峰期可以扛住每秒 4000 个请求,但是缓存机器意外发生了全盘宕机。缓存挂了,此时 1 秒 5000 个请求全部落数据库,数据库必然扛不住,它会报一下警,然后就挂了。此时,如果没有采用什么特别的方案来处理这个故障,DBA 很着急,重启数据库,但是数据库立马又被新的流量给打死了。

解决方案:
事前:redis 高可用,主从+哨兵,redis cluster,避免全盘崩溃。
事中:本地 ehcache 缓存 + hystrix 限流&降级,避免 MySQL 被打死。
事后:redis 持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据

7、缓存穿透


对于系统A,假设一秒 5000 个请求,结果其中 4000 个请求是黑客发出的恶意攻击。
黑客发出的那 4000 个攻击,缓存中查不到,每次你去数据库里查,也查不到。

解决方案:

每次系统 A 从数据库中只要没查到,就写一个空值到缓存里去,比如 set -999 UNKNOWN。然后设置一个过期时间,这样的话,下次有相同的 key 来访问的时候,在缓存失效之前,都可以直接从缓存中取数据。

8、Redis锁,使用不当造成锁不能释放,陷入死锁


目前常用的2种锁:

1)SET Key UniqId Seconds

仅在单实例的场景下是安全的,不用setnx+expire+del 中间断了仍可能造成死锁;不用SET Key UnixTimestamp Seconds NX,高并发可能存在相同时间戳

2)分布式Redis锁:Redlock

此种方式比原先的单节点的方法更安全

安全性:在同一时间不允许多个Client同时持有锁。
活性死锁:锁最终应该能够被释放,即使Client端crash或者出现网络分区(通常基于超时机制)。
容错性:只要超过半数Redis节点可用,锁都能被正确获取和释放。

案例:
综合推荐获取航班动态的redis数据时,某个航线没有推荐出来,高并发生成相同时间戳redis的KEYLOCK,redis解锁失败导致

9、Redis持久化


当Redis数据需要长久有效时,需要考虑是否做RDB和AOF持久化,一般RDB和AOF配合使用,但做持久化,会影响性能,目前做持久化的很少见

比如如来Redis数据是长久有效的,但却为了响应快不影响性能,未做持久化;采用了其他的降级方案Hbase,以及业务的兜底

10、缓存与数据库双写时的数据一致性


一般来说,就是如果你的系统不是严格要求缓存+数据库必须一致性的话,缓存可以稍微的跟数据库偶尔有不一致的情况,最好不要做这个方案,读请求和写请求串行化,串到一个内存队列里去,这样就可以保证一定不会出现不一致的情况
串行化之后,就会导致系统的吞吐量会大幅度的降低,用比正常情况下多几倍的机器去支撑线上的一个请求。
还有一种方式就是可能会暂时产生不一致的情况,但是发生的几率特别小,就是先更新数据库,然后再删除缓存。

并行写数据库和缓存,可以加个事务都写成功才成功,有一个环节失败了就回滚事务,全失败

问题场景 描述
先写缓存,再写数据库,缓存写成功,数据库写失败 缓存写成功,但写数据库失败或者响应延迟,则下次读取(并发读)缓存时,就出现脏读
先写数据库,再写缓存,数据库写成功,缓存写失败 写数据库成功,但写缓存失败,则下次读取(并发读)缓存时,则读不到数据
需要缓存异步刷新 指数据库操作和写缓存不在一个操作步骤中,比如在分布式场景下,无法做到同时写缓存或需要异步刷新(补救措施)时候

来源:Nine Days

声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2021年3月21日
下一篇 2021年3月21日

相关推荐