RocketMq系列,第三章:RocketMq简单消息案例

RocketMq系列,第三章:RocketMq简单消息案例

一、准备工作

导入MQ客户端依赖

<!--rocketmq client-->
<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-client</artifactId>
    <version>4.7.1</version>
</dependency>

生产者生产消息步骤:

1、创建消息生产者producer
2、指定生产者组名
3、指定namesrv地址
4、启动producer
5、创建消息对象,指定主题Topic、Tag和消息体
6、发送消息
7、关闭生产者producer

消费者消费消息步骤:

1、创建消费者consumer
2、指定消费者组名
3、指定namesrv地址
4、订阅主题Topic和Tag
5、设置回调函数,处理消息
6、启动消费者consumer

二、案例

1、基本案例:

1.1、生产消息
1)生产同步消息

这种可靠性同步地发送方式使用的比较广泛,比如:重要的消息通知,商品减库存等。

public static void main(String[] args) throws Exception {
    BaseProducer producer = new BaseProducer();
    //发送同步消息
    producer.SyncProducer();
}

/**
 * 1、发送同步消息:可靠性强
 */
public void SyncProducer() throws Exception {
    //创建消息生产者-指定生产组
    DefaultMQProducer producer = new DefaultMQProducer("producer_group_name");
    //设置namesrv
    producer.setNamesrvAddr("127.0.0.1:9876");
    //启动producer实例
    producer.start();
    //创建消息体
    for(int i=0;i<10;i++){
        Message msg = new Message("TopicA","TagA",("hello_world : "+i).getBytes());
        SendResult res = producer.send(msg);
        System.out.println("producer:----->"+res);
    }
    //关闭生产者
    producer.shutdown();
}

2)生产异步消息

发送异步消息:对相应时间敏感的业务场景:比如发送短信等

public static void main(String[] args) throws Exception {
    BaseProducer producer = new BaseProducer();
    //发送同步消息
    //producer.SyncProducer();

    //发送同步消息
    producer.ASyncProducer();
}

/**
 * 2、发送异步消息:对相应时间敏感的业务场景
 */
public void ASyncProducer() throws Exception {
    //创建消息生产者-指定生产组
    DefaultMQProducer producer = new DefaultMQProducer("producer_group_name");
    //指定namesrc
    producer.setNamesrvAddr("127.0.0.1:9876");
    //启动producer实例
    producer.start();
    //设置消息发送失败后重试次数
    producer.setRetryTimesWhenSendAsyncFailed(2);
    //创建消息体-发送消息
    for(int i=0;i<10;i++){
        Message msg = new Message("TopicB","TagB",("你好 rocketmq: "+i).getBytes(RemotingHelper.DEFAULT_CHARSET));
        producer.send(msg, new SendCallback() {
            @Override
            public void onSuccess(SendResult sendResult) {
                System.out.println("producer---success-->"+sendResult);
            }

            @Override
            public void onException(Throwable e) {
                System.out.println("producer--error-->"+e.toString());
            }
        });
    }
    //关闭发送不成功的
}

3)生产单向消息

单向发送消息:这种方式主要用在不特别关心发送结果的场景,例如日志发送。

public static void main(String[] args) throws Exception {
    BaseProducer producer = new BaseProducer();
    //发送同步消息
    //producer.SyncProducer();

    //发送同步消息
    //producer.ASyncProducer();

    //单向发送消息
    producer.OneWayProducer();
}


/**
 *  3、单向发送消息:这种方式主要用在不特别关心发送结果的场景,例如日志发送。
 */
public void OneWayProducer() throws Exception {
    //创建消息生产者-指定生产组
    DefaultMQProducer producer = new DefaultMQProducer("producer_group_name");
    //指定namesrc
    producer.setNamesrvAddr("127.0.0.1:9876");
    //启动producer实例
    producer.start();
    //创建消息体-发送消息
    for(int i=0;i<10;i++){
        Message msg = new Message("TopicC","TagC",("hello_world : "+i).getBytes());
        producer.sendOneway(msg);
    }
    //关闭生产者
    producer.shutdown();
}

1.2、消费消息
1)负载均衡模式

多个消费者 共同 消费 消息队列中的消息。每个消费者处理的消息不同。

public static void main(String[] args) throws MQClientException {
    //负载均衡模式
    BaseConsumer baseConsumer =  new BaseConsumer();
    baseConsumer.loadBalanceConsumer();
}


/**
 * 1、负载均衡的模式消费消息:多个消费者 共同 消费  消息队列中的消息。
 *      每个消费者处理的消息不同
 */
public void loadBalanceConsumer() throws MQClientException {
    //创建消息消费者,指定消费者组名
    DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer_group_name");
    //指定namesrv
    consumer.setNamesrvAddr("127.0.0.1:9876");
    //订阅Topic
    consumer.subscribe("TopicA","*");
    //设置消费者模式:负载均衡模式
    consumer.setMessageModel(MessageModel.CLUSTERING);
    //设置消息回调函数,处理消息
    consumer.registerMessageListener(new MessageListenerConcurrently() {
        @Override
        public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
            for(MessageExt ext : list){
                System.out.println("consumer消费消息:====>"+Thread.currentThread().getName()+"{ topic: "+ext.getTopic()
                                   +"  tags:  "+ext.getTags()
                                   +"  body:  "+new String(ext.getBody()));
            }
            return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
        }
    });
    //启动消费者
    consumer.start();
    System.out.println("consumer:======>消费者启动");

}

2)广播模式
public static void main(String[] args) throws MQClientException {
    BaseConsumer baseConsumer =  new BaseConsumer();
    //负载均衡模式
    //baseConsumer.loadBalanceConsumer();

    //广播模式
    baseConsumer.BroadCastConsumer();
}


/**
 * 2、广播模式:每个消费者消费的消息都是相同的
 */
public void BroadCastConsumer() throws MQClientException {
    //创建消息消费者,指定消费者组
    DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer_group_name");
    //指定namesrv
    consumer.setNamesrvAddr("127.0.0.1:9876");
    //订阅Topic
    consumer.subscribe("TopicA","*");
    //设置消费者消费模式
    consumer.setMessageModel(MessageModel.BROADCASTING);
    //设置消息回调,处理消息
    consumer.registerMessageListener(new MessageListenerConcurrently() {
        @Override
        public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
            for(MessageExt ext : msgs){
                System.out.println("consumer消费消息:====>"+Thread.currentThread().getName()+"{ topic: "+ext.getTopic()
                                   +"  tags:  "+ext.getTags()
                                   +"  body:  "+new String(ext.getBody()));
            }
            return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
        }
    });

    //启动消费者
    consumer.start();
    System.out.println("consumer:======>消费者启动");
}

2、顺序消息

消息有序指的是可以按照消息的发送顺序来消费(FIFO)。

RocketMQ可以严格的保证消息有序, 可以分为 分区有序 或者 全局有序

顺序消费的原理解析,在默认的情况下消息发送会采取Round Robin轮询方式把消息 发送到不同的queue(分区队列 );

而消费消息的时候从多个queue上拉取消息,这种情况发送和消费是不能保证顺序。

但是如果控制发送的顺序消息只 依次发送到同一个queue中 ,消费的时候 只从这个queue上依次拉取 ,则就保证了顺序。

当发送和消费参与的queue只有一个,则是 全局有序

如果多个queue参与,则为 分区有序 ,即相对每个queue,消息都是有序的。

2.1、准备工作
/**
 * 模拟订单
 */
@Data
public class OrderStep {

    //订单ID
    private long orderId;
    //描述
    private String desc;

    //模拟 生成订单数据
    public List<OrderStep> buildOrders(){
        List<OrderStep> orderStepList = new ArrayList<>();

        //1 创建  付款  推送  完成
        //2 创建  付款  完成
        //3 创建  付款  完成
        OrderStep order = new OrderStep();
        order.setOrderId(1l);
        order.setDesc("创建");
        orderStepList.add(order);

        order = new OrderStep();
        order.setOrderId(2l);
        order.setDesc("创建");
        orderStepList.add(order);

        order = new OrderStep();
        order.setOrderId(1l);
        order.setDesc("付款");
        orderStepList.add(order);

        order = new OrderStep();
        order.setOrderId(3l);
        order.setDesc("创建");
        orderStepList.add(order);

        order = new OrderStep();
        order.setOrderId(1l);
        order.setDesc("推送");
        orderStepList.add(order);

        order = new OrderStep();
        order.setOrderId(2l);
        order.setDesc("付款");
        orderStepList.add(order);

        order = new OrderStep();
        order.setOrderId(2l);
        order.setDesc("完成");
        orderStepList.add(order);

        order = new OrderStep();
        order.setOrderId(3l);
        order.setDesc("付款");
        orderStepList.add(order);

        order = new OrderStep();
        order.setOrderId(3l);
        order.setDesc("完成");
        orderStepList.add(order);

        order = new OrderStep();
        order.setOrderId(1l);
        order.setDesc("完成");
        orderStepList.add(order);

        return orderStepList;
    }


}
2.2、生产消息
/**
 * 顺序消息:生产者
 */
public class OrderProducer {

    public static void main(String[] args) throws Exception {
        OrderProducer producer = new OrderProducer();

        //生产顺序消息
        producer.orderMessageProducer();
    }


    //生产顺序消息
    public void orderMessageProducer() throws Exception {
        //创建消息生产者-指明消息生产组
        DefaultMQProducer producer = new DefaultMQProducer("producer_mq_group");
        //指明namesrv
        producer.setNamesrvAddr("127.0.0.1:9876");
        //开启producer实例
        producer.start();

        String tags[] = {"TagA","TagB","TagC"};

        //生成订单列表
        List<OrderStep> orderStepList = new OrderStep().buildOrders();

        LocalTime time = LocalTime.now();

        //创建消息-发送消息
        for(int i=0;i<10;i++){

            String body = time+"  "+"hello rocketMq"+"  "+orderStepList.get(i);
            Message msg = new Message("TopicTest",tags[i%tags.length],body.getBytes(RemotingHelper.DEFAULT_CHARSET));

            //orderStepList.get(i).getOrderId()对应着Object arg入参
            SendResult res = producer.send(msg, new MessageQueueSelector() {
                @Override
                public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
                    System.out.println("---->" + arg);
                    Long id = (long) arg;//订单ID,根据订单ID选择发送的queue--这样相同的ID选择的queue是相同的
                    long index = id % mqs.size();
                    MessageQueue messageQueue = mqs.get((int) index);
                    return messageQueue;
                }
            }, orderStepList.get(i).getOrderId());

            System.out.println("producer:  queueId:{ "+res.getMessageQueue().getQueueId()+" }, " +
                    " status:{ "+res.getSendStatus()+" }," +
                    " body:{ "+body+" }");

        }

        producer.shutdown();
    }

}

2.3、消费消息
/**
 * 顺序消息:消费者
 */
public class OrderConsumer {

    public static void main(String[] args) throws MQClientException {
        OrderConsumer consumer = new OrderConsumer();

        //消费顺序消息
        consumer.orderMessageConsumer();
    }

    //消费顺序消息
    public void orderMessageConsumer() throws MQClientException {
        //创建消息消费者-指定消费组
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer_group_name");
        //指定namesrv
        consumer.setNamesrvAddr("127.0.0.1:9876");

        //设置consumer第一次启动,是从队列头部开始消费还是从队列尾部开始消费
        //如果非第一次启动,从上次消费位置接着消费
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);

        //订阅消息
        consumer.subscribe("TopicTest","TagA || TagB || TagC");

        consumer.registerMessageListener(new MessageListenerOrderly() {
            @Override
            public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
                context.setAutoCommit(true);
                for(MessageExt ext : msgs){
                    System.out.println("threadName: "+Thread.currentThread().getName()+"   queueId: "+ext.getQueueId()+"  body: "+new String(ext.getBody()));
                }
                return ConsumeOrderlyStatus.SUCCESS;
            }
        });

        consumer.start();
        System.out.println("=====>消费者启动");
    }
}

3、延时消息

比如电商里,提交了一个订单就可以发送一个延时消息,1h后去检查这个订单的状态,如果还是未付款就取消订单释放库存。

RocketMq 不支持定义延时时间 ,RocketMq定义了固定的延时等级,从1s到2h分别对应了1到18

// org/apache/rocketmq/store/config/MessageStoreConfig.java
private String messageDelayLevel = "1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h";
3.1、生产消息
/**
 * 延时消息
 */
public class ScheduleProducer {

    public static void main(String[] args) throws Exception {
        ScheduleProducer producer = new ScheduleProducer();

        //生产延时消息
        producer.ScheduleMessageProducer();
    }

    //生产延时消息:如订单生成后,发送延时消息,等1H后判断是否付款 否则取消订单释放库存
    public void ScheduleMessageProducer() throws Exception {
        //创建消息生产者-指定生产组
        DefaultMQProducer producer = new DefaultMQProducer("producer_group_name");
        //指定namesrv
        producer.setNamesrvAddr("127.0.0.1:9876");
        //开启producer实例
        producer.start();

        //生产消息-发送消息
        for (int i = 0; i <10 ; i++) {
            Message msg  =  new Message("TopicA","TagA",("Hello Schedule Message"+i).getBytes());

            //设置延时等级-3对应10s
            msg.setDelayTimeLevel(3);
            LocalTime time = LocalTime.now();
            //发送消息
            SendResult send = producer.send(msg);
            System.out.println("producer----->"+time+"  "+send.getSendStatus()+"  "+send.getMsgId()+"  "+new String(msg.getBody()));
        }

        //关闭生产者
        producer.shutdown();
    }

}

3.2、消费消息
/**
 * 延时消息
 */
public class ScheduleConsumer {

    public static void main(String[] args) throws MQClientException {
        ScheduleConsumer consumer = new ScheduleConsumer();
        //消费延时消息
        consumer.ScheduleMessageConsumer();
    }

    //消费延时消息
    public void ScheduleMessageConsumer() throws MQClientException {
        //创建消息消费者-指定消费组
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer_group_name");
        //指定namesrv
        consumer.setNamesrvAddr("127.0.0.1:9876");
        //订阅消息
        consumer.subscribe("TopicA","*");
        //设置消息回调函数,处理消息
        consumer.setMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                for (MessageExt ext: msgs) {
                    LocalTime time = LocalTime.now();
                    System.out.println("consumer===>"+time+"  "+ext.getQueueId()+"  "+ext.getMsgId()+" "+new String(ext.getBody()));
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });

        //启动消费者
        consumer.start();
        System.out.println("消费者启动");
    }
}

4、批量消息

批量发送消息能显著提高传递小消息的性能。

限制是这些批量消息应该有 相同的topic ,相同的waitStoreMsgOK,而且 不能是延时消息 。此外,这一批消息的 总大小不应超过4MB

4.1、生产消息
/**
 * 批量消息生产者
 */
public class BatchProducer {

    public static void main(String[] args) throws Exception {
        BatchProducer producer = new BatchProducer();

        //批量生产消息
        producer.BatchMesssageProducer();
    }

    //批量生产消息
    public void BatchMesssageProducer() throws Exception {
        //创建消息生产者-指定生产组
        DefaultMQProducer producer = new DefaultMQProducer("producer_group_name");
        //指定namesrv
        producer.setNamesrvAddr("127.0.0.1:9876");
        //启动producer实例
        producer.start();

        //创建消息body
        String topicName = "TopicD";
        List<Message> messageList = new ArrayList<>();
        for(int i=0;i<10;i++){
            Message msg = new Message(topicName,"tag"+i, ("hello rocketmq,this is batch message : "+i).getBytes());
            messageList.add(msg);
        }
        
        //批量发送消息
        SendResult send = producer.send(messageList);
        System.out.println("producer------>"+send.getSendStatus()+" "+send.getMsgId()+" "+send.getMessageQueue().getQueueId()+" "+messageList);

        //关闭producer
        producer.shutdown();
    }

}

5、过滤消息

5.1、byTag基本语法

在大多数情况下,TAG是一个简单而有用的设计,其可以来选择您想要的消息。例如:

DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer_group_name");
consumer.subscribe("TopicE", "tagA || tagB || tagC");

消费者将接收包含tagA或tagB或tagC的消息。

5.1、bySQL基本语法

byTag限制是 一个消息只能有一个标签 ,这对于复杂的场景可能不起作用。

在这种情况下,可以使用SQL表达式筛选消息。SQL特性可以通过发送消息时的属性来进行计算。在RocketMQ定义的语法下,可以实现一些简单的逻辑。下面是一个例子:

------------
| message  |
|----------|  a > 5 AND b = 'abc'
| a = 10   |  --------------------> Gotten
| b = 'abc'|
| c = true |
------------
------------
| message  |
|----------|   a > 5 AND b = 'abc'
| a = 1    |  --------------------> Missed
| b = 'abc'|
| c = true |
------------

RocketMQ只定义了一些基本语法来支持这个特性。你也可以很容易地扩展它。

  • 数值比较,比如: >,>=,<,<=,BETWEEN,=;
  • 字符比较,比如: =,<>,IN;
  • IS NULL 或者 IS NOT NULL;
  • 逻辑符号 AND,OR,NOT;

常量支持类型为:

  • 数值,比如: 123,3.1415;
  • 字符,比如: 'abc',必须用单引号包裹起来;
  • NULL ,特殊的常量
  • 布尔值, TRUE FALSE

只有使用push模式的消费者才能用使用SQL92标准的sql语句,接口如下:

public void subscribe(finalString topic, final MessageSelector messageSelector)
5.2、生产消息
/**
 * 消息生产者
 */
public class FilterProducer {

    public static void main(String[] args) throws Exception {
        FilterProducer producer = new FilterProducer();

        //生产消息
        producer.filterMessageProducer();
    }

    //生产消息
    public void filterMessageProducer() throws Exception {
        //创建消息生产者-指定生产组
        DefaultMQProducer producer = new DefaultMQProducer("producer_group_name");
        //设置namesrv
        producer.setNamesrvAddr("127.0.0.1:9876");
        //启动producer实例
        producer.start();
        //创建消息体-发送消息
        for(int i=0;i<10;i++){
            Message msg = new Message("TopicE","tage"+i,("hello_world filter rocketmq : "+i).getBytes());

            //设置消息参数--为消费判断做前提
            msg.putUserProperty("myParam",String.valueOf(i));

            SendResult res = producer.send(msg);
            System.out.println("producer:----->"+res);
        }
        //关闭生产者
        producer.shutdown();
    }
}
5.3、消费消息
/**
 * 过滤消息
 */
public class FilterConsumer {

    public static void main(String[] args) throws MQClientException {
        FilterConsumer consumer = new FilterConsumer();

        //过滤消息
        consumer.FilterMessageConsumer();
    }

    //过滤消息
    public void FilterMessageConsumer() throws MQClientException {
        //创建消息消费者,指定消费者组名
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer_group_name");
        //指定namesrv
        consumer.setNamesrvAddr("127.0.0.1:9876");
        //订阅Topic--过滤消息【两种方式1:bySql 2:byTag】
        consumer.subscribe("TopicE",MessageSelector.bySql("myParam between 1 and 5"));
        //设置消费者模式:负载均衡模式
        //consumer.setMessageModel(MessageModel.CLUSTERING);
        //设置消息回调函数,处理消息
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                for(MessageExt ext : list){
                    System.out.println("consumer消费消息:====>"+Thread.currentThread().getName()+"{ topic: "+ext.getTopic()
                            +"  tags:  "+ext.getTags()
                            +"  body:  "+new String(ext.getBody()));
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        //启动消费者
        consumer.start();
        System.out.println("consumer:======>消费者启动");
    }


}

5.4、注意:

consumer启动的时候会报:

org.apache.rocketmq.client.exception.MQClientException: CODE: 1 DESC: The broker does not support consumer to filter message by SQL92

解决方法:

在配置文件中新增:
enablePropertyFilter = true

6、事务消息

6.1、流程分析

上图说明了事务消息的大致方案,其中分为两个流程:正常事务消息的发送及提交、事务消息的补偿流程。

1)事务消息发送及提交

(1) 发送消息(half消息)。

(2) 服务端响应消息写入结果。

(3) 根据发送结果执行本地事务(如果写入失败,此时half消息对业务不可见,本地逻辑不执行)。

(4) 根据本地事务状态执行Commit或者Rollback(Commit操作生成消息索引,消息对消费者可见)

2)事务补偿

(1) 对没有Commit/Rollback的事务消息(pending状态的消息),从服务端发起一次“回查”

(2) Producer收到回查消息,检查回查消息对应的本地事务的状态

(3) 根据本地事务状态,重新Commit或者Rollback

其中,补偿阶段用于解决消息Commit或者Rollback发生超时或者失败的情况。

3)事务消息状态

事务消息共有三种状态,提交状态、回滚状态、中间状态:

  • TransactionStatus.CommitTransaction: 提交事务,它允许消费者消费此消息。
  • TransactionStatus.RollbackTransaction: 回滚事务,它代表该消息将被删除,不允许被消费。
  • TransactionStatus.Unknown: 中间状态,它代表需要检查消息队列来确定状态。
6.2、生产消息

使用 TransactionMQProducer 类创建生产者,并指定唯一的 ProducerGroup ,就可以设置自定义线程池来处理这些检查请求。执行本地事务后、需要根据执行结果对消息队列进行回复。

/**
 * 事务消息生产者
 */
public class TransactionProducer {


    public static void main(String[] args) throws Exception {
        TransactionProducer producer = new TransactionProducer();

        //生产者创建事务消息
        producer.TransactionMessageProducer();
    }

    public void TransactionMessageProducer() throws Exception {

        //创建事务消息生产者
        TransactionMQProducer producer = new TransactionMQProducer("TRANSACTION_GROUP_NAME");
        //设置namesrv
        producer.setNamesrvAddr("127.0.0.1:9876");

        //设置监听器
        TransactionListener listener = new MyTransactionListener();
        producer.setTransactionListener(listener);

        //启动消息生产者
        producer.start();
        String[] tags = new String[]{"TagA","TagB","TagC"};

        for(int i=0;i<3;i++){
            Message msg = new Message("TransactionMessageTopic",tags[i%tags.length],
                    ("KEY:"+i+" HELLO TRANSACTION MQ MESSAGE").getBytes(RemotingHelper.DEFAULT_CHARSET));
            TransactionSendResult result = producer.sendMessageInTransaction(msg, null);
            System.out.println("Producer:---->"+result);
        }

        //关闭生产者
//        producer.shutdown();
    }
}
6.3、事务监听

当发送半消息成功时,我们使用 executeLocalTransaction 方法来执行本地事务。它返回前一节中提到的三个事务状态之一。 checkLocalTranscation 方法用于检查本地事务状态,并回应消息队列的检查请求。它也是返回前一节中提到的三个事务状态之一。

public class MyTransactionListener implements TransactionListener {
    @Override
    public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
        System.out.println("执行本地事务");
        if (StringUtils.equals("TagA", msg.getTags())) {
            return LocalTransactionState.COMMIT_MESSAGE;
        } else if (StringUtils.equals("TagB", msg.getTags())) {
            return LocalTransactionState.ROLLBACK_MESSAGE;
        } else {
            return LocalTransactionState.UNKNOW;
        }
    }

    @Override
    public LocalTransactionState checkLocalTransaction(MessageExt msg) {
        System.out.println("MQ检查消息Tag【"+msg.getTags()+"】的本地事务执行结果");
        return LocalTransactionState.COMMIT_MESSAGE;
    }
}

6.4、使用限制
  1. 事务消息不支持延时消息和批量消息。
  2. 为了避免单个消息被检查太多次而导致半队列消息累积,我们默认将单个消息的检查次数限制为 15 次,但是用户可以通过 Broker 配置文件的 transactionCheckMax 参数来修改此限制。如果已经检查某条消息超过 N 次的话( N = transactionCheckMax ) 则 Broker 将丢弃此消息,并在默认情况下同时打印错误日志。用户可以通过重写 AbstractTransactionCheckListener 类来修改这个行为。
  3. 事务消息将在 Broker 配置文件中的参数 transactionMsgTimeout 这样的特定时间长度之后被检查。当发送事务消息时,用户还可以通过设置用户属性 CHECK_IMMUNITY_TIME_IN_SECONDS 来改变这个限制,该参数优先于 transactionMsgTimeout 参数。
  4. 事务性消息可能不止一次被检查或消费。
  5. 提交给用户的目标主题消息可能会失败,目前这依日志的记录而定。它的高可用性通过 RocketMQ 本身的高可用性机制来保证,如果希望确保事务消息不丢失、并且事务完整性得到保证,建议使用同步的双重写入机制。
  6. 事务消息的生产者 ID 不能与其他类型消息的生产者 ID 共享。与其他类型的消息不同,事务消息允许反向查询、MQ服务器能通过它们的生产者 ID 查询到消费者。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值