RocketMQ笔记(七):普通消息

  普通消息为 RocketMQ 中最基础的消息,支持生产者和消费者的异步解耦通信。

一、普通消息的生命周期

 0

1、初始化

  消息被生产者构建并完成初始化,待发送到服务端的状态。

2、待消费

  消息被发送到服务端,对消费者可见,等待消费者消费的状态。

3、消费中

  消息被消费者获取,并按照消费者本地的业务逻辑进行处理的过程。此时服务端会等待消费者完成消费并提交消费结果,如果一定时间后没有收到消费者的响应,RocketMQ会对消息进行重试处理。

4、消费提交

  消费者完成消费处理,并向服务端提交消费结果,服务端标记当前消息已经被处理(包括消费成功和失败)。 RocketMQ默认支持保留所有消息,此时消息数据并不会立即被删除,只是逻辑标记已消费。消息在保存时间到期或存储空间不足被删除前,消费者仍然可以回溯消息重新消费。

5、消息删除

  RocketMQ按照消息保存机制滚动清理最早的消息数据,将消息从物理文件中删除。

二、普通消息的发送方式

  RocketMQ提供了三种发送消息的模式,分别为同步发送消息、异步发送消息、单向发送消息。

1 public enum CommunicationMode {
2     SYNC,  // 同步
3     ASYNC,  // 异步
4     ONEWAY;  // 单向
5 
6     private CommunicationMode() {
7     }
8 }

1、同步发送

  同步发送是指消息发送方发出数据后,同步等待,直到收到接收方发回响应之后才发下一个请求。

  

2、异步发送

  消息发送方在发送了一条消息后,不等接收方发回响应,接着进行第二条消息发送。发送方通过回调接口的方式接收服务器响应,并对响应结果进行处理。

  异步消息通常用在对响应时间敏感的业务场景,即发送端不能容忍长时间地等待Broker的响应。

  

3、单向发送

  单向(Oneway)发送特点为发送方只负责发送消息,不等待服务器回应且没有回调函数触发,即只发送请求不等待应答。

  单向发送方式发送消息的过程耗时非常短,一般在微秒级别。不需要关心发送结果的场景,例如日志发送。

  

三、普通消息的消费方式

 1 // 消息消费模式
 2 public enum MessageModel {
 3     // 广播
 4     BROADCASTING("BROADCASTING"),
 5     // 负载均衡
 6     CLUSTERING("CLUSTERING");
 7     private String modeCN;
 8     private MessageModel(String modeCN) {
 9         this.modeCN = modeCN;
10     }
11     public String getModeCN() {
12         return this.modeCN;
13     }
14 }

  在实际的开发过程中,使用consumer的setMessageModel()方法,指定消费方式。

1、负载均衡消费模式 - CLUSTERING

  消费者采用负载均衡方式消费消息,一个分组(Group)下的多个消费者共同消费队列消息,每个消费者处理的消息不同。

  一个Consumer Group中的各个Consumer实例分摊去消费消息,即一条消息只会投递到一个Consumer Group下面的一个实例。

  

  例如某个Topic有3个队列,其中一个Consumer Group 有 3 个实例,那么每个实例只消费其中的1个队列。集群消费模式是消费者默认的消费方式。

2、广播消费模式 - BROADCASTING

  广播消费模式中消息将对一个Consumer Group下的各个Consumer实例都投递一遍。即使这些 Consumer属于同一个Consumer Group,消息也会被Consumer Group 中的每个Consumer都消费一次。

 

  

  实际上,是一个消费组下的每个消费者实例都获取到了topic下面的每个Message Queue去拉取消费。所以消息会投递到每个消费者实例。

3、消费模式特点

1、负载均衡消费模式

  消费端集群化部署,每条消息只需要被处理一次;

  每一条消息都只会被分发到一台机器上处理;

  不保证每一次失败重投的消息路由到同一台机器上。

2、集群消费模式

  每条消息都需要被相同逻辑的多台机器处理;

  消息队列 RocketMQ 保证每条消息至少被每台客户端消费一次,但是并不会对消费失败的消息进行失败重投;

  客户端每一次重启都会从最新消息消费。客户端在被停止期间发送至服务端的消息将会被自动跳过;

  广播消费模式下不支持重置消费位点,广播模式下服务端不维护消费进度,所以消息队列 RocketMQ 控制台不支持消息堆积查询、消息堆积报警和订阅关系查询功能;

  广播消费模式下不支持顺序消息。

 

 

四、普通消息的示例demo

  工具类详见:RocketMQ笔记(六):示例代码工具类

1、消息发送封装类 SDKSendMsg

  1 import org.apache.rocketmq.client.exception.MQClientException;
  2 import org.apache.rocketmq.client.producer.DefaultMQProducer;
  3 import org.apache.rocketmq.client.producer.RequestCallback;
  4 import org.apache.rocketmq.client.producer.SendResult;
  5 import org.apache.rocketmq.common.message.Message;
  6 import org.apache.rocketmq.remoting.common.RemotingHelper;
  7 import java.util.Objects;
  8 
  9 /**
 10  * @Description: 发送消息方式
 11  */
 12 public class SDKSendMsg {
 13     // 默认组
 14     private final static String DEFAULT_GROUP_NAME = "test-group";
 15     // 测试地址
 16     private final static String DEFAULT_NAMESRV_ADDR = "192.168.33.55:9876";
 17     // 默认Topic
 18     public final static String DEFAULT_TOPIC = "TestTopic";
 19     // 同步消息的标签与键
 20     public final static String DEFAULT_SYNC_MSG_TAG = "SYNC_TAG";
 21     public final static String DEFAULT_SYNC_MSG_KEY = "SYNC_KEY";
 22     // 异步消息的标签与键
 23     public final static String DEFAULT_ASYNC_MSG_TAG = "ASYNC_TAG";
 24     public final static String DEFAULT_ASYNC_MSG_KEY = "ASYNC_KEY";
 25     // 单向
 26     public final static String DEFAULT_ONEWAY_MSG_TAG = "ONEWAY_TAG";
 27     public final static String DEFAULT_ONEWAY_MSG_KEY = "ONEWAY_KEY";
 28 
 29     // 声明生产者
 30     private DefaultMQProducer producer;
 31 
 32     public SDKSendMsg() {
 33         // 启动生产者实例
 34         try {
 35             // 实例化生产者组名称
 36             DefaultMQProducer producer = new DefaultMQProducer(DEFAULT_GROUP_NAME);
 37             // 指定name server地址
 38             producer.setNamesrvAddr(DEFAULT_NAMESRV_ADDR);
 39             this.producer = producer;
 40             this.producer.start();
 41         } catch (MQClientException e) {
 42             e.printStackTrace();
 43         }
 44     }
 45 
 46     /**
 47      * 关闭生产者实例
 48      */
 49     public void shutdownProducer() {
 50         this.producer.shutdown();
 51     }
 52 
 53 
 54     /**
 55      * 同步发送消息, 使用默认超时时间
 56      * @param topic  主题
 57      * @param msgTag 消息标签
 58      * @param msgKey 消息key
 59      * @param msgBody 消息内容
 60      * @return
 61      */
 62     public SendResult syncSendMsg(String topic, String msgTag, String msgKey, String msgBody) {
 63         return syncSendMsg(topic, msgTag, msgKey, msgBody, null);
 64     }
 65 
 66     /**
 67      * 同步发送消息, 使用指定的超时时间
 68      * @param topic  主题
 69      * @param msgTag 消息标签
 70      * @param msgKey 消息key
 71      * @param msgBody 消息内容
 72      * @param timeout 超时时间
 73      * @return
 74      */
 75     public SendResult syncSendMsg(String topic, String msgTag, String msgKey, String msgBody, Long timeout) {
 76         System.out.println("发送同步消息: " + msgBody);
 77         try {
 78             Message msg = new Message(topic, msgTag, msgKey, msgBody.getBytes(RemotingHelper.DEFAULT_CHARSET));
 79             SendResult send;
 80             if (Objects.isNull(timeout)) {
 81                 send = this.producer.send(msg);
 82                 System.out.printf("消息发送结果:%s%n", send);
 83                 return send;
 84             }
 85             send = this.producer.send(msg, timeout);
 86             System.out.printf("消息发送结果:%s%n", send);
 87             return send;
 88         } catch (Exception e) {
 89             e.printStackTrace();
 90         }
 91         return null;
 92     }
 93 
 94     /**
 95      * 异步发送消息,使用默认的回调处理
 96      * @param topic  主题
 97      * @param msgTag 消息标签
 98      * @param msgKey 消息key
 99      * @param msgBody 消息内容
100      * @param timeout 超时时间
101      * @return
102      */
103     public Message asynSendMsg(String topic, String msgTag, String msgKey, String msgBody, Long timeout) {
104         System.out.println("发送异步消息: " + msgBody);
105         try {
106             Message msg = new Message(topic, msgTag, msgKey, msgBody.getBytes(RemotingHelper.DEFAULT_CHARSET));
107             return this.producer.request(msg, timeout);
108         } catch (Exception e) {
109             e.printStackTrace();
110         }
111         return null;
112     }
113 
114     /**
115      * 异步发送消息,自定义回调处理
116      * @param topic  主题
117      * @param msgTag 消息标签
118      * @param msgKey 消息key
119      * @param msgBody 消息内容
120      * @param timeout 超时时间
121      * @param callback 回调处理
122      */
123     public void asynSendMsg(String topic, String msgTag, String msgKey, String msgBody, Long timeout, RequestCallback callback) {
124         System.out.println("发送异步消息: " + msgBody);
125         try {
126             Message msg = new Message(topic, msgTag, msgKey, msgBody.getBytes(RemotingHelper.DEFAULT_CHARSET));
127             this.producer.request(msg, callback, timeout);
128         } catch (Exception e) {
129             e.printStackTrace();
130         }
131     }
132 
133     /**
134      * 发送单向消息
135      * @param topic
136      * @param msgTag
137      * @param msgKey
138      * @param msgBody
139      */
140     public void sendOneWay(String topic, String msgTag, String msgKey, String msgBody) {
141         System.out.println("发送单向消息: " + msgBody);
142         try {
143             Message msg = new Message(topic, msgTag, msgKey, msgBody.getBytes(RemotingHelper.DEFAULT_CHARSET));
144             this.producer.sendOneway(msg);
145         } catch (Exception e) {
146             e.printStackTrace();
147         }
148     }
149 }

2、生产者 ProducerMsg

 1 import org.apache.rocketmq.client.producer.RequestCallback;
 2 import org.apache.rocketmq.common.message.Message;
 3 /**
 4  * @Description: 发送消息
 5  */
 6 public class ProducerMsg {
 7     public static void main(String[] args) {
 8         // 创建消息发送实例
 9         SDKSendMsg sdkSendMsg = new SDKSendMsg();
10 
11         // 同步消息发送
12         String syncMsg = "同步消息 -- " + 0;
13         sdkSendMsg.syncSendMsg(SDKSendMsg.DEFAULT_TOPIC, SDKSendMsg.DEFAULT_SYNC_MSG_TAG,
14                 SDKSendMsg.DEFAULT_SYNC_MSG_KEY, syncMsg);
15 
16         // 异步消息发送
17         String asynMsg = "异步消息 --" + 1;
18         sdkSendMsg.asynSendMsg(SDKSendMsg.DEFAULT_TOPIC, SDKSendMsg.DEFAULT_ASYNC_MSG_TAG,
19                 SDKSendMsg.DEFAULT_ASYNC_MSG_KEY, asynMsg, 3000l, new RequestCallback() {
20                     @Override
21                     public void onSuccess(Message message) {
22                         System.out.println("异步消息发送成功");
23                         // TODO 业务数据状态更新
24                     }
25                     @Override
26                     public void onException(Throwable throwable) {
27                         System.out.println("异步消息发送失败");
28                         // TODO 业务状态回滚
29                     }
30                 });
31 
32         // 单向消息发送
33         String oneWayMsg = "单向消息 --" + 2;
34         sdkSendMsg.sendOneWay(SDKSendMsg.DEFAULT_TOPIC, SDKSendMsg.DEFAULT_ONEWAY_MSG_TAG,
35                 SDKSendMsg.DEFAULT_ONEWAY_MSG_KEY, oneWayMsg);
36 
37         // 销毁生产者实例
38         sdkSendMsg.shutdownProducer();
39     }
40 }

3、消费者 ProducerMsg

 1 import com.snails.rmq.common.RMQConstant;
 2 import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
 3 import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
 4 import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
 5 import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
 6 import org.apache.rocketmq.client.exception.MQClientException;
 7 import org.apache.rocketmq.common.message.MessageExt;
 8 import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;
 9 
10 import java.io.UnsupportedEncodingException;
11 import java.util.List;
12 
13 /**
14  * @Description: RocketMQ并发消费
15  */
16 public class ConsumerMsg {
17     public static void main(String[] args) throws MQClientException {
18 
19         // 实例化消费者组名称
20         DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(RMQConstant.TEST_GROUP);
21         // 指定name server地址
22         consumer.setNamesrvAddr(RMQConstant.NAEMSRV_ADDR);
23         // 订阅至少一个主题以供消费
24         consumer.subscribe(RMQConstant.TEST_TOPIC, "*");
25         // 负载均衡消费模式
26         consumer.setMessageModel(MessageModel.CLUSTERING);
27         // 广播消费模式
28 //        consumer.setMessageModel(MessageModel.BROADCASTING);
29         //  注册回调,处理从服务端获取的消息
30         consumer.registerMessageListener(new MessageListenerConcurrently() {
31             @Override
32             public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
33                 for (MessageExt msg : msgs) {
34                     System.out.println("当前消息的KEY: " + msg.getKeys());
35                     try {
36                         if (SDKSendMsg.DEFAULT_SYNC_MSG_KEY.equals(msg.getKeys())) {
37                             System.out.println(String.format("线程%s,接收同步消息:%s", Thread.currentThread().getName(), new String(msg.getBody(), "UTF-8")));
38                         } else if (SDKSendMsg.DEFAULT_ASYNC_MSG_KEY.equals(msg.getKeys())) {
39                             System.out.println(String.format("线程%s,接收异步消息:%s", Thread.currentThread().getName(), new String(msg.getBody(), "UTF-8")));
40                         }else if (SDKSendMsg.DEFAULT_ONEWAY_MSG_KEY.equals(msg.getKeys())) {
41                             System.out.println(String.format("线程%s,接收单向消息:%s", Thread.currentThread().getName(), new String(msg.getBody(), "UTF-8")));
42                         }
43                     } catch (UnsupportedEncodingException e) {
44                         // TODO 补偿机制
45                         System.out.println(e.getMessage());
46                     }
47                 }
48                 // 消费消息确认
49                 return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
50             }
51 
52         });
53         // 启动消费者实例
54         consumer.start();
55         System.out.printf("消费者已启动.%n");
56     }
57 }

 

posted @ 2023-05-05 08:36  无虑的小猪  阅读(117)  评论(0编辑  收藏  举报