RocketMQ入坑指南(四):消费者的使用方式

前言

在这里先祝福大家新年快乐!因为假期原因,距离上一篇的生产者教程已经过了很久,今天是上班的第一天,我们继续分享RocketMQ的第四篇教程,主要介绍消费者相关的知识。

一、消费者概述

消费者组:一个逻辑概念,在使用消费者时需要指定一个组名。一个消费者组可以订阅多个Topic。

消费者实例:一个消费者组程序部署了多个进程,每个进程都可以称为一个消费者实例。

订阅关系:一个消费者组订阅一个Topic的某一个 Tag,这种记录被称为订阅关系。

实际使用中,要保证消费订阅关系一致,一个消费者组中的订阅Topic和Tag必须一致。

二、集群消费模式和广播消费模式

1.集群消费模式

在同一个消费组中,消费者是负载均衡的消费Topic中的消息。

设置消费模式为集群模式

consumer.setMessageModel(MessageModel.CLUSTERING);

生产者:

每一秒生产一条数据


        //创建DefaultMQProducer消息生产者对象
        DefaultMQProducer producer = new DefaultMQProducer("TestProducerGroup");
        //设置NameServer
        producer.setNamesrvAddr("192.168.2.5:9876");
        //设置NameServer节点地址,多个节点间用分号分割
        try {
            //与NameServer建立长连接
            producer.start();
            //发送一百条数据
            for (int i = 1; i <= 10; i++) {
                //1S中发送一次
                Thread.sleep(1000);
                JSONObject json = new JSONObject();
                json.put("orderId", i);
                json.put("desc", "这是第" + i + "个订单");
                //数据正文
                String data = json.toJSONString();
                Message message = new Message("TopicOrder", "", data.getBytes());
                //发送消息,获取发送结果
                SendResult result = producer.send(message);
                //将发送结果对象打印在控制台
                System.out.println("消息已发送:MsgId:" + result.getMsgId() + ",发送状态:"
                        + result.getSendStatus());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                producer.shutdown();
            } catch (Exception e) {
            }
        }

启动两个消费者


        //创建消费者对象
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("TestPushConsumerGroup");
        try {
            //设置NameServer节点
            consumer.setNamesrvAddr("192.168.2.5:9876");
            //订阅主题
            consumer.subscribe("TopicOrder", "*");
            //设置消费模式为集群模式
            consumer.setMessageModel(MessageModel.CLUSTERING);
            //创建监听,当有新的消息监听程序会及时捕捉并加以处理。
            consumer.registerMessageListener(new MessageListenerConcurrently() {
                public ConsumeConcurrentlyStatus consumeMessage(
                        List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                    //批量数据处理
                    for (MessageExt msg : msgs) {
                        System.out.println("消费者获取数据:" + msg.getMsgId() + "==>" + new
                                String(msg.getBody()));
                    }
                    //返回数据已接收标识
                    return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                }
            });
            //启动消费者,与Broker建立长连接,开始监听。
            consumer.start();
        } catch (Exception e) {
            e.printStackTrace();
        }

启动之后我们可以看到两个消费者各自消费了5条数据

2.广播消费模式

消息广播分发,所有消费者组中的消费者消费全量的消息。

唯一不同点就是setMessageModel设置的消费模式不同

//设置消费模式为集群模式
consumer.setMessageModel(MessageModel.BROADCASTING);

设置完后依然启动两个消费者

我们看到,两个消费者获得了相同的数据

三、消费者如何实现消息过滤

1.tag方式

tag方式经过前面的教程,大家也应该了解到如何使用,只需消费者订阅的时候,设置需要消费的tag标记即可

写法:

  • * :消费所有消息
  • Tag : 只消费制定的Tag
  • Tag1 || Tag2 || Tag3 : 只要满足一个Tag值就被消费

2.sql方式(不推荐)

当生产者发送消息的时候,可以自定义消息的额外属性,消费者可以通过类似sql的语法实现消息过滤,相比较Tag方式,会更加灵活

先在broker.conf增加配置

#开启自定义属性SQL过滤
enablePropertyFilter=true

java代码如下:

生产者:测试取余为1的设置type为food

//创建DefaultMQProducer消息生产者对象
        DefaultMQProducer producer = new DefaultMQProducer("TestProducerGroup");
        //设置NameServer
        producer.setNamesrvAddr("192.168.2.5:9876");
        //设置NameServer节点地址,多个节点间用分号分割
        try {
            //与NameServer建立长连接
            producer.start();
            //发送一百条数据
            for (int i = 1; i <= 10; i++) {
                //1S中发送一次
                Thread.sleep(1000);
                JSONObject json = new JSONObject();
                json.put("orderId", i);
                json.put("desc", "这是第" + i + "个订单");
                //数据正文
                String data = json.toJSONString();
                Message message = new Message("TopicOrder", "", data.getBytes());
                if(i % 3 == 1){
                    message.putUserProperty("type","food");
                }else{
                    message.putUserProperty("type","drink");
                }
                //发送消息,获取发送结果
                SendResult result = producer.send(message);
                //将发送结果对象打印在控制台
                System.out.println("消息已发送:MsgId:" + result.getMsgId() + ",发送状态:"
                        + result.getSendStatus());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                producer.shutdown();
            } catch (Exception e) {
            }
        }

消费者:消费type为food的消息

 //创建消费者对象
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("TestPushConsumerGroup");
        try {
            //设置NameServer节点
            consumer.setNamesrvAddr("192.168.2.5:9876");
            //订阅主题,消费type为food的消息
            consumer.subscribe("TopicOrder", MessageSelector.bySql("type='food'"));
            //设置消费模式为集群模式
            consumer.setMessageModel(MessageModel.CLUSTERING);
            //创建监听,当有新的消息监听程序会及时捕捉并加以处理。
            consumer.registerMessageListener(new MessageListenerConcurrently() {
                public ConsumeConcurrentlyStatus consumeMessage(
                        List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                    //批量数据处理
                    for (MessageExt msg : msgs) {
                        System.out.println("消费者获取数据:" + msg.getMsgId() + "==>" + new
                                String(msg.getBody()));
                    }
                    //返回数据已接收标识
                    return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                }
            });
            //启动消费者,与Broker建立长连接,开始监听。
            consumer.start();
        } catch (Exception e) {
            e.printStackTrace();
        }

结果:

从结果中我们可以看出,该消费者只消费了type为food的消息

四、有序消息的实现

在第三章中,我们已经了解到了生产者如何发送有序消息,实现的方式就是通过数据的hash值,根据hash值判断,指定让消息通过哪一个队列传输,忘记的小伙伴可以回头复习一下,下面将介绍消费者端如何进行有序消费

生产者:

我们生成了20个订单,每个订单需要发送三条消息,顺序是创建订单、生成支付订单、创建物流信息

通过有序消息的发送,我们已经确保相同订单号的数据是通过相同队列来发送消息

 //创建DefaultMQProducer消息生产者对象
        DefaultMQProducer producer = new DefaultMQProducer("TestProducerGroup");
        //设置NameServer
        producer.setNamesrvAddr("192.168.2.5:9876");
        //设置NameServer节点地址,多个节点间用分号分割
        try {
            //与NameServer建立长连接
            producer.start();
            //设置个集合,集合信息中是要发送订单消息、支付消息、物流消息。要保证相同订单号的在同一个队列
            List<JSONObject> orderInfos = new ArrayList<>();
            //一共10个订单,每一个订单要发送3条消息
            for (int i = 1; i <= 10; i++) {
                //每一个订单发送的信息
                for (int j = 0; j < 3; j++) {
                    JSONObject orderInfo = new JSONObject();
                    orderInfo.put("orderId",i);
                    String msg = "";
                    switch (j % 3){
                        case 0:
                            msg = "订单号:"+i+"创建订单";
                            break;
                        case 1:
                            msg = "订单号:"+i+"生成支付订单";
                            break;
                        case 2:
                            msg = "订单号:"+i+"创建物流信息";
                            break;
                    }
                    orderInfo.put("msg",msg);
                    Message message = new Message("TopicOrder", "orderinfo", i + "",
                            orderInfo.toJSONString().getBytes());
                    SendResult sendResult = producer.send(message, new MessageQueueSelector() {
                        @Override
                        public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
                            //附加参数 producer.send方法中的第三个参数
                            int id = (Integer) arg;
                            //和队列总数取余,来决定选择哪个队列,因为相同的订单ID取余的结果是一样的,这样就能保证相同的订单,我们选择的是同一个队列
                            int index = id % mqs.size();
                            MessageQueue messageQueue = mqs.get(index);
                            System.out.println("队列:" + messageQueue+",信息:"+new String(msg.getBody()));
                            return messageQueue;
                        }
                    }, i);
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                producer.shutdown();
            } catch (Exception e) {
            }
        }

消费者

使用MessageListenerOrderly监听器,用于实现有序消费,相同订单号的消息由同一个消费者进行消费处理

//创建消费者对象
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("TestPushConsumerGroup");
        try {
            //设置NameServer节点
            consumer.setNamesrvAddr("192.168.2.5:9876");
            //订阅主题,
            consumer.subscribe("TopicOrder", "");
            //设置消费模式为集群模式
            consumer.setMessageModel(MessageModel.CLUSTERING);
            //创建监听,使用MessageListenerOrderly监听器,用于实现有序队列
            consumer.registerMessageListener(new MessageListenerOrderly() {
                @Override
                public ConsumeOrderlyStatus consumeMessage(List<MessageExt> list, ConsumeOrderlyContext consumeOrderlyContext) {
                    for (MessageExt msg : list) {
                        System.out.println("队列"+consumeOrderlyContext.getMessageQueue()+"获取数据:" + msg.getMsgId() + "==>" + new
                                String(msg.getBody()));
                    }
                    return ConsumeOrderlyStatus.SUCCESS;
                }
            });
            //启动消费者,与Broker建立长连接,开始监听。
            consumer.start();
        } catch (Exception e) {
            e.printStackTrace();
        }

接下来我们看下生产者的日志记录:

可以看出,相同订单号的订单产生的信息,由同一个队列进行发送

消费者日志:

从日志不难看出,相同订单号的消息,由同一个消费者顺序执行,以上就是有序消息的实现过程

五、注意事项

1.RocketMQ如何保证消息可靠

消费侧通过重试-死信机制,Rebalance机制等多种机制保证消费的可靠性

重试-死信机制

提供了内置的消息重试机制,以确保在消息发送或者消费时发生问题时,消息能够被重新尝试。默认的情况为16次,这16次的尝试机会,间隔的时间也不同

尝试次数

与上次重试间隔时间

尝试次数

与上次重试间隔时间

1

10s

9

7min

2

30s

10

8min

3

1min

11

9min

4

2min

12

10min

5

3min

13

20min

6

4min

14

30min

7

5min

15

1h

8

6min

16

2h

死信机制是指被消费者重试次数达到阈值后,无法被消费的信息被投入到了死信队列,死信队列中的消息可以通过一个专门的消费者进行处理,以查看失败的消息并采取适当的措施,比如记录日志、人工干预或者其他处理方式

Rebalance机制

用于动态调整消费者集群中各个消费者实例之间对消息队列的分配,以确保负载均衡。这个机制使得在集群中增加或减少消费者实例时,系统能够自动地重新分配队列,以确保每个消费者实例负责处理的队列数量大致相等。

2.有序消费的使用限制

有序消费模式只支持集群模式MessageModel.CLUSTERING,不支持广播模式,这一点要注意

 下一篇将介绍SpringBoot与RocketMQ的集成使用

关注我,不迷路

  • 26
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员的小黑板

请作者喝一杯咖啡吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值