Rocketmq消息批量发送&消息批量消费

前言:批量发送和消费消息在一定程度上可以提高吞吐量,减少带宽,那么Rocketmq 中的消息怎么进行批量的发送和批量的消费呢;

1 消息的批量发送:

1.1 批量发送的优点以及实现:
批量发送消息可以提高 RocketMQ 的生产者性能和吞吐量。由于批量发送消息可以减少网络 I/O 操作和降低消息发送延迟,因此它在以下情况下特别有用:

  • 发送大量小型消息时
  • 需要降低消息发送延迟时
  • 需要提高生产者性能时

但是,批量发送消息也存在一些注意事项,需要注意以下几点:

  • 消息列表的大小不能超过 broker 设置的最大消息大小。
  • 消息列表的大小不能超过生产者设置的 maxMessageSize 参数,此参数默认为 4MB。
  • 批量发送消息不支持消息事务。
  • 如果的代码在发送消息列表时发生异常,则可能会发生部分消息发送成功,部分消息发送失败的情况。如果要确保所有消息都已成功发送,则需要增加错误处理逻辑和消息重试机制。

批量发送消息是一种提高 RocketMQ 生产者性能和吞吐量的好方法,但需要注意消息列表大小和错误处理机制,以确保生产者的可靠性和稳定性。

public class SimpleBatchProducer {

    public static void main(String[] args) throws Exception {
        DefaultMQProducer producer = new DefaultMQProducer("BatchProducerGroupName");
        producer.start();

        //If you just send messages of no more than 1MiB at a time, it is easy to use batch
        //Messages of the same batch should have: same topic, same waitStoreMsgOK and no schedule support
        String topic = "BatchTest";
        List<Message> messages = new ArrayList<>();
        messages.add(new Message(topic, "Tag", "OrderID001", "Hello world 0".getBytes()));
        messages.add(new Message(topic, "Tag", "OrderID002", "Hello world 1".getBytes()));
        messages.add(new Message(topic, "Tag", "OrderID003", "Hello world 2".getBytes()));

        producer.send(messages);
    }
}

1.2 批量发送消息为什么要限制maxMessageSize :
消息列表的大小不能超过生产者设置的 maxMessageSize 参数,主要是为了避免消息发送延迟和消息过大导致 broker 出现性能问题。如果尝试发送大于 maxMessageSize 的消息,RocketMQ 会抛出 MessageTooLargeException 异常,并且消息不会被发送到 broker。

如果开发者在开发时遇到了消息列表大小超过 maxMessageSize 的情况,可以考虑以下几种处理方式:

  • 提升 maxMessageSize 参数的大小,这样可以容纳更大的消息列表。但是,需要注意在提升参数大小时,要考虑到 RocketMQ broker 的性能和网络带宽等因素。

  • 考虑将消息列表进行拆分,然后分批发送。这样可以避免一次发送过多的消息。

  • 计算消息的大小并进行压缩。可以使用一些压缩算法,如 LZ4、Snappy 等,对消息进行压缩,以减小消息的大小。

  • 对超过 maxMessageSize 的消息进行过滤或其他处理。可以通过业务逻辑对消息进行分组或分类,对超过 maxMessageSize 的消息进行过滤或其他处理,以避免发送超出限制的消息。

开发者在开发时需要注意消息列表的大小限制,避免出现超出限制的情况。

2 消息的批量消费:

2.1 批量消费的优点:
批量消费消息可以提高 RocketMQ 的消费者性能和吞吐量,因为批量消费消息可以减少网络 I/O 操作和降低消息消费延迟。批量消费消息在以下情况下特别有用:

  • 消费大量小型消息时
  • 需要降低消息消费延迟时
  • 需要提高消费者性能时
    但是,批量消费消息也存在一些注意事项,需要注意以下几点:
  • 消息列表的大小不能超过 broker 设置的最大消息大小。
  • 消息列表的顺序可能与单条消息的顺序不同。如果要保持消息顺序,需要对消息进行分组。
  • 批量消费消息会增大消息重试的难度。因此,如果的消息消费逻辑具有事务性质,建议使用单条消息消费方式。

批量消费消息是一种提高 RocketMQ 消费者性能和吞吐量的好方法,但需要注意消息列表大小、消息顺序和事务性质等问题,以确保消费者的可靠性和稳定性。

2.2 推、拉和长轮询:MQ的消费模式可以大致分为两种,一种是推Push,一种是拉Pull

  • Push是服务端主动推送消息给客户端,优点是及时性较好,但如果客户端没有做好流控,一旦服务端推送大量消息到客户端时,就会导致客户端消息堆积甚至崩溃。

  • Pull是客户端需要主动到服务端取数据,优点是客户端可以依据自己的消费能力进行消费,但拉取的频率也需要用户自己控制,拉取频繁容易造成服务端和客户端的压力,拉取间隔长又容易造成消费不及时。

2.3 对pull(拉模式)的批量消费:
DefaultLitePullConsumer是RocketMQ中的拉模式消息消费者,其工作流程如下:

  • 初始化:创建DefaultLitePullConsumer实例,并设置相关参数,如消费者组、NameServer地址等。
    订阅主题:调用DefaultLitePullConsumer的subscribe()方法,订阅感兴趣的主题(Topic)及消息过滤标签(Tag)。
  • 消费者启动:调用DefaultLitePullConsumer的start()方法,启动消费者。启动后,消费者将与NameServer建立连接, 获取感兴趣的主题的路由信息,找到对应的Broker,之后建立与Broker的连接。然后,消费者向Broker报告当前的消费进度。
  • 轮询拉取消息:调用DefaultLitePullConsumer的poll()方法,轮询地从Broker拉取消息。拉取消息后,本地需要有一个处理消息的逻辑。
  • 消息处理:处理拉取到的消息,如处理业务逻辑、持久化等。消息处理需要考虑潜在的并发和处理速度问题。
  • 提交消费进度:当消息处理完成后,调用DefaultLitePullConsumer的commitSync()或commitAsync()方法,将消费进度提交给Broker,以便下次拉取时能继续从上一次完成处理的消息开始拉取。
  • 异常处理:如果拉取消息过程中出现异常,可以考虑重试。简单的重试可以通过DefaultLitePullConsumer提供的seek()方法回滚消费进度,但需要注意处理消息时的幂等性问题。
  • 关闭消费者:当需要关闭消费者时,调用DefaultLitePullConsumer的shutdown()方法。关闭过程中会断开与Broker和NameServer的连接,并释放相关资源。

在使用DefaultLitePullConsumer时,需要注意控制拉取消息的速率(比如使用定时任务调用poll()方法)及消息处理的并发能力。根据实际业务去实现适当的处理策略,保证在消费者速率和处理能力之间达到一个平衡。

demo:

 public static void main(String[] args) throws Exception {
        DefaultLitePullConsumer litePullConsumer = new DefaultLitePullConsumer("lite_pull_consumer_test");
        litePullConsumer.setNamesrvAddr("localhost:9876");
        litePullConsumer.subscribe("test_topic", "*");
        litePullConsumer.setPullBatchSize(20);
        litePullConsumer.start();
        try {
            while (running) {
                List<MessageExt> messageExts = litePullConsumer.poll();
                System.out.printf("%s%n", messageExts);
            }
        } finally {
            litePullConsumer.shutdown();
        }
    }

首先还是初始化DefaultLitePullConsumer并设置ConsumerGroupName,调用subscribe方法订阅topic并启动。与Push Consumer不同的是,LitePullConsumer拉取消息调用的是轮询poll接口,如果能拉取到消息则返回对应的消息列表,否则返回null。通过setPullBatchSize可以设置每一次拉取的最大消息数量,此外如果不额外设置,LitePullConsumer默认是自动提交位点。在subscribe模式下,同一个消费组下的多个LitePullConsumer会负载均衡消费。

2.3 对push(推模式)的批量消费:DefaultMQPushConsumer的工作流程如下:

  • 客户端初始化:创建DefaultMQPushConsumer实例,并设置相关配置参数,如消费者组、NameServer地址等。
  • 消费者订阅:消费者通过调用DefaultMQPushConsumer的subscribe()方法订阅感兴趣的主题(Topic)以及消息的过滤标签(Tag)。订阅的Topic信息最终将传递给Broker。
  • 注册MessageListener:实现一个自定义的MessageListener作为消费者处理消息的监听器。这个监听器会在消费者收到新消息时被调用。
  • 消费者启动:消费者调用DefaultMQPushConsumer的start()方法启动。启动时,消费者将与NameServer建立连接,获取感兴趣的主题的路由信息,找到对应的Broker,之建立连接。随后,消费者会定期向Broker发起心跳请求以维持连接。
  • Broker消息推送:当Broker上有新的生产者消息,它会分配给消费者组内的消费者进行消费。Broker会将消息推送给消费者,消费者在收到新消息后会执行相应的MessageListener逻辑处理消息。
  • 消息消费确认:消费者成功处理消息后,将发送ACK确认信息回Broker。Broker收到ACK确认后,会将消息标记为消费成功并标记为已处理。如果消费失败,可以选择重试,根据配置设定的策略最终可能会进入死信队列。
  • 逐步消费:消费者会持续消费新消息,直到关闭消费者。
  • 消费者关闭:调用DefaultMQPushConsumer的shutdown()方法关闭消费者。关闭时,消费者将断开与Broker的连接,并释放相关资源。

请注意,在使用DefaultMQPushConsumer时,消费者的并发能力由MessageListener实现来保证。因此,在设计MessageListener实现时需要考虑到高并发处理能力。虽然RocketMQ客户端提供了设置消费线程池的配置选项,但还是推荐根据实际需求来实现合适的并发方案。‘

2.3.1 使用DefaultMQPushConsumer :
批量消费消息需要在消费者端设置 ConsumeMessageBatchMaxSize 参数,以指定每次批量消费的消息数量。

public static void main(String[] args) throws InterruptedException, MQClientException {
        // step1: 创建一个DefaultMQPushConsumer实例
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("BatchPushConsumer");
        consumer.setNamesrvAddr("localhost:9876");
        consumer.setConsumeMessageBatchMaxSize(10); // 设置每次批量消费的消息数量

        // step2: 为消费者订阅一个Topic
        consumer.subscribe("test_topic", "*");

        // step3: 注册一个MessageListenerConcurrently,并实现批量消费逻辑
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(
                    List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msgs);
                // 处理批量消息
                for (MessageExt msg : msgs) {
                    // 在此处理每条消息,例如保存到数据库等
                    System.out.println("msg : " + new String(msg.getBody()));
                }
                // 返回消费状态
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });

        // step4: 启动消费者
        consumer.start();
        System.out.println("Consumer started!");

        // 让主线程等待以保持进程不退出
        TimeUnit.SECONDS.sleep(60);
    }

在上述示例中,每次批量消费的消息数量被设置为10。每次推送过来的消息数量可能并不总是达到这个数字,但是它不会超过这个数量。如果希望调整批量大小,可以通过consumer.setConsumeMessageBatchMaxSize();修改这个值。

2.3.2 :springboot 通过@RocketMQMessageListener 完成消息消费:
:默认的 RocketMQ Spring Boot Starter 并不支持直接设置批量消费模式,消息是一个个处理的。对于RocketMQ Listener可以管理多个线程同时处理消息。可以有多个消息同时处理。通过将顺序消息设置为并发模式并设置消费线程数。

import com.example.demo.MessageProcessor;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
@RocketMQMessageListener(topic = "my-topic", consumerGroup = "myConsumerGroup", consumeMode = ConsumeMode.CONCURRENTLY, consumeThreadMax = 3)
public class MyConsumer implements RocketMQListener<MessageExt> {
    @Autowired
    private MessageProcessor messageProcessor;

    @Override
    public void onMessage(MessageExt messageExt) {
        messageProcessor.process(messageExt);
    }
}

在这个例子中,consumeMode = ConsumeMode.CONCURRENTLY表示消费者将启用并发模式,而consumeThreadMax = 3将同时处理三个消息。注意,这不是真正意义上的批量消费,而是通过多线程来同时处理多个消息。要实现批量消费,需要进一步处理MessageExt消息,这取决于的实际需求。例如,可以缓冲消息,等待足够多的消息可用后一次性处理它们。

在设置 consumeThreadMax 参数时,请确保它不要过大,以避免系统资源过载。同时,优化MessageProcessor中的相关逻辑,尽量减少处理每个消息所需的时间。通过这种机制,虽然无法实现真正意义上的批量消费,但仍然可以帮助提高消息处理的效率。

3 参考:
3.1 RocketMq 消费者;
3.2 RocketMq 客户端配置参数;

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个使用Spring Boot和RocketMQ实现批量消费的样例代码: 1. 添加RocketMQ的依赖 在pom.xml文件中添加以下依赖: ``` <dependency> <groupId>org.apache.rocketmq</groupId> <artifactId>rocketmq-spring-boot-starter</artifactId> <version>${rocketmq-spring-boot.version}</version> </dependency> ``` 2. 配置RocketMQ的生产者和消费者 在application.properties文件中配置RocketMQ的生产者和消费者: ``` # RocketMQ Producer rocketmq.producer.group=producer_group rocketmq.producer.namesrv-addr=127.0.0.1:9876 # RocketMQ Consumer rocketmq.consumer.group=consumer_group rocketmq.consumer.namesrv-addr=127.0.0.1:9876 rocketmq.consumer.topics=topic_batch rocketmq.consumer.consume-thread-max=20 rocketmq.consumer.consume-message-batch-max-size=32 ``` 3. 实现RocketMQ消息监听器 在Spring Boot中,可以使用@RocketMQMessageListener注解来实现RocketMQ消息监听器。例如: ``` @Component @RocketMQMessageListener(topic = "${rocketmq.consumer.topics}", consumerGroup = "${rocketmq.consumer.group}", messageModel = MessageModel.CLUSTERING) public class BatchMessageListener implements RocketMQListener<List<String>> { @Override public void onMessage(List<String> messages) { // 批量消息处理逻辑 for (String message : messages) { System.out.println(message); } } } ``` 在上面的代码中,我们将RocketMQ消息监听器实现为一个Spring Bean,并使用@RocketMQMessageListener注解来指定监听的主题、消费者组和消息模式。在onMessage方法中,我们对批量消息进行处理。 4. 发送批量消息 在需要发送批量消息的地方,可以使用RocketMQ批量消息发送器(DefaultMQProducer)来发送消息。例如: ``` @Autowired private DefaultMQProducer producer; public void sendBatchMessage() throws Exception { List<Message> messages = new ArrayList<>(); for (int i = 0; i < 32; i++) { Message message = new Message("topic_batch", "tag", ("Hello RocketMQ " + i).getBytes()); messages.add(message); } SendResult result = producer.send(messages); System.out.println(result); } ``` 在上面的代码中,我们首先创建了一个包含32个消息消息列表,然后使用DefaultMQProducer发送器来发送批量消息发送结果会返回一个SendResult对象。 通过以上步骤,我们就可以实现Spring Boot和RocketMQ批量消息处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值