RocketMQ学习之不同消息类型发送样例

IDEA整合Maven项目

  • idea搭建maven项目

    https://blog.csdn.net/weixin_39209728/article/details/85853516

  • idea整合tomcat

    https://blog.csdn.net/lsqingfeng/article/details/90199876

    https://www.cnblogs.com/best/p/8543022.html

    https://www.cnblogs.com/creasybear/p/11598255.html

  • idea启动tomcat控制台乱码

    https://blog.csdn.net/nan_cheung/article/details/79337273

  • idea启动tomcat项目jsp页面乱码

    
        <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
    
    
  • tomcat添加配置后启动失败

    1. 添加配置
        <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" URIEncoding="UTF-8"/>
    
    
    1. tomcat启动报错
    
        org.apache.catalina.LifecycleException: 协议处理器启动失败
    
        Caused by: java.lang.IllegalArgumentException: The AJP Connector is configured with secretRequired="true" but the secret attribute is either null or "". This combination is not valid.
    
    
    
    1. 参考链接

      https://segmentfault.com/a/1190000021838764

消息发送总体思路

  • 导入RocketMQ客户端依赖

    
        <!-- 原生rocketmq client -->
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-client</artifactId>
            <version>4.4.0</version>
        </dependency>
    
    
    
  • 消息发送者步骤分析

    1. 创建消息生产者producer,并制定生产者组名

    2. 指定NameServer地址

    3. 启动Producer

    4. 创建消息对象,指定主题Topic、Tag和消息体

    5. 发送消息

    6. 关闭生产者Producer

  • 消息消费者步骤分析

    1. 创建消费者Consumer,指定消费者组名

    2. 指定Nameserver地址

    3. 订阅主题Topic和Tag

    4. 设置回调函数,处理消息

    5. 启动消费者

基本样例

同步消息

  • 案例代码
    
        import org.apache.rocketmq.client.producer.DefaultMQProducer;
        import org.apache.rocketmq.client.producer.SendResult;
        import org.apache.rocketmq.common.message.Message;
    
        import java.util.concurrent.TimeUnit;
    
        /**
        * 可靠性同步地发送方式使用的比较广泛,比如:重要的消息通知、短信通知
        */
        public class SyncProducer {
    
            public static void main(String[] args) throws Exception {
                //1. 创建消息生产者producer,并制定生产者组名
                DefaultMQProducer producer = new DefaultMQProducer("group1");
                //2. 指定NameServer地址
                producer.setNamesrvAddr("192.168.159.133:9876;192.168.159.134:9876");
                //3. 启动Producer
                producer.start();
                for (int i = 0; i < 10; i++) {
                //4. 创建消息对象,指定主题Topic、Tag和消息体
    
                    /**
                    * 参数1:消息主题Topic
                    * 参数2:消息标签Tag
                    * 参数3:消息内容
                    */
                    Message message = new Message("base", "tag1", ("武汉加油" + i).getBytes());
    
                    //5. 发送消息
                    SendResult result = producer.send(message);
                    System.out.println("发送结果:" + result);
    
                    /**
                    * Thread.sleep(1000);它是一个静态方法,暂停线程时它不会释放锁,该方法会抛出InterrupttedException异常(如果有线程中断了当前线程)。
                    * TimeUnit具有更好的可读性,不需要进行复杂的单位换算
                    */
                    //线程睡眠1秒
                    TimeUnit.SECONDS.sleep(1);
    
                }
    
                //6. 关闭生产者Producer
                producer.shutdown();
            }
        }
    
    
    

异步消息

  • 案例代码

    
        import org.apache.rocketmq.client.producer.DefaultMQProducer;
        import org.apache.rocketmq.client.producer.SendCallback;
        import org.apache.rocketmq.client.producer.SendResult;
        import org.apache.rocketmq.common.message.Message;
    
        import java.util.concurrent.TimeUnit;
    
        /**
        * 发送异步消息:通常对响应时间敏感的业务场景,即发送不能容忍长时间地等待broker的响应
        */
        public class AsyncProducer {
    
            public static void main(String[] args) throws Exception {
                //1. 创建消息生产者producer,并制定生产者组名
                DefaultMQProducer producer = new DefaultMQProducer("group1");
                //2. 指定NameServer地址
                producer.setNamesrvAddr("192.168.159.133:9876;192.168.159.134:9876");
                //3. 启动Producer
                producer.start();
                for (int i = 0; i < 10; i++) {
                    //4. 创建消息对象,指定主题Topic、Tag和消息体
    
                    /**
                    * 参数1:消息主题Topic
                    * 参数2:消息标签Tag
                    * 参数3:消息内容
                    */
                    Message message = new Message("base", "tag2", ("武汉加油" + i).getBytes());
    
                    //5. 发送消息结果包含 发送状态,消息id,消息接收队列id
                    producer.send(message, new SendCallback() {
    
                        //发送成功回调函数
                        @Override
                        public void onSuccess(SendResult sendResult) {
                            System.out.println("发送成功结果:" + sendResult);
                        }
    
                        @Override
                        public void onException(Throwable throwable) {
                            System.out.println("发送失败结果:" + throwable);
                        }
                    });
                    /**
                    * Thread.sleep(1000);它是一个静态方法,暂停线程时它不会释放锁,该方法会抛出InterrupttedException异常(如果有线程中断了当前线程)。
                    * TimeUnit具有更好的可读性,不需要进行复杂的单位换算
                    */
                    //线程睡眠1秒
                    TimeUnit.SECONDS.sleep(1);
    
                }
    
                //6. 关闭生产者Producer
                producer.shutdown();
                System.out.println("发送完成");
            }
        }
    
    
    
    

单向消息

  • 案例代码

    
        import org.apache.rocketmq.client.producer.DefaultMQProducer;
        import org.apache.rocketmq.common.message.Message;
    
        import java.util.concurrent.TimeUnit;
    
        /**
        * 发送单向消息:这种方式主要用于不特别关心发送结果的场景,例如日志发送
        */
        public class OneWayProducer {
            public static void main(String[] args) throws Exception {
    
                //1.创建消息生产者Producer,并制定生产者组名
                DefaultMQProducer producer = new DefaultMQProducer("group1");
    
                //2.指定NameServer地址
                producer.setNamesrvAddr("192.168.159.133:9876;192.168.159.134:9876");
    
                //3.启动producer
                producer.start();
    
                for (int i = 0; i < 10; i++) {
    
                    //4.创建消息对象,指定主题Topic,Tag和消息体
                    /**
                    * 参数1:消息主题Topic
                    * 参数2:消息标签Tag
                    * 参数3:消息内容
                    */
                    Message message = new Message("base", "tag3", ("武汉加油" + i).getBytes());
    
                    //5.发送单向消息
                    producer.send(message);
                    System.out.println("发送单向消息");
    
                    /**
                    * Thread.sleep(1000);它是一个静态方法,暂停线程时它不会释放锁,该方法会抛出InterrupttedException异常(如果有线程中断了当前线程)。
                    * TimeUnit具有更好的可读性,不需要进行复杂的单位换算
                    */
                    //线程睡眠1秒
                    TimeUnit.SECONDS.sleep(1);
                }
    
                //6.关闭生产者
                producer.shutdown();
    
            }
        }
    
    
    
    

消费消息

  • 样例代码

    
        import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
        import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
        import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
        import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
        import org.apache.rocketmq.common.message.MessageExt;
        import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;
    
        import java.util.List;
    
        public class Consumer {
    
            public static void main(String[] args) throws Exception {
                //1. 创建消费者Consumer,指定消费者组名 (推送模式)
                DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group1");
                //2. 指定Nameserver地址
                consumer.setNamesrvAddr("192.168.159.133:9876;192.168.159.134:9876");
                //3. 订阅主题Topic和Tag
                consumer.subscribe("base","tag3");
    
                //设置消费模式:默认是负载均衡模式,这里选用是广播
                consumer.setMessageModel(MessageModel.BROADCASTING);
                //4. 设置回调函数,处理消息
                consumer.registerMessageListener(new MessageListenerConcurrently() {
                    @Override
                    public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                        //接收消息内容
                        for (MessageExt messageExt : list) {
                            System.out.println(new String(messageExt.getBody()));
                        }
    
                        return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                    }
                });
    
                //5. 启动消费者
                consumer.start();
            }
        }
    
    
    
    

顺序消息

  • 介绍

    消息有序:是指可以按照消息的发送顺序来消费(FIFO)。RocketMQ可以严格的保证消息有序,可以分为分区有序或者全局有序

  • 原理分析

    默认情况下消息发送会采取Round Robin轮询方式把消息发送到不同的queue(分区队列),而消费消息的时候从多个queue中拉取消息,这种情况下导致发送和消费不能保证顺序。但相对于每一个queue消息都是有序的。

    全局有序:发送和消费参与的queue只有一个。

    分区有序:发送和消费参与的queue有多个。

  • 流程分析
    顺序消息流程分析

  • 顺序消息生产

    订单号相同的消息(创建/付款/推送/完成)会被先后发送到同一个队列中

  1. 构建订单集合

   import java.util.ArrayList;
   import java.util.List;

   public class OrderStep {

       private long orderId;

       private String desc;

       public long getOrderId() {
           return orderId;
       }

       public void setOrderId(long orderId) {
           this.orderId = orderId;
       }

       public String getDesc() {
           return desc;
       }

       public void setDesc(String desc) {
           this.desc = desc;
       }

       @Override
       public String toString() {
           return "OrderStep{" +
                   "orderId=" + orderId +
                   ", desc='" + desc + '\'' +
                   '}';
       }

       public static List<OrderStep> buildOrders() {
           // 1039L  创建  付款 推送 完成
           // 1065L 创建  付款
           // 7235L 创建  付款  推送
           List<OrderStep> orderList = new ArrayList<>();
           OrderStep demo = new OrderStep();
           demo.setOrderId(1039L);
           demo.setDesc("创建");
           orderList.add(demo);

           demo = new OrderStep();
           demo.setOrderId(1039L);
           demo.setDesc("付款");
           orderList.add(demo);

           demo.setOrderId(1039L);
           demo.setDesc("推送");
           orderList.add(demo);

           demo = new OrderStep();
           demo.setOrderId(1039L);
           demo.setDesc("完成");
           orderList.add(demo);

           demo = new OrderStep();
           demo.setOrderId(1065L);
           demo.setDesc("创建");
           orderList.add(demo);

           demo = new OrderStep();
           demo.setOrderId(1065L);
           demo.setDesc("付款");
           orderList.add(demo);

           demo = new OrderStep();
           demo.setOrderId(7235L);
           demo.setDesc("创建");
           orderList.add(demo);

           demo = new OrderStep();
           demo.setOrderId(7235L);
           demo.setDesc("付款");
           orderList.add(demo);

           demo = new OrderStep();
           demo.setOrderId(7235L);
           demo.setDesc("推送");
           orderList.add(demo);

           return orderList;
       }
   }



  1. 消息生产者


   import org.apache.rocketmq.client.producer.DefaultMQProducer;
   import org.apache.rocketmq.client.producer.MessageQueueSelector;
   import org.apache.rocketmq.client.producer.SendResult;
   import org.apache.rocketmq.common.message.Message;
   import org.apache.rocketmq.common.message.MessageQueue;

   import java.util.List;

   public class Producer {

       public static void main(String[] args) throws Exception {
           //1.创建消息生产者producer,并指定生产者组名
           DefaultMQProducer producer = new DefaultMQProducer("group1");
           //2.指定NameServer地址
           producer.setNamesrvAddr("192.168.159.133:9876;192.168.159.134:9876");
           //3.启动producer
           producer.start();
           //构建消息集合
           List<OrderStep> orderStepList = OrderStep.buildOrders();
           //发送消息
           for (int i = 0; i < orderStepList.size(); i++) {

               String body = orderStepList.get(i) + "";

               Message message = new Message("OrderTopic", "Order", i + "i", body.getBytes());

               /**
               * 参数1:消息对象
               * 参数2:消息队列选择器
               * 参数3:选择队列的业务标识(订单id)
               */
               SendResult result = producer.send(message, new MessageQueueSelector() {

                   /**
                   *
                   * @param list      队列集合
                   * @param message   消息对象
                   * @param o         业务标识的参数
                   * @return
                   */
                   @Override
                   public MessageQueue select(List<MessageQueue> list, Message message, Object o) {

                       long orderId = (Long) o;
                       //取余,建议根据实际场景使用hash算法
                       long index = orderId % list.size();
                       return list.get((int) index);
                   }
               }, orderStepList.get(i).getOrderId());
               System.out.println("发送结果:" + result);
           }
           producer.shutdown();
       }
   }



  • 顺序消息消费

    
      import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
      import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext;
      import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus;
      import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly;
      import org.apache.rocketmq.common.message.MessageExt;
    
      import java.util.List;
    
      public class Consumer {
    
          public static void main(String[] args) throws Exception {
              //1.创建消费者Consumer,制定消费者组名
              DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group1");
              //2.指定NameServer地址
              consumer.setNamesrvAddr("192.169.159.134:9876;192.168.159.133:9876");
              //3.订阅主题Topic和Tag
              consumer.subscribe("OrderTopic","*");
              //4.注册消息监听器
              consumer.registerMessageListener(new MessageListenerOrderly() {
    
                  @Override
                  public ConsumeOrderlyStatus consumeMessage(List<MessageExt> list, ConsumeOrderlyContext consumeOrderlyContext) {
    
                      for (MessageExt messageExt : list) {
                          System.out.println("线程名称:【" + Thread.currentThread().getName() + "】消费消息:" + new String(messageExt.getBody()));
                      }
                      return ConsumeOrderlyStatus.SUCCESS;
                  }
              });
    
              //5.启动消费者
              consumer.start();
              System.out.println("消费者启动了...");
    
          }
      }
    
    
    
    

延时消息

  • 场景分析

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

  • 延时等级
    延迟消息可选延时等级

  • 消息生产者

    
      public class Producer {
    
          public static void main(String[] args) throws Exception {
              // 1.创建消息生产者producer,并制定生产者组名
              DefaultMQProducer producer = new DefaultMQProducer("group1");
              // 2.指定NameServer地址
              producer.setNamesrvAddr("192.169.159.134:9876;192.168.159.133:9876");
              // 3.启动producer
              producer.start();
      
              for (int i = 0; i < 10; i++) {
                  // 4.创建消息对象,指定主题Topic、Tag和消息体
                  /*
                  参数1:消息主题Topic
                  参数2:消息Tag
                  参数3:消息内容
                  */
                  Message msg = new Message("DelayTopic","tag1",("hello world"+i).getBytes());
                  // 设置延迟时间 RocketMq并不支持任意时间的延时,需要试着几个固定的延时等级
                  // 从1s到2h分别对应着等级1到18  1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
                  msg.setDelayTimeLevel(2);
                  // 5.发送消息结果包含  发送状态  消息id 消息接收队列id等
                  SendResult result = producer.send(msg);
                  System.out.println("发送结果"+result);
      
                  // 线程睡眠1秒
                  TimeUnit.SECONDS.sleep(1);
              }
              // 6关闭生产者producer
              producer.shutdown();
          }
      }
    
    
  • 消息消费者

    
      public class Consumer {
      
          public static void main(String[] args) throws MQClientException {
              // 1.创建消费者Consumer,制定消费者组名
              DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group1");
              // 2.指定Nameserver地址
              consumer.setNamesrvAddr("192.169.159.134:9876;192.168.159.133:9876");
              // 3.订阅主题Topic和Tag
              consumer.subscribe("DelayTopic","tag1");
              // 消费模式:默认是负载均衡模式,还有一种是广播模式
      //        consumer.setMessageModel(MessageModel.BROADCASTING);
              // 4.设置回调函数,处理消息
              consumer.registerMessageListener(new MessageListenerConcurrently() {
                  //接收消息内容
                  public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                      for (MessageExt messageExt : list) {
                          System.out.println("消息id:【"+messageExt.getMsgId()+"】,延时时间:"+(System.currentTimeMillis()-messageExt.getStoreTimestamp()));
                      }
                      return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                  }
              });
              // 5.启动消费者consumer
              consumer.start();
              System.out.println("消费者启动");
          }
      }
    
    

批量消息

  • 介绍
    批量发送消息能显著提高传递消息的性能。限制是这些批量消息应该有相同的topic,相同的waitStoreMsgOK,而且不能是延时消息。此外,这一批消息的总大小不应超过4MB

  • 消息分割类

    
      import org.apache.rocketmq.common.message.Message;
    
      import java.util.Iterator;
      import java.util.List;
      import java.util.Map;
    
      public class ListSplitter implements Iterator<List<Message>> {
    
          private final int SIZE_LIMIT = 1000 * 1000;
          private final List<Message> messages;
          private int currIndex;
    
          public ListSplitter(List<Message> messages) {
              this.messages = messages;
          }
    
          @Override
          public boolean hasNext() {
              return currIndex < messages.size();
          }
    
          @Override
          public List<Message> next() {
              int nextIndex = currIndex;
              int totalSize = 0;
              for (; nextIndex < messages.size(); nextIndex++) {
                  Message message = messages.get(nextIndex);
                  int tmpSize = message.getTopic().length() + message.getBody().length;
                  Map<String, String> properties = message.getProperties();
                  for (Map.Entry<String, String> entry : properties.entrySet()) {
                      tmpSize += entry.getKey().length() + entry.getValue().length();
                  }
                  tmpSize = tmpSize + 20; //for log overhead
                  if (tmpSize > SIZE_LIMIT) {
                      //it is unexpected that single message exceeds the SIZE_LIMIT
                      //here just let it go, otherwise it will block the splitting process
                      if (nextIndex - currIndex == 0) {
                          //if the next sublist has no element, add this one and then break, otherwise just break
                          nextIndex++;
                      }
                      break;
                  }
                  if (tmpSize + totalSize > SIZE_LIMIT) {
                      break;
                  } else {
                      totalSize += tmpSize;
                  }
    
              }
              List<Message> subList = messages.subList(currIndex, nextIndex);
              currIndex = nextIndex;
              return subList;
          }
      }
    
    
    
  • 消息生产者

    
      import org.apache.rocketmq.client.producer.DefaultMQProducer;
      import org.apache.rocketmq.common.message.Message;
      import org.apache.rocketmq.remoting.common.RemotingHelper;
    
      import java.util.ArrayList;
      import java.util.List;
    
      public class Producer {
    
          public static void main(String[] args) throws Exception {
              DefaultMQProducer producer = new DefaultMQProducer("BatchProducerGroup");
    
              producer.setNamesrvAddr("192.169.159.134:9876;192.168.159.133:9876");
              producer.start();
    
              List<Message> msgs = new ArrayList<>();
              for (int i = 0; i < 1000; i++) {
                  try {
                      // Create a message instance, specifying topic, tag and message body.
                      Message msg = new Message(
                              "batch_send_message_topic" /* Topic */,
                              "TagA" /* Tag */,
                              ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET) /* Message body */
                      );
                      msgs.add(msg);
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              }
    
              //then you could split the large list into small ones:
              ListSplitter splitter = new ListSplitter(msgs);
              while (splitter.hasNext()) {
                  try {
                      List<Message> listItem = splitter.next();
                      producer.send(listItem);
                  } catch (Exception e) {
                      e.printStackTrace();
                      //handle the error
                  }
              }
    
              // Shut down once the producer instance is not longer in use.
              producer.shutdown();
          }
    
      }
    
    
    
  • 消息消费者

    
      public class Consumer {
          public static void main(String[] args) throws MQClientException {
              // 1.创建消费者Consumer,制定消费者组名
              DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group1");
              // 2.指定Nameserver地址
              consumer.setNamesrvAddr("192.169.159.134:9876;192.168.159.133:9876");
              // 3.订阅主题Topic和Tag
              consumer.subscribe("batchTopic","*");
              // 消费模式:默认是负载均衡模式,还有一种是广播模式
      //        consumer.setMessageModel(MessageModel.BROADCASTING);
              // 4.设置回调函数,处理消息
              consumer.registerMessageListener(new MessageListenerConcurrently() {
                  //接收消息内容
                  public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                      for (MessageExt messageExt : list) {
                          System.out.println(new String(messageExt.getBody()));
                      }
                      return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                  }
              });
              // 5.启动消费者consumer
              consumer.start();
              System.out.println("消费者启动");
          }
      }
    
    
    

过滤消息

  • Tag方式过滤
    Tag方式过滤

  • SQL表达式基本语法
    过滤消息SQL表达式

  • 消息生产者

    
      public class Producer {
          public static void main(String[] args) throws MQClientException, RemotingException, InterruptedException, MQBrokerException {
              // 1.创建消息生产者producer,并制定生产者组名
              DefaultMQProducer producer = new DefaultMQProducer("group1");
              // 2.指定NameServer地址
              producer.setNamesrvAddr("192.169.159.134:9876;192.168.159.133:9876");
              // 3.启动producer
              producer.start();
      
              for (int i = 0; i < 10; i++) {
                  // 4.创建消息对象,指定主题Topic、Tag和消息体
                  /*
                  参数1:消息主题Topic
                  参数2:消息Tag
                  参数3:消息内容
                  */
                  Message msg = new Message("filterSqlTopic","tag1",("hello world"+i).getBytes());
    
                  // 注意:设置属性
                  msg.putUserProperty("i",String.valueOf(i));
    
                  // 5.发送消息结果包含  发送状态  消息id 消息接收队列id等
                  SendResult result = producer.send(msg);
                  System.out.println("发送结果"+result);
      
                  // 线程睡眠1秒
                  TimeUnit.SECONDS.sleep(1);
              }
              // 6关闭生产者producer
              producer.shutdown();
          }
      }
    
    
    
  • 消息消费者

    
      public class ConsumerSql {
          public static void main(String[] args) throws MQClientException {
              // 1.创建消费者Consumer,制定消费者组名
              DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group1");
              // 2.指定Nameserver地址
              consumer.setNamesrvAddr("192.169.159.134:9876;192.168.159.133:9876");
              // 3.订阅主题Topic和Tag
    
              // 注意:通过tag筛选
              //consumer.subscribe("filterTopic","tag1 || tag2");
              // 注意:通过sql筛选
              consumer.subscribe("filterSqlTopic", MessageSelector.bySql("i>5"));
              // 消费模式:默认是负载均衡模式,还有一种是广播模式
              consumer.setMessageModel(MessageModel.BROADCASTING);
              // 4.设置回调函数,处理消息
              consumer.registerMessageListener(new MessageListenerConcurrently() {
                  //接收消息内容
                  public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                      for (MessageExt messageExt : list) {
                          System.out.println(new String(messageExt.getBody()));
                      }
                      return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                  }
              });
              // 5.启动消费者consumer
              consumer.start();
              System.out.println("消费者启动");
          }
      }
    
    
    

事务消息

  • 流程分析
    事务消息流程分析

  • 流程分类

  1. 事务消息发送及提交

    (1) 发送消息(half消息)
    (2) 服务端响应消息写入结果
    (3) 根据发送结果执行本地事务(如果写入失败,此时half消息对业务不可见,本地逻辑不执行)
    (4) 根据本地事务状态执行Commit或者Rollback(Commit操作生成消息索引,消息对消费者可见)

  2. 事务补偿

 (1) 对没有Commit/Rollback的事务消息(pending状态的消息),从服务端发起一次"回查"
 (2) Producer收到回查消息,检查回查消息对应的本地事务的状态
 (3) 根据本地事务状态,重新Commit或者Rollback
  • 事务消息状态

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

    
      public class Producer {
      
          public static void main(String[] args) throws Exception {
              // 1.创建消息生产者producer,并制定生产者组名  事务生产者
              TransactionMQProducer producer = new TransactionMQProducer("group5");
              // 2.指定NameServer地址
              producer.setNamesrvAddr("192.169.159.134:9876;192.168.159.133:9876");
              // 生产者监听器
              producer.setTransactionListener(new TransactionListener() {
                  /**
                  * 在该方法中执行本地事务 第二步
                  * @param message
                  * @param o
                  * @return
                  */
                  public LocalTransactionState executeLocalTransaction(Message message, Object o) {
                      if (StringUtils.equals("taga",message.getTags())){
                          return LocalTransactionState.COMMIT_MESSAGE;
                      } else if (StringUtils.equals("tagb",message.getTags())){
                          return LocalTransactionState.ROLLBACK_MESSAGE;
                      } else if (StringUtils.equals("tagc",message.getTags())){
                          return LocalTransactionState.UNKNOW;
                      }
                      return LocalTransactionState.UNKNOW;
                  }
      
                  /**
                  * 该方法是mq进行消息事务状态回查 第三步
                  * @param messageExt
                  * @return
                  */
                  public LocalTransactionState checkLocalTransaction(MessageExt messageExt) {
                      System.out.println("消息的tag:"+messageExt.getTags());
                      return LocalTransactionState.COMMIT_MESSAGE;
                  }
              });
              // 3.启动producer
              producer.start();
              String[] tags = {"taga","tagb","tagc"};
              for (int i = 0; i < 3; i++) {
                  // 4.创建消息对象,指定主题Topic、Tag和消息体
                  /*
                  参数1:消息主题Topic
                  参数2:消息Tagf
                  参数3:消息内容
                  */
                  Message msg = new Message("transactionTopic",tags[i],("hello world"+i).getBytes());
                  // 5.发送消息结果包含  发送状态  消息id 消息接收队列id等 第一步
                  SendResult result = producer.sendMessageInTransaction(msg,null);
                  System.out.println("发送结果"+result);
      
                  // 线程睡眠1秒
                  TimeUnit.SECONDS.sleep(3);
              }
              // 6关闭生产者producer
      //        producer.shutdown();
          }
      }
    
    
    
  • 消息消费者

    
      public class Consumer {
    
          public static void main(String[] args) throws MQClientException {
              // 1.创建消费者Consumer,制定消费者组名
              DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group5");
              // 2.指定Nameserver地址
              consumer.setNamesrvAddr("192.169.159.134:9876;192.168.159.133:9876");
              // 3.订阅主题Topic和Tag
              consumer.subscribe("transactionTopic","*");
      
              // 4.设置回调函数,处理消息
              consumer.registerMessageListener(new MessageListenerConcurrently() {
                  //接收消息内容
                  public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                      for (MessageExt messageExt : list) {
                          System.out.println(new String(messageExt.getBody()));
                      }
                      return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                  }
              });
              // 5.启动消费者consumer
              consumer.start();
              System.out.println("消费者启动");
          }
      
      }
    
    
    
    
  • 使用限制

    事务消息使用限制

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值