RocketMQ面试题合集

消费者获取消息是从Master Broker还是Slave Broker获取?

Master Broker宕机,Slave Broker会自动切换为Master Broker吗?

这种Master-Slave模式不是彻底的高可用模式,他没法实现自动把Slave切换为Master。在RocketMQ 4.5之后,这种情况得到了改变,因为RocketMQ支持了一种新的机制,叫做Dledger(基于Raft协议)

老齐

当同一类消息被送入不同队列,且这些消息在处理上并不需要按时序消费时,可以考虑使用并发消费模式。并发消费模式生产者会将消息轮询发送到不同的队列当中,这些队列会和消费者实例建立多个连接(线程)将消息并发送入到不同的消费者。因为消费者处理速度有快慢,所以并不能保证物流数据会按0~9的顺序依次消费。并发消费模式处理效率很高,但无法保证有序性。

有序消费

有序消息是指生产者在产生数据的时候,根据Hash规则指定让消息放入哪个队列,在消费者消费时会保证不同消费者针对每一个队列只有唯一的连接(线程)用于消费指定队列。

有序消费模式可以保证消息按队列FIFO顺序依次被消费,但因此失去并发性能,有序消费模式只有在业务要求必须按顺序消费的场景下才允许使用。

RocketMQ如何实现有序消息

要实现RocketMQ有序消息需要两点调整:

生产者端要求按id等唯一标识分配消息队列消费者端采用专用的监听器保证对队列的单线程应用下面咱们来看一下代码:

生产者端

com.itlaogi.rocketmg.seqmsg.SequenceMessageProvider

核心代码是在向Broker发送消息时附加MessageQueueSelector对象,在实现select方法时指定存放到哪个队列中。

java 复制代码
public class RocketMQProducerExample {
    public static void main(String[] args) throws MQClientException {
        // 创建生产者实例
        DefaultMQProducer producer = new DefaultMQProducer("producer_group_name");
        // 设置NameServer地址,这里需要替换为实际的地址
        producer.setNamesrvAddr("localhost:9876");
        producer.start();

        try {
            for (Integer orderId = 1; orderId <= 10; orderId++) {
                for (int i = 0; i < 3; i++) {
                    String data = "";
                    switch (i % 3) {
                        case 0:
                            data = orderId + "号创建订单";
                            break;
                        case 1:
                            data = orderId + "号订单减库存";
                            break;
                        case 2:
                            data = orderId + "号订单加积分";
                            break;
                    }
                    // 创建消息对象 topic="order", tags="order", key=orderId
                    Message message = new Message("order", "order", orderId.toString(), data.getBytes("UTF-8"));
                    // 发送消息,实现MessageQueueSelector接口
                    SendResult result = producer.send(message, new MessageQueueSelector() {
                        // select方法决定向broker哪一个队列发送消息
                        @Override
                        public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
                            int currentOrderId = Integer.parseInt(msg.getKeys());
                            int index = currentOrderId % mqs.size();
                            MessageQueue messageQueue = mqs.get(index);
                            Logger.getLogger(RocketMQProducerExample.class.getName()).log(Level.INFO,
                                    "id:{0},data:{1},queue:{2}", new Object[]{currentOrderId, new String(msg.getBody()), messageQueue});
                            return messageQueue;
                        }
                    }, null);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            producer.shutdown();
        }
    }
}
java 复制代码
public class RocketMQConsumerExample {
    public static void main(String[] args) throws Exception {
        // 创建消费者实例并指定消费者组名
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer_group_name");
        // 设置NameServer地址,需替换为实际地址
        consumer.setNamesrvAddr("localhost:9876");
        // 设置从哪里开始消费消息,比如从上次消费的位置开始
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);

        // 订阅主题和标签
        consumer.subscribe("order", "*");

        // 注册顺序消费的监听器,用于实现有序队列
        consumer.registerMessageListener(new MessageListenerOrderly() {
            @Override
            public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
                try {
                    for (MessageExt msg : msgs) {
                        // 在这里处理具体的消息,按照顺序来处理每个消息
                        System.out.println(new String(msg.getBody()));
                    }
                    return ConsumeOrderlyStatus.SUCCESS;
                } catch (Exception e) {
                    e.printStackTrace();
                    return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;
                }
            }
        });

        consumer.start();
        System.out.println("Consumer started.");
    }
}

运行结果

如何实现消息全局顺序消费?

只需要在生产者固定将所有消息发送到0号队列即可保证全局有序,这也意味着全局采用单线程消费,执行效率极差

java 复制代码
@Override
public MessageQueue select(List<MessageQueue> mgs, Message msg, object arg){
	MessageQueue messageQueue = mgs.get(0);
	return messageQueue;
}

有序消费有什么使用限制吗?

有序消费模式只支持集群模式(CLUSTERING),不支持广播模式(BROADCASTING),:采用广播模式会无法接收到数据

java 复制代码
//设置为集群模式
consumer.setMessageModel(MessageMode1.CLUSTERING)://支持有序消息,默认模式。
consumer.setMessageModel(MessageMode1.BROADCASTING)://不支持有序消息
相关推荐
神明不懂浪漫1 小时前
【第五章】Java中的继承与多态
java·开发语言
AI多Agent协作实战派3 小时前
AI多Agent协作系统实战(十七):凌晨4点,我的AI系统在“假装工作“——3个bug同时爆炸的5小时
java·前端·bug
gaolei_eit3 小时前
Java+Ai+vue
java·spring·maven
qq_2518364573 小时前
基于java Web 动漫视频网站毕业论文
java·开发语言·前端
LayZhangStrive3 小时前
JUC相关的函数、注解、变量杂记
java·面试·多线程·juc
未秃头的程序猿3 小时前
给公司做了个AI客服Agent,用的Spring AI 1.0,3天上线领导拍板了
java·后端·ai编程
曹牧3 小时前
Eclipse 批量文本替换
java·ide·eclipse
程序员清风3 小时前
OpenAI官方发布最新提示词技巧!
java·后端·面试
一只枫林3 小时前
MySQL内、外连接知识点汇总
java·前端·数据库
梅头脑4 小时前
写了3年CRUD,volatile和synchronized的区别还是答不清——直到我画出了这张三性两锁边界图
java