rotcketmq实践 吞吐量 发送重试 重试消费 异步 批量发送

吞吐量对比

Kafka、RabbitMQ、RocketMQ消息中间件的对比------ 消息发送性能

测试目的

对比Kafka、RabbitMQ、RocketMQ发送小消息(124字节)的性能。这次压测我们只关注服务端的性能指标,所以压测的标准是:

不断增加发送端的压力,直到系统吞吐量不再上升,而响应时间拉长。这时服务端已出现性能瓶颈,可以获得相应的系统最佳吞吐量。

在同步发送场景中,三个消息中间件的表现区分明显:

Kafka的吞吐量高达17.3w/s,不愧是高吞吐量消息中间件的行业老大。这主要取决于它的队列模式保证了写磁盘的过程是线性IO。此时broker磁盘IO已达瓶颈。

RocketMQ也表现不俗,吞吐量在11.6w/s,磁盘IO %util已接近100%。RocketMQ的消息写入内存后即返回ack,由单独的线程专门做刷盘的操作,所有的消息均是顺序写文件。

RabbitMQ的吞吐量5.95w/s,CPU资源消耗较高。它支持AMQP协议,实现非常重量级,为了保证消息的可靠性在吞吐量上做了取舍。我们还做了RabbitMQ在消息持久化场景下的性能测试,吞吐量在2.6w/s左右。

测试结论

在服务端处理同步发送的性能上,Kafka>RocketMQ>RabbitMQ。

测试环境

服务端为单机部署,机器配置如下:

CPU 24核

内存 94G

硬盘 Seagate Constellation ES (SATA 6Gb/s) 2,000,398,934,016 bytes [2.00 TB] 7202 rpm

网卡 1000Mb/s

应用版本:

消息中间件 版本

Kafka 0.8.2

RabbitMQ 3.5.4

RocketMQ 3.4.6

测试脚本

压力端 Jmeter的java客户端

消息大小 128字节

并发数 能达到服务端最大TPS的最优并发

Topic分区数量 8

刷盘策略 Kafka和RocketMQ为异步落盘,RabbitMQ的Queue不开启durable持久化

kafka与RocketMQ的对比

RocketMQ与Kafka对比(18项差异)

淘宝内部的交易系统使用了淘宝自主研发的Notify消息中间件,使用Mysql作为消息存储媒介,可完全水平扩容,为了进一步降低成本,我们认为存储部分可以进一步优化,2011年初,Linkin开源了Kafka这个优秀的消息中间件,淘宝中间件团队在对Kafka做过充分Review之后,Kafka无限消息堆积,高效的持久化速度吸引了我们,但是同时发现这个消息系统主要定位于日志传输,对于使用在淘宝交易、订单、充值等场景下还有诸多特性不满足,为此我们重新用Java语言编写了RocketMQ,定位于非日志的可靠消息传输(日志场景也OK),目前RocketMQ在阿里集团被广泛应用在订单,交易,充值,流计算,消息推送,日志流式处理,binglog分发等场景。

数据可靠性

RocketMQ支持异步实时刷盘,同步刷盘,同步Replication,异步Replication

Kafka使用异步刷盘方式,异步Replication/同步Replication

总结:RocketMQ的同步刷盘在单机可靠性上比Kafka更高,不会因为操作系统Crash,导致数据丢失。

Kafka同步Replication理论上性能低于RocketMQ的同步Replication,原因是Kafka的数据以分区为单位组织,意味着一个Kafka实例上会有几百个数据分区,RocketMQ一个实例上只有一个数据分区,RocketMQ可以充分利用IO Group Commit机制,批量传输数据,配置同步Replication与异步Replication相比,性能损耗约20%~30%,Kafka没有亲自测试过,但是个人认为理论上会低于RocketMQ。

性能对比

Kafka单机写入TPS约在百万条/秒,消息大小10个字节

RocketMQ单机写入TPS单实例约7万条/秒,单机部署3个Broker,可以跑到最高12万条/秒,消息大小10个字节

总结:Kafka的TPS跑到单机百万,主要是由于Producer端将多个小消息合并,批量发向Broker。

RocketMQ为什么没有这么做?

Producer通常使用Java语言,缓存过多消息,GC是个很严重的问题

Producer调用发送消息接口,消息未发送到Broker,向业务返回成功,此时Producer宕机,会导致消息丢失,业务出错

Producer通常为分布式系统,且每台机器都是多线程发送,我们认为线上的系统单个Producer每秒产生的数据量有限,不可能上万。

缓存的功能完全可以由上层业务完成。

单机支持的队列数

Kafka单机超过64个队列/分区,Load会发生明显的飙高现象,队列越多,load越高,发送消息响应时间变长。Kafka分区数无法过多的问题

RocketMQ单机支持最高5万个队列,Load不会发生明显变化

队列多有什么好处?

单机可以创建更多Topic,因为每个Topic都是由一批队列组成

Consumer的集群规模和队列数成正比,队列越多,Consumer集群可以越大

消息投递实时性

Kafka使用短轮询方式,实时性取决于轮询间隔时间,0.8以后版本支持长轮询。

RocketMQ使用长轮询,同Push方式实时性一致,消息的投递延时通常在几个毫秒。

消费失败重试

Kafka消费失败不支持重试。

RocketMQ消费失败支持定时重试,每次重试间隔时间顺延

总结:例如充值类应用,当前时刻调用运营商网关,充值失败,可能是对方压力过多,稍后再调用就会成功,如支付宝到银行扣款也是类似需求。

这里的重试需要可靠的重试,即失败重试的消息不因为Consumer宕机导致丢失。

严格的消息顺序

Kafka支持消息顺序,但是一台Broker宕机后,就会产生消息乱序

RocketMQ支持严格的消息顺序,在顺序消息场景下,一台Broker宕机后,发送消息会失败,但是不会乱序

Mysql Binlog分发需要严格的消息顺序

定时消息

Kafka不支持定时消息

RocketMQ支持两类定时消息

开源版本RocketMQ仅支持定时Level,定时Level用户可定制

阿里云ONS支持定时Level,以及指定的毫秒级别的延时时间

分布式事务消息

Kafka不支持分布式事务消息

阿里云ONS支持分布式定时消息,未来开源版本的RocketMQ也有计划支持分布式事务消息

消息查询

Kafka不支持消息查询

RocketMQ支持根据Message Id查询消息,也支持根据消息内容查询消息(发送消息时指定一个Message Key,任意字符串,例如指定为订单Id)

总结:消息查询对于定位消息丢失问题非常有帮助,例如某个订单处理失败,是消息没收到还是收到处理出错了。

消息回溯

Kafka理论上可以按照Offset来回溯消息

RocketMQ支持按照时间来回溯消息,精度毫秒,例如从一天之前的某时某分某秒开始重新消费消息

总结:典型业务场景如consumer做订单分析,但是由于程序逻辑或者依赖的系统发生故障等原因,导致今天消费的消息全部无效,需要重新从昨天零点开始消费,那么以时间为起点的消息重放功能对于业务非常有帮助。

消费并行度

Kafka的消费并行度依赖Topic配置的分区数,如分区数为10,那么最多10台机器来并行消费(每台机器只能开启一个线程),或者一台机器消费(10个线程并行消费)。即消费并行度和分区数一致。

RocketMQ消费并行度分两种情况

顺序消费方式并行度同Kafka完全一致

乱序方式并行度取决于Consumer的线程数,如Topic配置10个队列,10台机器消费,每台机器100个线程,那么并行度为1000。

消息轨迹

Kafka不支持消息轨迹

阿里云ONS支持消息轨迹

开发语言友好性

Kafka采用Scala编写

RocketMQ采用Java语言编写

Broker端消息过滤

Kafka不支持Broker端的消息过滤

RocketMQ支持两种Broker端消息过滤方式

根据Message Tag来过滤,相当于子topic概念

向服务器上传一段Java代码,可以对消息做任意形式的过滤,甚至可以做Message Body的过滤拆分。

消息堆积能力

理论上Kafka要比RocketMQ的堆积能力更强,不过RocketMQ单机也可以支持亿级的消息堆积能力,我们认为这个堆积能力已经完全可以满足业务需求。

开源社区活跃度

Kafka社区更新较慢

RocketMQ的github社区有250个个人、公司用户登记了联系方式,QQ群超过1000人。

成熟度

Kafka在日志领域比较成熟

RocketMQ在阿里集团内部有大量的应用在使用,每天都产生海量的消息,并且顺利支持了多次天猫双十一海量消息考验,是数据削峰填谷的利器。

批量、重试、异步

1、同步发送、异步发送

2、单笔发送、批量发送

3、发送失败重试

4、消费失败重新消费

5、批量消费

Apache RocketMQ 是一款分布式消息中间件,支持多种类型的消息发送模式。批量发送消息和单笔发送消息是其中两种常见的消息发送方式,它们的主要区别在于性能、资源消耗以及错误处理机制。

单笔发送消息

  • 定义:每次只发送一条消息到消息队列。
  • 优点
    • 实现简单,易于理解和调试。
    • 发送失败时,可以精确地知道哪条消息需要重试或采取其他补救措施。
  • 缺点
    • 相对于批量发送,单条发送消息的网络开销较大,效率较低。
    • 在高并发场景下,可能会导致较高的系统负载。

批量发送消息

  • 定义:将多条消息打包成一个批次进行发送。
  • 优点
    • 减少了网络往返次数,提高了发送效率。
    • 降低了客户端与服务端之间的通信开销,提升了整体吞吐量。
  • 缺点
    • 如果整个批次发送失败,可能需要重新发送整个批次,这可能导致部分消息被重复发送。
    • 错误处理相对复杂,因为需要跟踪整个批次的状态,并在必要时对失败的消息进行重试。

总结

  • 性能:批量发送通常比单笔发送更高效,尤其是在消息体较小的情况下。
  • 资源消耗:批量发送减少了网络IO次数,降低了系统资源消耗。
  • 错误处理:单笔发送更容易实现精确的错误处理逻辑;批量发送则需要更复杂的逻辑来确保消息的正确性。

在实际应用中,可以根据业务需求和性能要求选择合适的消息发送方式。例如,在对实时性和准确性要求非常高的场景下,可以选择单笔发送;而在追求高吞吐量且能接受一定延迟的场景下,则可以采用批量发送。

在 Apache RocketMQ 中,无论是批量发送消息还是单笔发送消息,都可以选择同步发送或者异步发送的方式。这两种发送方式的选择取决于您的具体需求,比如性能要求、可靠性考虑等。下面分别解释同步发送和异步发送的特点:

同步发送

  • 特点
    • 发送方发出消息后会等待接收方(通常是 Broker)确认接收。
    • 只有当消息成功发送并收到确认后,发送方才会继续执行后续操作。
    • 提供了较高的消息发送可靠性。
    • 发送速度相对较慢,因为需要等待确认响应。
  • 应用场景
    • 当消息的可靠性和一致性非常重要时使用。
    • 对于消息发送延迟有一定容忍度的情况。

异步发送

  • 特点
    • 发送方发出消息后立即返回,无需等待接收方的确认。
    • 发送方可以通过回调函数等方式处理发送结果。
    • 提供了更高的消息发送吞吐量。
    • 相对于同步发送,可能需要额外的机制来保证消息的可靠性。
  • 应用场景
    • 当需要尽可能提高消息发送速度和系统吞吐量时使用。
    • 对于消息发送延迟要求不高,但对吞吐量有较高要求的情况。

批量发送与单笔发送

  • 批量发送
    • 可以选择同步发送或异步发送。
    • 如果选择异步发送,那么整个批次的消息将作为一个单元进行处理。
    • 如果批次中有任何消息发送失败,通常需要重试整个批次。
  • 单笔发送
    • 同样可以选择同步发送或异步发送。
    • 如果选择异步发送,每条消息独立处理,失败时只需重试该条消息。

总结

  • 批量发送:无论同步还是异步,都是针对一批消息进行的操作,能够有效减少网络交互次数,提高发送效率。
  • 单笔发送:同步或异步都是针对单条消息进行的操作,更易于实现精确的错误处理。

在实际应用中,您可以根据业务需求选择最适合的发送策略。如果需要保证消息的可靠性和一致性,可以考虑使用同步发送;如果需要追求高吞吐量和低延迟,则可以考虑使用异步发送。

Apache RocketMQ 支持多种发送重试机制,这些机制可以帮助您确保消息能够可靠地发送到目标队列。以下是一些关键的发送重试机制:

消息发送重试机制

1. 同步发送重试
  • 描述:当消息通过同步方式发送失败时,RocketMQ 会自动尝试重新发送消息。
  • 原理:客户端会在发送消息后等待 Broker 的响应。如果在超时时间内没有接收到确认,客户端将重新发送消息。
  • 配置 :可以通过设置客户端的 sendMsgTimeout 参数来控制同步发送的超时时间。
2. 异步发送重试
  • 描述:异步发送时,客户端可以配置重试机制,如设置重试次数和重试间隔。
  • 原理:客户端在异步发送消息时,如果消息发送失败,可以根据预先配置的策略自动重试。
  • 配置 :可以通过设置客户端的 maxReSendTimesretryBackoff 参数来控制最大重试次数和重试间隔。
3. 自动重试
  • 描述:RocketMQ 客户端在遇到网络问题或其他临时故障时,会自动尝试重新发送消息。
  • 配置:可以通过客户端配置文件中的相关参数调整自动重试的行为。
4. 手动重试
  • 描述:在某些情况下,开发者可能需要手动控制消息的重试过程。
  • 原理:可以在回调函数中捕获异常并决定是否重试。
  • 示例:使用异步发送时,可以在回调函数中检查发送状态,并根据状态决定是否重新发送消息。

配置示例

以下是使用 Java 客户端配置同步发送重试的一个示例:

java 复制代码
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.common.message.Message;

public class SendDemo {
    public static void main(String[] args) throws Exception {
        // 创建一个生产者实例
        DefaultMQProducer producer = new DefaultMQProducer("YourProducerGroup");
        
        // 设置 NameServer 地址
        producer.setNamesrvAddr("localhost:9876");
        
        // 启动生产者实例
        producer.start();
        
        // 创建消息对象
        Message msg = new Message("YourTopic", "TagA", "OrderID188", "Hello world".getBytes());
        
        // 发送消息
        SendResult sendResult = producer.send(msg);
        
        // 关闭生产者实例
        producer.shutdown();
    }
}

在上述代码中,可以通过以下方式配置同步发送的超时时间:

java 复制代码
producer.setSendMsgTimeout(3000); // 设置超时时间为3秒

对于异步发送,可以配置重试次数和重试间隔:

java 复制代码
producer.setMaxReSendTimes(5); // 最大重试次数为5次
producer.setRetryBackoff(1000); // 重试间隔为1秒

注意事项

  • 重试次数:需要合理设置重试次数,避免无限重试

发送批量消息

java 复制代码
@Test
public void testBatchProducer() throws Exception {
    // 创建默认的生产者
    DefaultMQProducer producer = new DefaultMQProducer("test-batch-group");
    //连接namesrv,参数是namesrv的ip地址:端口号
    producer.setNamesrvAddr(MqConstant.NAME_SERVER_ADDR);
    // 启动实例
    producer.start();
    List<Message> msgs = Arrays.asList(
        new Message("batchTopicTest", "我是一组消息的A消息".getBytes()),
        new Message("batchTopicTest", "我是一组消息的B消息".getBytes()),
        new Message("batchTopicTest", "我是一组消息的C消息".getBytes())

    );
    SendResult send = producer.send(msgs);
    System.out.println(send);
    // 关闭实例
    producer.shutdown();
}

producer.send(messages); 调用很多次,算不算批量发送

如果您多次调用 producer.send(messages),即使每次调用都发送了一个包含多条消息的列表,这仍然被视为多次发送操作,而不是一次真正的批量发送。真正的批量发送是指一次性发送一个消息列表给 Broker。

示例代码

假设您有多个消息列表,每个列表包含多条消息,您希望分批发送这些列表。下面是分批发送这些列表的示例代码:

java 复制代码
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.exception.RemotingException;

import java.util.ArrayList;
import java.util.List;

public class BatchMessageSender {

    public static void main(String[] args) throws MQClientException, RemotingException, InterruptedException {
        // 创建一个生产者实例
        DefaultMQProducer producer = new DefaultMQProducer("BatchMessageProducerGroup");

        // 设置 NameServer 地址
        producer.setNamesrvAddr("localhost:9876");

        // 启动生产者实例
        producer.start();

        // 创建消息列表
        List<List<Message>> messageLists = createMessageLists();

        // 分批发送消息列表
        for (List<Message> messages : messageLists) {
            sendMessages(producer, messages);
        }

        // 关闭生产者实例
        producer.shutdown();
    }

    private static List<List<Message>> createMessageLists() {
        List<List<Message>> messageLists = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            List<Message> messages = new ArrayList<>();
            for (int j = 0; j < 10; j++) {
                String body = "Batch Message " + i + "-" + j;
                Message msg = new Message("BatchTopic", "BatchTag", body.getBytes());
                messages.add(msg);
            }
            messageLists.add(messages);
        }
        return messageLists;
    }

    private static void sendMessages(DefaultMQProducer producer, List<Message> messages) throws RemotingException, InterruptedException {
        // 发送消息列表
        producer.send(messages);
    }
}

解释

在这个示例中,我们创建了一个 List<List<Message>> 来存储多个消息列表,然后循环遍历这些列表,每次调用 producer.send(messages) 发送一个列表。这意味着虽然每个列表包含多条消息,但实际上是通过多次调用来发送的。

批量发送与多次发送的区别

  • 批量发送 :指一次性发送多个消息,即一次调用 producer.send(messages) 发送一个包含多个消息的列表。
  • 多次发送 :指通过多次调用 producer.send(messages) 来发送多个消息列表,即使每个列表本身包含多条消息。

性能差异

  • 批量发送:减少网络交互次数,提高发送效率。
  • 多次发送:虽然每次发送的是一个列表,但如果调用多次,则会增加网络交互次数,相对于批量发送来说效率较低。

因此,如果您想要真正实现批量发送,您应该尽量减少 producer.send() 的调用次数,将更多的消息打包到一个列表中,然后一次性发送。

RocketMQ---RocketMQ发送同步、异步、单向、延迟、批量、顺序、批量消息、带标签消息

相关推荐
java_logo2 天前
Apache RocketMQ Docker 容器化部署指南
运维·docker·容器·kafka·kubernetes·apache·rocketmq
腾讯云中间件2 天前
腾讯云 RocketMQ 5.x:如何兼容 Remoting 全系列客户端
架构·消息队列·rocketmq
程序员老赵2 天前
Apache RocketMQ Docker 容器化部署指南
docker·rocketmq
jiayong232 天前
微服务架构与 Spring 生态完全指南
kafka·rabbitmq·rocketmq
阿拉斯攀登2 天前
Spring Cloud Alibaba 生态中 RocketMQ 最佳实践
分布式·微服务·rocketmq·springcloud·cloudalibaba
ChrisitineTX3 天前
RocketMQ 消费端卡死?深扒 Rebalance(重平衡)机制在“网络分区”下的致命 Bug
网络·bug·rocketmq
huisheng_qaq4 天前
【RocketMq源码篇-04】rocketmq的普通消息详解(broker存储位置,集群同步情况)
rocketmq·消息中间件·集群同步·普通消息·broker存储位置
小熊officer4 天前
RocketMQ简介
rocketmq
TracyCoder1238 天前
RocketMQ技术原理简单解析:从架构到核心流程
架构·wpf·rocketmq