【软件测试】盘一盘工作中遇到的 MQ 异常测试

上一篇小结了一下关于redis的异常测试,今天再来盘一盘 MQ 相关的。

MQ 跟 redis 一样,也是现在系统服务中不可或缺的重要中间件,通常用来流量削峰、应用解耦、异步处理等。
之前有过一篇整理【MQ 快速入门】介绍、分类、组成、优缺点、测试点,有兴趣也可以跳过去看看。

日常经手的系统主要用的是 RocketMQ,是阿里系下开源的一款分布式、队列模型的消息中间件,是阿里参照kafka设计思想使用java实现的一套MQ,并做了自己的改进。被广泛的应用在订单、交易、充值、流计算、消息推送、日志流处理等场景。

这里再简述一些知识点。

一、RocketMQ 消息模式

RocketMQ中,也存在两种消息模式,分别为集群消费模式广播消费模式

集群消费模式

RocketMQ默认的消息模式就是集群模式,当存在多个消费者时,消息通过一定负载均衡策略,将消息分发到多个consumer中。

比如现在有3个消费者,那么一条消息投递过来,只会被consumer 1、consumer 2、consumer 3中的一个消费。

在RockeMQ中,通过ConsumeGroup的机制,实现了天然的消息负载均衡,可以非常方便的通过加机器来实现水平扩展。

广播消费模式

这种模式下,会把消息分发给每一个消费者。一条消息投递过来,会被 consumer 1、consumer 2、consumer 3都消费一次,就像发了条朋友圈,你的朋友都可以看见。

目前我们用的比较多的是集群模式,在集群模式也可以模拟广播消费。

二、push 和 pull 优缺点

对于任何一款消息中间件而言,消费者客户端一般有两种方式从消息中间件获取消息并消费。

Pull方式

由消费者客户端主动向消息中间件(MQ消息服务器代理)拉取消息。

适用场景:对于生产者生产消息数据比较大时,而消费端处理比较复杂,消费能力相对较低。

优点:消费者可以依据自己的消费能力进行消费,生产者不需要维护和消费者之间的会话。

缺点:拉取消息的间隔不太好设置。间隔太短,对服务器请求压力过大。间隔时间过长,那么必然会造成一部分数据的延迟,实时性相对较低。

优化方案:
长轮询的消费方式,需要Server和Client的配合才能够实现。

即Client发送消息请求,Server端接受请求,如果发现Server队列里没有新消息,Server端不立即返回,而是持有这个请求一段时间(通过设置超时时间来实现),在这段时间内轮询Server队列内是否有新的消息,如果有新消息,就利用现有的连接返回消息给消费者;如果这段时间内没有新消息进入队列,则返回空。

长轮询的弊端:在持有消费者请求的这段时间,占用了系统资源,因此长轮询适合客户端连接数可控的业务场景中。

Push方式

由消息服务端主动地将消息推送给消费者,尽可能实时地将消息发送给消费者进行消费。

适用场景:对于数据实时性要求高的场景。

优点:生产者主动推送给消费者,及时性很高。

缺点:当消费者消费能力远低于生产者生产能力,那么一旦生产者推送大量消息到消费者时,就会导致消费者消息堆积,处理缓慢,甚至服务崩溃。
另外,生产者需要维护和每个消费者之间的会话。

优化方案:不采用 http 长连接的方法保持会话,采用 socket 监听。

三、刷盘策略

RocketMQ的存储读写是基于JDK NIO的内存映射机制的,消息存储时首先将消息追加到内存中,再根据不同的刷盘策略在不同的时间进行刷盘。

同步刷盘

同步刷盘是指数据到达内存之后,必须刷到commitlog日志之后才算成功,然后返回producer数据已经发送成功。

异步刷盘

指数据到达内存之后,返回producer说数据已经发送成功,然后再写入commitlog日志。

什么是commitlog

commitlog 就是来存储所有的元信息,包含消息体,类似于Mysql、Oracle 的 redolog。所以只要有 CommitLog 在,Consume Queue即使数据丢失,仍然可以恢复出来。

而 consumequeue,就是用来记录数据的位置,以便 Consumer 快速通过 consumequeue 找到 commitlog 中的数据。

四、MQ 异常测试

MQ消息体

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

消息重复发送

消息重复发送,只消费一条,一般根据消息内容中唯一标识来去重。

消息到达顺序不一致

消息到达顺序不一致,导致业务异常。

比如:订单下单后再取消,如果先收到取消的消息,再收到下单消息,就会有问题。

消息发送失败重试

Producer端重试

比如网络抖动导致生产者发送消息到MQ失败,可以手动设置发送失败重试的次数。

Consumer端重试

默认16次,重试时间间隔会越来越长,如果失败的多,容易堆积。这里的重试次数可自定义设置。

值得注意的是,只有消息推送失败才需要重推,不要把其他失败的情况也进行重试。

接线上生产者

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

消息丢失

消息丢失,业务是否兼容,是否有补偿或者监控机制。

消息争用

如果是集群模式,同一topic下新增新的消费组,但是没有申请新的group,导致一条消息投递过来,多个消费组争抢。

比如开发为了省事,预发和线上同一个topic,消费组的group也一样,上线后,可能存在有效消息被预发消费组消费了。

MQ比落库快

比如某接口A,新增一条数据后会同步更新DB和发送MQ给服务B,服务B收到消息后查询DB这条数据。曾经发现了收到消息却查不到数据的情况,因为数据库更新速度没有MQ快,后来改成异步了。

上一篇:K8S部署rocketmq单机和集群


下一篇:kafka的Rebalance机制