RocketMQ——生产者和消费者

本文详细介绍了RocketMQ消息队列的生产者与消费者工作原理,包括消息的生产和消费流程,以及DefaultMQPushConsumer和DefaultMQPullConsumer的使用方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

RocketMQ——生产者和消费者


RocketMQ简介

RocketMQ共有四个角色,分别是Producer、Consumer、Broker、NameServer,他们分别对应的作用如下:

  • Producer:消息生产者,负责消息的生产发送
  • Consumer:消息消费者,负责消息接收和使用
  • Broker:负责消息的传输和暂存
  • NameServer:负责协调整个消息队列,维护配置信息和状态信息

RocketMQ架构

RocketMQ生产者

消息生产者向消息队列写入数据,在不同的业务场景中可能会有不同的需求,下面介绍消息生产者

public class producer {
    public static void main(String[] args) throws Exception {
        DefaultMQProducer producer = new DefaultMQProducer("group2");//设置group名
        producer.setNamesrvAddr("192.168.108.128:9876");//设置Nameserver地址
        producer.setVipChannelEnabled(false);
        producer.start();//开启生产者
        Message msg = new Message("TopicTest2", 
        							"TagA",
        		("MQTest").getBytes(RemotingHelper.DEFAULT_CHARSET));
        msg.setDelayTimeLevel(3);//设置延迟发送消息
        SendResult result = producer.send(msg);//发送消息
        System.out.println(result);
        producer.shutdown();
    }
}

在这里使用了默认消息生产类DefaultMQProducer,并且在创建好消息后通过调用setDelayTimeLevel(int level)方法设置延迟时间。

RocketMQ消费者

消费者可以分为两类

  • DefaultMQPushConsumer:系统控制读取操作
  • DefaultMQPullConsumer:使用者自主控制读取操作
    但是这两个的本质都是通过Consumer轮询Broker然后再拉取消息,即pull模式

DefaultMQPushConsumer

DefaultMQPushConsumer通过用户设置好的参数等,系统会自动根据用户设置参数进行消息处理,自动保存Offset、做负载均衡等。

public class consumer {
    public static void main(String[] args) throws Exception {
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group2");//设置group名
        consumer.setNamesrvAddr("192.168.108.128:9876");//设置Nameserver地址
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);//设置读取位置,从最小的offset开始
        consumer.subscribe("TopicTest2", "*");//生命接受的Topic名以及过滤tags
        consumer.setMessageModel(MessageModel.BROADCASTING);
        consumer.registerMessageListener((MessageListenerConcurrently) (list, consumeConcurrentlyContext) -> {
            System.out.println(Thread.currentThread().getName() + " Receive Message: " + list);
            return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
        });
        System.out.println("consumer start...");
        consumer.start();
    }
}

在程序中创建好了consumer对象之后,需要对Group、MessageModel、NameServer、Topic进行设置:

  • Group主要将多个Consumer组织到一起,提高并发能力
  • RocketMQ支持两种MessageModel消息模式:Clustering和BroadCasting。Clustering模式下,在同一个group中的消费者只会消费消息的一部分,即group内所有消费者的消费消息的总和是Topic内容整体。Broadcasting即广播模式,消息会同时发布到group内的每一个consumer。
  • NameServer需要配置ip和端口号,多个NameServer用分号(;)隔开
  • Topic是消息队列的标识,表明消费者需要消费哪些消息,并且可以通过消息的tags来进行过滤,如:consumer.subscribe(“TopicTest2”, “tag1||tag2||tag3”);此处使用 * 表示消费topic中的所有消息

在前面说过,DefaultMQPushConsumer虽然叫Push,其原理依然是Pull模式,怎么实现用Pull达到Push效果的呢?

首先我们来看看Push和Pull两种方式的优缺点:

  • Push方式是在服务端接收到消息后立刻将消息推送到客户端去,这样的方式实时性高,但是如果在消息队列中使用这种方式首先是加大服务端的压力,一收到消息就立刻推送到客户端,会降低服务端的部分性能。其次,如果在推送过程中如果客户端发生阻塞,导致客户端不能及时处理消息,消息会在客户端堆积,从而造成各种潜在问题。
    在这里插入图片描述
    在这里插入图片描述
  • Pull方式是客户端向服务端发起请求拉取数据,这种方式的优点是由客户端控制消息接收,可以使客户端处理完消息后再去服务端拉取下一条消息,防止消息在客户端堆积。这样的方式的缺点是需要对客户端拉取消息时间间隔进行把握,间隔过长实时性较低,间隔过短同样会造成消息堆积。
    在这里插入图片描述
    DefaultMQPushConsumer的主要功能都在类DefaultMQPushConsumerImpl中,这个类实现MQConsumerInner接口,该类中有个很长的方法叫public void pullMessage(final PullRequest pullRequest) ,该方法是处理消息的主要方法,并且在DefaultMQPushConsumer中又有非常多的Pull方法。

那么RocketMQ是怎样做到用Pull的方式实现Push的效果呢?其实PushConsumer是通过长轮询的方式来达到Push的效果。Broker将Consumer发送过来的拉取请求保持住,然后Consumer循环查看队列中是否有消息,如果没有消息则等待5秒,然后再去查看队列是否有消息,如果有消息则拉取,如果没有消息,就继续等待。当超过设置的BrokerSuspendMaxTimeMillis时(默认时15s,在默认情况下Consumer会等待3次),返回空结果。此时消息的控制权在Consumer上,大大减少了Consumer客户端的消息处理压力,但是在保持链接时会暂用一定的资源。

DefaultMQPullConsumer

下面代码是org.apache.rocketmq.example.simple中的示例代码,该代码演示了从消息队列中使用Pull模式拉取消息的步骤

public class PullConsumer {
    private static final Map<MessageQueue, Long> OFFSE_TABLE = new HashMap<MessageQueue, Long>();
 
    public static void main(String[] args) throws MQClientException {
        DefaultMQPullConsumer consumer = new DefaultMQPullConsumer("please_rename_unique_group_name_5");
         consumer.start();
 		//根据Topic名获取队列
        Set<MessageQueue> mqs = consumer.fetchSubscribeMessageQueues("TopicTest1");
        for (MessageQueue mq : mqs) {
            //获取消息的offset
            long Offset = Consumer.fetchConsumeOffset(mq, true);
            System.out.printf("Consume from the queue: %s%n", mq);
            SINGLE_MQ:
            while (true) {
                try {
                    PullResult pullResult =
                        consumer.pullBlockIfNotFound(mq, null, getMessageQueueOffset(mq), 32);
                    System.out.printf("%s%n", pullResult);
                    putMessageQueueOffset(mq, pullResult.getNextBeginOffset());
                    //根据不同的效应进行处理
                    switch (pullResult.getPullStatus()) {
                        case FOUND:
                            break;
                        case NO_MATCHED_MSG:
                            break;
                        case NO_NEW_MSG:
                            break SINGLE_MQ;
                        case OFFSET_ILLEGAL:
                            break;
                        default:
                            break;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
 
        consumer.shutdown();
    }
 
    private static long getMessageQueueOffset(MessageQueue mq) {
        Long offset = OFFSE_TABLE.get(mq);
        if (offset != null)
            return offset;
 
        return 0;
    }
 
    private static void putMessageQueueOffset(MessageQueue mq, long offset) {
        OFFSE_TABLE.put(mq, offset);
    }
 }

使用Pull模式拉取消息额外做了三件事

  1. 获取MessageQueue并遍历。因为一个Topic可能包含多个MessageQueue,因此需要对MessageQueue进行遍历才能获取;
  2. 维护Offsetstore。从MessageQueue拉去消息时,需要传入Offset值,随着MessageQueue的增长,需要用户存储Offset;
  3. 根据不同响应状态进行处理。拉去请求会返回FOUND(获取消息)、NO_MATCHED_MSG(没有合适消息)、NO_NEW_MSG(没有新消息)、OFFSET_ILLEGAL(不合法的Offset)
### RocketMQ 生产者组消费者的区别 #### 消费者 (Consumer Group) 消费者是指多个消费者实例可以归属于同一逻辑,这些消费者共享消费进度并协同处理来自相同主题(Topic)的消息。每个`ConsumerGroup`内的成员能够共同负责订阅的主题下的所有消息分区,确保每条消息仅由其中一个成员处理一次[^1]。 对于集群模式而言,消费者的消费状态会被持久化存储于Broker端;当有新的节点加入或现有节点离开时,会触发负载均衡(rebalance),重新分配各子集间的责任范围以维持整体系统的稳定性效率[^3]。 #### 生产者角色 相比之下,生产者的职责在于向特定的目标Topic发布新产生的数据记录。值得注意的是,在官方文档及相关实践中并未提及“生产者组”的概念——即不存在类似于消费者那样的显式合形式来描述一具有相似配置或目标的Producer实体。因此,“生产者组”更多指的是开发者为了便于管理维护而自行设定的一种逻辑上的分类方式,并不涉及底层实现层面的功能差异[^2]。 综上所述: - **功能定位不同**:前者用于接收处理已发布的事件流,后者则是用来推送新鲜的数据给下游应用; - **工作原理各异**:Consumers通过协调机制保证单次传递语义以及支持广播等多种读取策略;Producers则专注于高效可靠地注入新纪录至指定位置; - **管理结构相异**:Consumers可成Groups以便更好地协作完成任务,而Producers通常独立运作无需形成固定的集体单位。 ```java // 创建一个简单的RocketMQ生产者示例 public class ProducerExample { public static void main(String[] args) throws Exception { DefaultMQProducer producer = new DefaultMQProducer("example_producer_group"); producer.start(); Message msg = new Message("TopicTest", "TagA", ("Hello RocketMQ").getBytes(RemotingHelper.DEFAULT_CHARSET)); SendResult sendResult = producer.send(msg); System.out.printf("%s%n", sendResult); producer.shutdown(); } } ``` ```java // 创建一个简单的RocketMQ消费者示例 public class ConsumerExample { public static void main(String[] args) throws MQClientException { DefaultLitePullConsumer consumer = new DefaultLitePullConsumer("example_consumer_group"); consumer.subscribe("TopicTest", "*"); consumer.start(); try { List<MessageExt> msgs = consumer.poll(); for (MessageExt msg : msgs) { // Process message here... System.out.println(new String(msg.getBody())); } consumer.updateConsumeOffset(msgs.get(0), ConsumeStatus.CONSUME_SUCCESS.getCode()); } catch (Exception e) { e.printStackTrace(); } finally { consumer.shutdown(); } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值