消息队列-RockMQ-过滤发送消息实践

本文介绍了如何在RockMQ中通过TAG、订阅策略和SQL筛选来满足不同消费者组对不同类型消息的需求,以防止消息丢失。作者通过实例展示了全订阅、单TAG订阅、多TAG订阅以及SQL订阅的方法及其潜在问题
摘要由CSDN通过智能技术生成

过滤发送消息实战

#这种场景Broker是要开启属性过滤的
enablePropertyFilter=true 

我们有这样一个需求,我们上面的消费组只想感知A类消息,下面的消费者组只想感知BC类的消息。我们的消息都是发送到同一个Topic的,这个时候怎么做一个筛选呢?RockMQ是通过TAG来进行筛选。这个时候就又有一个问题如果我们上面这个消费者组订阅了Tag1和Tag2的消息,下面订阅了Tag1和Tag3的消息。那么这个时候如果上面这个消费组的第二个消费者遇到了Tag1的消息岂不是就直接丢弃掉,同理下面也是,可能无形中丢失了一些消息。
在这里插入图片描述
验证是否丢失消息
我们启动一个生产者发送TAG1和TAG2的消息,然后启动两个消费者(属于同一个消费者组分别订阅TAG1和TAG2)来消费看消费情况:

public class Consumer6 {
    public static void main(String[] args) throws Exception{
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("test-lost");
        consumer.setNamesrvAddr("ip:9876");
        consumer.subscribe("test-lost", "TAG1");
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List< MessageExt > msgs, ConsumeConcurrentlyContext context) {
                for (MessageExt msg : msgs) {
                    System.out.println(String.format("msg {%s} recvTime %s", new String(msg.getBody()), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        consumer.start();
    }
}
public class Consumer5 {
    public static void main(String[] args) throws Exception{
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("test-lost");
        consumer.setNamesrvAddr("ip:9876");
        consumer.subscribe("test-lost", "TAG2");
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List< MessageExt > msgs, ConsumeConcurrentlyContext context) {
                for (MessageExt msg : msgs) {
                    System.out.println(String.format("msg {%s} recvTime %s", new String(msg.getBody()), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        consumer.start();
    }
}

我这里测试得到的结果是始终消费不到TAG2的消息,如果你先启动消费者TAG2也就消费不到TAG1的消息,产生了消息丢失。

为了避免这种情况,我们要保证一个消费者组订阅的都是同一个TAG(非常重要)
在这里插入图片描述
生产者1

public class Producer {
    public static void main(String[] args) throws Exception {
        DefaultMQProducer producer = new DefaultMQProducer("producer_group");
        producer.setNamesrvAddr("ip:9876");
        producer.start();
        // 生成3大类消息
        List<Order> F = OrderBuilder.build(1, 4, "TAG1", "A");
        List<Order> S = OrderBuilder.build(5, 4, "TAG2", "B" );
        List<Order> T = OrderBuilder.build(9, 4, "TAG3", "C" );
        ArrayList<Order> orders = new ArrayList<Order>() {{
            addAll(F);
            addAll(S);
            addAll(T);
        }};
        List<Message> msgs = new ArrayList<>();
        for (Order order : orders) {
            Message msg = new Message("filter-topic", "filter-topic-str", order.toString().getBytes());
            msg.setKeys("filter-topic-trace");
            msgs.add(msg);
        }
        producer.send(msgs);
    }
}

生产者2

public class Producer2 {
    public static void main(String[] args) throws Exception {
        DefaultMQProducer producer = new DefaultMQProducer("producer_group");
        producer.setNamesrvAddr("ip:9876");
        producer.start();
        // 生成3大类消息
        List<Order> F = OrderBuilder.build(1, 4, "TAG1", "A");
        List<Order> S = OrderBuilder.build(5, 4, "TAG2", "B" );
        List<Order> T = OrderBuilder.build(9, 4, "TAG3", "C" );
        ArrayList<Order> orders = new ArrayList<Order>() {{
            addAll(F);
            addAll(S);
            addAll(T);
        }};
        List<Message> msgs = new ArrayList<>();
        for (Order order : orders) {
            Message msg = new Message("filter-topic", order.getTag(), order.toString().getBytes());
            msg.setKeys("filter-topic-trace");
            msg.putUserProperty("idx", String.valueOf(order.getOrderID()));
            msgs.add(msg);

        }
        producer.send(msgs);
    }
}

我们有这几种订阅方式
第一种 全部订阅

public class Consumer4 {
    public static void main(String[] args) throws Exception{
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("filter-group-all");
        consumer.setNamesrvAddr("ip:9876");
        consumer.subscribe("filter-topic", "*");
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,  ConsumeConcurrentlyContext context) {for (MessageExt msg : msgs) {
                System.out.println(String.format("msg {%s} recvTime %s", new String(msg.getBody()), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
            }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        consumer.start();
    }
}

第二种Tag订阅
订阅一个TAG

public class Consumer {
    public static void main(String[] args) throws Exception{
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("filter-group-tag1");
        consumer.setNamesrvAddr("ip:9876");
        consumer.subscribe("filter-topic", "TAG1");
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List< MessageExt > msgs, ConsumeConcurrentlyContext context) {
                for (MessageExt msg : msgs) {
                    System.out.println(String.format("msg {%s} recvTime %s", new String(msg.getBody()), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        consumer.start();
    }
}

我们可以通过||的方式订阅多个TAG

public class Consumer2 {
    public static void main(String[] args) throws Exception{
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("filter-group-tag2-tag3");
        consumer.setNamesrvAddr("ip:9876");
        consumer.subscribe("filter-topic", "TAG2 || TAG3");
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,  ConsumeConcurrentlyContext context) {for (MessageExt msg : msgs) {
                    System.out.println(String.format("msg {%s} recvTime %s", new String(msg.getBody()), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        consumer.start();
    }
}

第三种SQL订阅
这种方式是基于我们使用生产者2在发送消息的时候设置了属性,这样我们就可以在收到消息的时候使用SQL进行筛选。

public class Consumer3 {
    public static void main(String[] args) throws Exception{
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("filter-group-sql");
        consumer.setNamesrvAddr("ip:9876");
        consumer.subscribe("filter-topic", MessageSelector.bySql("idx > 10"));
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {for (MessageExt msg : msgs) {
                System.out.println(String.format("msg {%s} recvTime %s", new String(msg.getBody()), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
            }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        consumer.start();
    }
}

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值