消息队列RocketMQ版在任何一个环境都是可扩展的,生产者必须是一个集群,消息服务器必须是一个集群,消费者也同样。集群级别的高可用,是消息队列RocketMQ版跟其他的消息服务器的主要区别,消息生产者发送一条消息到消息服务器,消息服务器会随机的选择一个消费者,只要这个消费者消费成功就认为是成功了。
是一个几乎无状态节点,可集群部署,在消息队列RocketMQ版中提供命名服务,更新和发现Broker服务。
消息中转角色,负责存储消息,转发消息。分为Master Broker和Slave Broker,一个Master Broker可以对应多个Slave Broker,但是一个Slave Broker只能对应一个Master Broker。Broker启动后需要完成一次将自己注册至Name Server的操作;随后每隔30s定期向Name Server上报Topic路由信息。
主题是消息队列RocketMQ版中消息传输和存储的顶层容器,用于标识同一类业务逻辑的消息
该模式下创建的Topic在该集群中,所有Broker中的Queue数量都是相同的,可多选
该模式下创建的Topic在该集群中,每个Broker中的Queue数量可以不一样,可多选
即生产者生成消息的时候将消息写入多少个队列中,比如写队列数量有4个:0,1,2,3,那么生产者就会将这些信息写入0,1,2,3中
即消费者消费消息的时候从哪些队列中读取出来,比如读队列数量有4个:0,1,2,3,那么生产者就会将这些信息从0,1,2,3读取出来
Q:假设一个topic在每个broker上面都创建了数量为128的读写队列,若需要将它缩容到64个应该怎么做?
先缩写队列到64,再缩读队列到64,因为在缩写队列到64的时候要保证65-128的消息被消费完,所以读队列必须还是128,等待消息完全被消费完成后,则缩读队列到64
用于设置对当前创建Topic的操作权限:2表示:只写,4表示:只读,6表示:读写。
TopicController主要负责Topic的管理
@RequestMapping(value = "/createOrUpdate.do", method = { RequestMethod.POST})
@ResponseBody
public Object topicCreateOrUpdateRequest(@RequestBody TopicConfigInfo topicCreateOrUpdateRequest) {
Preconditions.checkArgument(CollectionUtils.isNotEmpty(topicCreateOrUpdateRequest.getBrokerNameList()) || CollectionUtils.isNotEmpty(topicCreateOrUpdateRequest.getClusterNameList()),
"clusterName or brokerName can not be all blank");
logger.info("op=look topicCreateOrUpdateRequest={}", JsonUtil.obj2String(topicCreateOrUpdateRequest));
topicService.createOrUpdate(topicCreateOrUpdateRequest);
return true;
}
通过TopicServiceImpl.createOrUpdate()来创建
@Override
public void createOrUpdate(TopicConfigInfo topicCreateOrUpdateRequest) {
TopicConfig topicConfig = new TopicConfig();
BeanUtils.copyProperties(topicCreateOrUpdateRequest, topicConfig);
try {
ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo();
for (String brokerName : changeToBrokerNameSet(clusterInfo.getClusterAddrTable(),
topicCreateOrUpdateRequest.getClusterNameList(), topicCreateOrUpdateRequest.getBrokerNameList())) {
mqAdminExt.createAndUpdateTopicConfig(clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr(), topicConfig);
}
} catch (Exception err) {
throw Throwables.propagate(err);
}
}
通过MQAdminExtImpl.createAndUpdateTopicConfig方法来创建
@Override
public void createAndUpdateTopicConfig(String addr, TopicConfig config)
throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
MQAdminInstance.threadLocalMQAdminExt().createAndUpdateTopicConfig(addr, config);
}
通过调用DefaultMQAdminExtImpl.createAndUpdateTopicConfig创建Topic
public void createAndUpdateTopicConfig(String addr, TopicConfig config) throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
this.mqClientInstance.getMQClientAPIImpl().createTopic(addr, this.defaultMQAdminExt.getCreateTopicKey(), config, this.timeoutMillis);
}
最后通过MQClientAPIImpl.createTopic创建Topic(这里面的方法实际上是生成cmd命令再去执行这些cmd命令)
public void createTopic(String addr, String defaultTopic, TopicConfig topicConfig, long timeoutMillis) throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
CreateTopicRequestHeader requestHeader = new CreateTopicRequestHeader();
requestHeader.setTopic(topicConfig.getTopicName());
requestHeader.setDefaultTopic(defaultTopic);
requestHeader.setReadQueueNums(topicConfig.getReadQueueNums());
requestHeader.setWriteQueueNums(topicConfig.getWriteQueueNums());
requestHeader.setPerm(topicConfig.getPerm());
requestHeader.setTopicFilterType(topicConfig.getTopicFilterType().name());
requestHeader.setTopicSysFlag(topicConfig.getTopicSysFlag());
requestHeader.setOrder(topicConfig.isOrder());
RemotingCommand request = RemotingCommand.createRequestCommand(17, requestHeader);
RemotingCommand response = this.remotingClient.invokeSync(MixAll.brokerVIPChannel(this.clientConfig.isVipChannelEnabled(), addr), request, timeoutMillis);
assert response != null;
switch(response.getCode()) {
case 0:
return;
default:
throw new MQClientException(response.getCode(), response.getRemark());
}
自动创建topic的时候,需要在broker配置文件中添加 autoCreateTopicEnable=true(/conf文件夹下的broker.conf配置文件)当使用自动创建topic的时候会为每个Broker创建4个Queue队列
默认情况下,topic不用手动创建,当producer进行消息发送时,会从nameserver拉取topic的路由信息,如果topic的路由信息不存在,那么会默认拉取broker启动时默认创建好名为“TBW102”的Topic,这定义在org.apache.rocketmq.common.MixAll类中
Broker启动的时候会调用TopicConfigManager的构造方法
public static BrokerController start(BrokerController controller) {
try {
// broker启动调用的方法
controller.start();
...
} catch (Throwable var2) {
...
}
}
public void start() throws Exception {
// 将Broker和Topic的信息定时NameServer发送心跳
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
public void run() {
try {
// 将所有的Broker注册到NameServer中
BrokerController.this.registerBrokerAll(true, false, BrokerController.this.brokerConfig.isForceRegister());
} catch (Throwable var2) {
BrokerController.log.error("registerBrokerAll Exception", var2);
}
}
}, 10000L, (long)Math.max(10000, Math.min(this.brokerConfig.getRegisterNameServerPeriod(), 60000)), TimeUnit.MILLISECONDS);
}
这个方法中定义了一个逻辑判断,如果Broker开启了自动创建Topic的属性,那就把自动创建的Topic以及系统定义的Topic注册到NameServer中,如下图
public TopicConfigManager(BrokerController brokerController) {
...
if (this.brokerController.getBrokerConfig().isAutoCreateTopicEnable()) {
topic = "TBW102";
topicConfig = new TopicConfig(topic);
TopicValidator.addSystemTopic(topic);
topicConfig.setReadQueueNums(this.brokerController.getBrokerConfig().getDefaultTopicQueueNums());
topicConfig.setWriteQueueNums(this.brokerController.getBrokerConfig().getDefaultTopicQueueNums());
int perm = 7;
topicConfig.setPerm(perm);
this.topicConfigTable.put(topicConfig.getTopicName(), topicConfig);
}
...
}
public synchronized void registerBrokerAll(boolean checkOrderConfig, boolean oneway, boolean forceRegister) {
// 在这一步的时候就会执行TopicConfigManager的构造方法,得到TopicConfigTable
TopicConfigSerializeWrapper topicConfigWrapper = this.getTopicConfigManager().buildTopicConfigSerializeWrapper();
if (!PermName.isWriteable(this.getBrokerConfig().getBrokerPermission()) || !PermName.isReadable(this.getBrokerConfig().getBrokerPermission())) {
...
//topicConfigWrapper会将所有的Topic配置表设置进去
topicConfigWrapper.setTopicConfigTable(topicConfigTable);
}
if (forceRegister || this.needRegister(this.brokerConfig.getBrokerClusterName(), this.getBrokerAddr(), this.brokerConfig.getBrokerName(), this.brokerConfig.getBrokerId(), this.brokerConfig.getRegisterBrokerTimeoutMills())) {
// 这里就真正执行把所有的topic都注册到NameServer上
this.doRegisterBrokerAll(checkOrderConfig, oneway, topicConfigWrapper);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1lTaJVb2-1662781345357)(RocketMq/image-20220908180545456.png)]
上述是将配置了自动生成参数的Topic和系统的Topic以及Broker路由信息注册到了NameServer上,那生产者如何向NameServer获取Topic的路由信息?
首先当我们实例化DefaultMQProducer对象的时候,构造方法会帮我们初始化一些参数例如 自动生成Topic的名字,默认消息队列的数量为4等等
public DefaultMQProducer(String namespace, String producerGroup, RPCHook rpcHook) {
this.log = ClientLogger.getLog();
this.retryResponseCodes = new CopyOnWriteArraySet(Arrays.asList(17, 14, 1, 16, 204, 205));
this.createTopicKey = "TBW102";
this.defaultTopicQueueNums = 4;
this.sendMsgTimeout = 3000;
this.compressMsgBodyOverHowmuch = 4096;
this.retryTimesWhenSendFailed = 2;
this.retryTimesWhenSendAsyncFailed = 2;
this.retryAnotherBrokerWhenNotStoreOK = false;
this.maxMessageSize = 4194304;
this.traceDispatcher = null;
this.namespace = namespace;
this.producerGroup = producerGroup;
this.defaultMQProducerImpl = new DefaultMQProducerImpl(this, rpcHook);
}
当producer调用send方法的时候,走的是sendDefaultImpl方法
private SendResult sendDefaultImpl(Message msg, CommunicationMode communicationMode, SendCallback sendCallback, long timeout) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
...
// 尝试从NameServer中获取topic的路由信息
TopicPublishInfo topicPublishInfo = this.tryToFindTopicPublishInfo(msg.getTopic());
...
}
调用DefaultMqProducerImpl中的this.tryToFindTopicPublishInfo()方法
private TopicPublishInfo tryToFindTopicPublishInfo(String topic) {
// 尝试根据主题名称获取topic的信息,如果发送一个从来未发送过的topic,那么这里获取的一定为空
TopicPublishInfo topicPublishInfo = (TopicPublishInfo)this.topicPublishInfoTable.get(topic);
// 如果为空或者发送消息队列的内容为空
if (null == topicPublishInfo || !topicPublishInfo.ok()) {
this.topicPublishInfoTable.putIfAbsent(topic, new TopicPublishInfo());
// 在NameServer中更新Topic路由信息
this.mQClientFactory.updateTopicRouteInfoFromNameServer(topic);
topicPublishInfo = (TopicPublishInfo)this.topicPublishInfoTable.get(topic);
}
// 若该topic没有路由信息 并且 消息队列的内容为空
if (!topicPublishInfo.isHaveTopicRouterInfo() && !topicPublishInfo.ok()) {
// 在NameServer中更新Topic路由信息
this.mQClientFactory.updateTopicRouteInfoFromNameServer(topic, true, this.defaultMQProducer);
topicPublishInfo = (TopicPublishInfo)this.topicPublishInfoTable.get(topic);
return topicPublishInfo;
} else {
return topicPublishInfo;
}
}
调用MQClientInstance的updateTopicRouteInfoFromNameServer()方法
try {
if (!this.lockNamesrv.tryLock(3000L, TimeUnit.MILLISECONDS)) {
} else {
try {
if (isDefault && defaultMQProducer != null) {
// 使用默认的TBW102 Topic获取路由信息
topicRouteData = this.mQClientAPIImpl.getDefaultTopicRouteInfoFromNameServer(defaultMQProducer.getCreateTopicKey(), (long)this.clientConfig.getMqClientApiTimeout());
} else {
// 使用已存在的Topic获取路由信息
topicRouteData = this.mQClientAPIImpl.getTopicRouteInfoFromNameServer(topic, (long)this.clientConfig.getMqClientApiTimeout());
}
// 判断获取默认的是否存在,如果存在把当前的Topic的信息更新。也就是把TBW102 Topic的数据更新为自动创建的数据。
TopicRouteData old = (TopicRouteData)this.topicRouteTable.get(topic);
boolean changed = this.topicRouteDataIsChange(old, topicRouteData);
if (!changed) {
changed = this.isNeedUpdateTopicRouteInfo(topic);
} else {
this.log.info("the topic[{}] route info changed, old[{}] ,new[{}]", new Object[]{topic, old, topicRouteData});
}
// 更新本地的缓存。这样TBW102 Topic的负载和一些默认的路由信息就会被自己创建的Topic使用
TopicRouteData cloneTopicRouteData = topicRouteData.cloneTopicRouteData();
Iterator var8 = topicRouteData.getBrokerDatas().iterator();
if (!this.producerTable.isEmpty()) {
TopicPublishInfo publishInfo = topicRouteData2TopicPublishInfo(topic, topicRouteData);
publishInfo.setHaveTopicRouterInfo(true);
it = this.producerTable.entrySet().iterator();
while(it.hasNext()) {
entry = (Entry)it.next();
MQProducerInner impl = (MQProducerInner)entry.getValue();
if (impl != null) {
impl.updateTopicPublishInfo(topic, publishInfo);
}
}
}
if (!this.consumerTable.isEmpty()) {
Set<MessageQueue> subscribeInfo = topicRouteData2TopicSubscribeInfo(topic, topicRouteData);
it = this.consumerTable.entrySet().iterator();
while(it.hasNext()) {
entry = (Entry)it.next();
MQConsumerInner impl = (MQConsumerInner)entry.getValue();
if (impl != null) {
impl.updateTopicSubscribeInfo(topic, subscribeInfo);
}
}
}
this.topicRouteTable.put(topic, cloneTopicRouteData);
} catch (MQClientException var17) {
} catch (RemotingException var18) {
} finally {
this.lockNamesrv.unlock();
}
return var26;
}
} catch (InterruptedException var20) {
this.log.warn("updateTopicRouteInfoFromNameServer Exception", var20);
}
return false;
}
与Name Server集群中的其中一个节点(随机)建立长连接(Keep-alive),定期从Name Server读取Topic路由信息,并向提供Topic服务的Master Broker建立长连接,且定时向Master Broker发送心跳。
与Name Server集群中的其中一个节点(随机)建立长连接,定期从Name Server拉取Topic路由信息,并向提供Topic服务的Master Broker、Slave Broker建立长连接,且定时向Master Broker、Slave Broker发送心跳。Consumer既可以从Master Broker订阅消息,也可以从Slave Broker订阅消息,订阅规则由Broker配置决定。
消息队列RocketMQ版提供三种方式来发送普通消息:同步(Sync)发送、异步(Async)发送和单向(Oneway)发送。
同步发送是指消息发送方发出一条消息后,会在收到服务端同步响应之后才发下一条消息的通讯方式。
此种方式应用场景非常广泛,例如重要通知邮件、报名短信通知、营销短信系统等
import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.Producer;
import com.aliyun.openservices.ons.api.SendResult;
import com.aliyun.openservices.ons.api.ONSFactory;
import com.aliyun.openservices.ons.api.PropertyKeyConst;
import java.util.Properties;
public class ProducerTest {
public static void main(String[] args) {
Properties properties = new Properties();
// AccessKeyId 阿里云身份验证,在阿里云用户信息管理控制台获取。
properties.put(PropertyKeyConst.AccessKey,"XXX");
// AccessKeySecret 阿里云身份验证,在阿里云用户信息管理控制台获取。
properties.put(PropertyKeyConst.SecretKey, "XXX");
//设置发送超时时间,单位:毫秒。
properties.setProperty(PropertyKeyConst.SendMsgTimeoutMillis, "3000");
// 设置TCP接入域名,进入消息队列RocketMQ版控制台实例详情页面的接入点区域查看。
properties.put(PropertyKeyConst.NAMESRV_ADDR, "XXX");
Producer producer = ONSFactory.createProducer(properties);
// 在发送消息前,必须调用start方法来启动Producer,只需调用一次即可。
producer.start();
//循环发送消息。
for (int i = 0; i < 100; i++){
Message msg = new Message(
// 普通消息所属的Topic,切勿使用普通消息的Topic来收发其他类型的消息。
"TopicTestMQ",
// Message Tag可理解为Gmail中的标签,对消息进行再归类,方便Consumer指定过滤条件在消息队列RocketMQ版的服务器过滤。
// Tag的具体格式和设置方法,请参见Topic与Tag最佳实践。
"TagA",
// Message Body可以是任何二进制形式的数据,消息队列RocketMQ版不做任何干预。
// 需要Producer与Consumer协商好一致的序列化和反序列化方式。
"Hello MQ".getBytes());
// 设置代表消息的业务关键属性,请尽可能全局唯一。
// 以方便您在无法正常收到消息情况下,可通过消息队列RocketMQ版控制台查询消息并补发。
// 注意:不设置也不会影响消息正常收发。
msg.setKey("ORDERID_" + i);
try {
SendResult sendResult = producer.send(msg);
// 同步发送消息,只要不抛异常就是成功。
if (sendResult != null) {
System.out.println(new Date() + " Send mq message success. Topic is:" + msg.getTopic() + " msgId is: " + sendResult.getMessageId());
}
}
catch (Exception e) {
// 消息发送失败,需要进行重试处理,可重新发送这条消息或持久化这条数据进行补偿处理。
System.out.println(new Date() + " Send mq message failed. Topic is:" + msg.getTopic());
e.printStackTrace();
}
}
// 在应用退出前,销毁Producer对象。
// 注意:如果不销毁也没有问题。
producer.shutdown();
}
}
消息消费者示例代码(使用ctrl+点击跳转)
及生产者发送消息到服务端的时候,不需要等待服务端响应即可进行下一个消息的发送接收
异步发送一般用于链路耗时较长,对响应时间较为敏感的业务场景,例如,您视频上传后通知启动转码服务,转码完成后通知推送转码结果等。
import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.OnExceptionContext;
import com.aliyun.openservices.ons.api.Producer;
import com.aliyun.openservices.ons.api.SendCallback;
import com.aliyun.openservices.ons.api.SendResult;
import com.aliyun.openservices.ons.api.ONSFactory;
import com.aliyun.openservices.ons.api.PropertyKeyConst;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
public class ProducerTest {
public static void main(String[] args) throws InterruptedException {
Properties properties = new Properties();
// AccessKeyId 阿里云身份验证,在阿里云服务器管理控制台创建。
properties.put(PropertyKeyConst.AccessKey, "XXX");
// AccessKeySecret 阿里云身份验证,在阿里云服务器管理控制台创建。
properties.put(PropertyKeyConst.SecretKey, "XXX");
//设置发送超时时间,单位毫秒。
properties.setProperty(PropertyKeyConst.SendMsgTimeoutMillis, "3000");
// 设置TCP接入域名,进入消息队列RocketMQ版控制台实例详情页面的接入点区域查看。
properties.put(PropertyKeyConst.NAMESRV_ADDR, "XXX");
Producer producer = ONSFactory.createProducer(properties);
// 在发送消息前,必须调用start方法来启动Producer,只需调用一次即可。
producer.start();
Message msg = new Message(
// 普通消息所属的Topic,切勿使用普通消息的Topic来收发其他类型的消息。
"TopicTestMQ",
// Message Tag,可理解为Gmail中的标签,对消息进行再归类,方便Consumer指定过滤条件在消息队列RocketMQ版的服务器过滤。
"TagA",
// Message Body,任何二进制形式的数据,消息队列RocketMQ版不做任何干预,需要Producer与Consumer协商好一致的序列化和反序列化方式。
"Hello MQ".getBytes());
// 设置代表消息的业务关键属性,请尽可能全局唯一。 以方便您在无法正常收到消息情况下,可通过消息队列RocketMQ版控制台查询消息并补发。
// 注意:不设置也不会影响消息正常收发。
msg.setKey("ORDERID_100");
// 异步发送消息, 发送结果通过callback返回给客户端。
producer.sendAsync(msg, new SendCallback() {
@Override
public void onSuccess(final SendResult sendResult) {
// 消息发送成功。
System.out.println("send message success. topic=" + sendResult.getTopic() + ", msgId=" + sendResult.getMessageId());
}
@Override
public void onException(OnExceptionContext context) {
// 消息发送失败,需要进行重试处理,可重新发送这条消息或持久化这条数据进行补偿处理。
System.out.println("send message failed. topic=" + context.getTopic() + ", msgId=" + context.getMessageId());
}
});
// 阻塞当前线程3秒,等待异步发送结果。
TimeUnit.SECONDS.sleep(3);
// 在应用退出前,销毁Producer对象。注意:如果不销毁也没有问题。
producer.shutdown();
}
}
消息消费者示例代码(使用ctrl+点击跳转)
即生产者发送消息到服务端,完全不需要服务端的响应即可进行下一个消息的发送接收,这样可能导致消息会丢失但是无法知道
适用于某些耗时非常短,但对可靠性要求并不高的场景,例如日志收集。
import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.Producer;
import com.aliyun.openservices.ons.api.ONSFactory;
import com.aliyun.openservices.ons.api.PropertyKeyConst;
import java.util.Properties;
public class ProducerTest {
public static void main(String[] args) {
Properties properties = new Properties();
// AccessKeyId 阿里云身份验证,在阿里云用户信息管理控制台创建。
properties.put(PropertyKeyConst.AccessKey, "XXX");
// AccessKeySecret 阿里云身份验证,在阿里云用户信息管理控制台创建。
properties.put(PropertyKeyConst.SecretKey, "XXX");
//设置发送超时时间,单位:毫秒。
properties.setProperty(PropertyKeyConst.SendMsgTimeoutMillis, "3000");
// 设置TCP接入域名,进入消息队列RocketMQ版控制台的实例详情页面的接入点区域查看。
properties.put(PropertyKeyConst.NAMESRV_ADDR,
"XXX");
Producer producer = ONSFactory.createProducer(properties);
// 在发送消息前,必须调用start方法来启动Producer,只需调用一次即可。
producer.start();
//循环发送消息。
for (int i = 0; i < 100; i++){
Message msg = new Message(
// 普通消息所属的Topic,切勿使用普通消息的Topic来收发其他类型的消息。
"TopicTestMQ",
// Message Tag,
// 可理解为Gmail中的标签,对消息进行再归类,方便Consumer指定过滤条件在消息队列RocketMQ版的服务器过滤。
"TagA",
// Message Body
// 任何二进制形式的数据,消息队列RocketMQ版不做任何干预,需要Producer与Consumer协商好一致的序列化和反序列化方式。
"Hello MQ".getBytes());
// 设置代表消息的业务关键属性,请尽可能全局唯一。
// 以方便您在无法正常收到消息情况下,可通过消息队列RocketMQ版控制台查询消息并补发。
// 注意:不设置也不会影响消息正常收发。
msg.setKey("ORDERID_" + i);
// 由于在oneway方式发送消息时没有请求应答处理,如果出现消息发送失败,则会因为没有重试而导致数据丢失。若数据不可丢,建议选用可靠同步或可靠异步发送方式。
producer.sendOneway(msg);
}
// 在应用退出前,销毁Producer对象。
// 注意:如果不销毁也没有问题。
producer.shutdown();
}
}
消息消费者示例代码(使用ctrl+点击跳转)
发送方式 | 发送TPS | 发送结果反馈 | 可靠性 |
---|---|---|---|
同步发送 | 快 | 有 | 不丢失 |
异步发送 | 快 | 有 | 不丢失 |
单向发送 | 最快 | 无 | 可能丢失 |
延时消息用于指定消息发送到消息队列RocketMQ版的服务端后,延时一段时间才被投递到客户端进行消费(例如3秒后才被消费),适用于解决一些消息生产和消费有时间窗口要求的场景,或者通过消息触发延迟任务的场景,类似于延迟队列。
import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.ONSFactory;
import com.aliyun.openservices.ons.api.Producer;
import com.aliyun.openservices.ons.api.PropertyKeyConst;
import com.aliyun.openservices.ons.api.SendResult;
import java.util.Properties;
public class ProducerDelayTest {
public static void main(String[] args) {
Properties properties = new Properties();
// AccessKey ID阿里云身份验证,在阿里云RAM控制台创建。
properties.put(PropertyKeyConst.AccessKey, "XXX");
// AccessKey Secret阿里云身份验证,在阿里云RAM控制台创建。
properties.put(PropertyKeyConst.SecretKey, "XXX");
// 设置TCP接入域名,进入消息队列RocketMQ版控制台实例详情页面的接入点区域查看。
properties.put(PropertyKeyConst.NAMESRV_ADDR,
"XXX");
Producer producer = ONSFactory.createProducer(properties);
// 在发送消息前,必须调用start方法来启动Producer,只需调用一次即可。
producer.start();
Message msg = new Message(
// 您在消息队列RocketMQ版控制台创建的Topic。
"Topic",
// Message Tag,可理解为Gmail中的标签,对消息进行再归类,方便Consumer指定过滤条件在消息队列RocketMQ版服务器过滤。
"tag",
// Message Body可以是任何二进制形式的数据,消息队列RocketMQ版不做任何干预,需要Producer与Consumer协商好一致的序列化和反序列化方式。
"Hello MQ".getBytes());
// 设置代表消息的业务关键属性,请尽可能全局唯一。
// 以方便您在无法正常收到消息情况下,可通过控制台查询消息并补发。
// 注意:不设置也不会影响消息正常收发。
msg.setKey("ORDERID_100");
try {
// 延时消息,在指定延迟时间(当前时间之后)进行投递。最大可设置延迟40天投递,单位毫秒(ms)。
// 以下示例表示消息在3秒后投递。
long delayTime = System.currentTimeMillis() + 3000;
// 设置消息需要被投递的时间。
msg.setStartDeliverTime(delayTime);
SendResult sendResult = producer.send(msg);
// 同步发送消息,只要不抛异常就是成功。
if (sendResult != null) {
System.out.println(new Date() + " Send mq message success. Topic is:" + msg.getTopic() + " msgId is: " + sendResult.getMessageId());
}
} catch (Exception e) {
// 消息发送失败,需要进行重试处理,可重新发送这条消息或持久化这条数据进行补偿处理。
System.out.println(new Date() + " Send mq message failed. Topic is:" + msg.getTopic());
e.printStackTrace();
}
// 在应用退出前,销毁Producer对象。
// 注意:如果不销毁也没有问题。
producer.shutdown();
}
}
消息消费者示例代码(使用ctrl+点击跳转)
延时消息用于指定消息发送到消息队列RocketMQ版的服务端后,延时一段时间才被投递到客户端进行消费(例如3秒后才被消费),适用于解决一些消息生产和消费有时间窗口要求的场景,或者通过消息触发延迟任务的场景,类似于延迟队列。
import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.ONSFactory;
import com.aliyun.openservices.ons.api.Producer;
import com.aliyun.openservices.ons.api.PropertyKeyConst;
import com.aliyun.openservices.ons.api.SendResult;
import java.util.Properties;
public class ProducerDelayTest {
public static void main(String[] args) {
Properties properties = new Properties();
// AccessKey ID阿里云身份验证,在阿里云RAM控制台创建。
properties.put(PropertyKeyConst.AccessKey, "XXX");
// AccessKey Secret阿里云身份验证,在阿里云RAM控制台创建。
properties.put(PropertyKeyConst.SecretKey, "XXX");
// 设置TCP接入域名,进入消息队列RocketMQ版控制台实例详情页面的接入点区域查看。
properties.put(PropertyKeyConst.NAMESRV_ADDR,
"XXX");
Producer producer = ONSFactory.createProducer(properties);
// 在发送消息前,必须调用start方法来启动Producer,只需调用一次即可。
producer.start();
Message msg = new Message(
// 您在消息队列RocketMQ版控制台创建的Topic。
"Topic",
// Message Tag,可理解为Gmail中的标签,对消息进行再归类,方便Consumer指定过滤条件在消息队列RocketMQ版服务器过滤。
"tag",
// Message Body可以是任何二进制形式的数据,消息队列RocketMQ版不做任何干预,需要Producer与Consumer协商好一致的序列化和反序列化方式。
"Hello MQ".getBytes());
// 设置代表消息的业务关键属性,请尽可能全局唯一。
// 以方便您在无法正常收到消息情况下,可通过控制台查询消息并补发。
// 注意:不设置也不会影响消息正常收发。
msg.setKey("ORDERID_100");
try {
// 延时消息,在指定延迟时间(当前时间之后)进行投递。最大可设置延迟40天投递,单位毫秒(ms)。
// 以下示例表示消息在3秒后投递。
long delayTime = System.currentTimeMillis() + 3000;
// 设置消息需要被投递的时间。
msg.setStartDeliverTime(delayTime);
SendResult sendResult = producer.send(msg);
// 同步发送消息,只要不抛异常就是成功。
if (sendResult != null) {
System.out.println(new Date() + " Send mq message success. Topic is:" + msg.getTopic() + " msgId is: " + sendResult.getMessageId());
}
} catch (Exception e) {
// 消息发送失败,需要进行重试处理,可重新发送这条消息或持久化这条数据进行补偿处理。
System.out.println(new Date() + " Send mq message failed. Topic is:" + msg.getTopic());
e.printStackTrace();
}
// 在应用退出前,销毁Producer对象。
// 注意:如果不销毁也没有问题。
producer.shutdown();
}
}
消息消费者示例代码(使用ctrl+点击跳转)
事务消息是rocketmq的一大特点,其他mq都没有分布式事务的特性
消息队列RocketMQ版提供类似XA或Open XA的分布式事务功能,通过消息队列RocketMQ版事务消息能达到分布式事务的最终一致。
暂不能投递的消息,生产者已经成功地将消息发送到了消息队列RocketMQ版服务端,但是消息队列RocketMQ版服务端未收到生产者对该消息的二次确认,此时该消息被标记成“暂不能投递”状态,处于该种状态下的消息即半事务消息。
由于网络闪断、生产者应用重启等原因,导致某条事务消息的二次确认丢失,消息队列RocketMQ版服务端通过扫描发现某条消息长期处于“半事务消息”时,需要主动向消息生产者询问该消息的最终状态(Commit或是Rollback),该询问过程即消息回查。
TransactionStatus.CommitTransaction
:提交事务,允许消费者消费该消息。TransactionStatus.RollbackTransaction
:回滚事务,消息将被丢弃不允许消费。TransactionStatus.Unknow
:暂时无法判断状态,等待固定时间以后消息队列RocketMQ版服务端根据回查规则向生产者进行消息回查。通过ONSFactory.createTransactionProducer
创建事务消息的Producer时必须指定LocalTransactionChecker
的实现类,处理异常情况下事务消息的回查。
回查规则:本地事务执行完成后,若消息队列RocketMQ版服务端收到的本地事务返回状态为TransactionStatus.Unknow,或生产者应用退出导致本地事务未提交任何状态。则消息队列RocketMQ版服务端会向消息生产者发起事务回查,第一次回查后仍未获取到事务状态,则之后每隔一段时间会再次回查。
回查间隔时间:系统默认每隔30秒发起一次定时任务,对未提交的半事务消息进行回查,共持续12小时。
第一次消息回查最快时间:该参数支持自定义设置。若指定消息未达到设置的最快回查时间前,系统默认每隔30秒一次的回查任务不会检查该消息。以Java为例,以下设置表示:第一次回查的最快时间为60秒。
Message message = new Message();
message.putUserProperties(PropertyKeyConst.CheckImmunityTimeInSeconds,"60");
事务消息的Group ID不能与其他类型消息的Group ID共用。与其他类型的消息不同,事务消息有回查机制,回查时消息队列RocketMQ版服务端会根据Group ID去查询生产者客户端。
发送半事务消息到服务端,及执行本地事务
package com.alibaba.webx.TryHsf.app1;
import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.PropertyKeyConst;
import com.aliyun.openservices.ons.api.SendResult;
import com.aliyun.openservices.ons.api.transaction.LocalTransactionExecuter;
import com.aliyun.openservices.ons.api.transaction.TransactionProducer;
import com.aliyun.openservices.ons.api.transaction.TransactionStatus;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
public class TransactionProducerClient {
private final static Logger log = ClientLogger.getLog(); // 您需要设置自己的日志,便于排查问题。
public static void main(String[] args) throws InterruptedException {
final BusinessService businessService = new BusinessService(); // 本地业务。
Properties properties = new Properties();
// 您在消息队列RocketMQ版控制台创建的Group ID。注意:事务消息的Group ID不能与其他类型消息的Group ID共用。
properties.put(PropertyKeyConst.GROUP_ID,"XXX");
// AccessKey ID阿里云身份验证,在阿里云RAM控制台创建。
properties.put(PropertyKeyConst.AccessKey,"XXX");
// AccessKey Secret阿里云身份验证,在阿里云RAM控制台创建。
properties.put(PropertyKeyConst.SecretKey,"XXX");
// 设置TCP接入域名,进入消息队列RocketMQ版控制台实例详情页面的接入点区域查看。
properties.put(PropertyKeyConst.NAMESRV_ADDR,"XXX");
TransactionProducer producer = ONSFactory.createTransactionProducer(properties,
new LocalTransactionCheckerImpl());
producer.start();
Message msg = new Message("Topic","TagA","Hello MQ transaction===".getBytes());
try {
SendResult sendResult = producer.send(msg, new LocalTransactionExecuter() {
@Override
public TransactionStatus execute(Message msg, Object arg) {
// 消息ID(有可能消息体一样,但消息ID不一样,当前消息属于半事务消息,所以消息ID在消息队列RocketMQ版控制台无法查询)。
String msgId = msg.getMsgID();
// 消息体内容进行crc32,也可以使用其它的如MD5。
long crc32Id = HashUtil.crc32Code(msg.getBody());
// 消息ID和crc32id主要是用来防止消息重复。
// 如果业务本身是幂等的,可以忽略,否则需要利用msgId或crc32Id来做幂等。
// 如果要求消息绝对不重复,推荐做法是对消息体使用crc32或MD5来防止重复消息。
Object businessServiceArgs = new Object();
TransactionStatus transactionStatus = TransactionStatus.Unknow;
try {
boolean isCommit =
businessService.execbusinessService(businessServiceArgs);
if (isCommit) {
// 本地事务已成功则提交消息。
transactionStatus = TransactionStatus.CommitTransaction;
} else {
// 本地事务已失败则回滚消息。
transactionStatus = TransactionStatus.RollbackTransaction;
}
} catch (Exception e) {
log.error("Message Id:{}", msgId, e);
}
System.out.println(msg.getMsgID());
log.warn("Message Id:{}transactionStatus:{}", msgId, transactionStatus.name());
return transactionStatus;
}
}, null);
}
catch (Exception e) {
// 消息发送失败,需要进行重试处理,可重新发送这条消息或持久化这条数据进行补偿处理。
System.out.println(new Date() + " Send mq message failed. Topic is:" + msg.getTopic());
e.printStackTrace();
}
// demo example防止进程退出(实际使用不需要这样)。
TimeUnit.MILLISECONDS.sleep(Integer.MAX_VALUE);
}
}
消息消费者示例代码(使用ctrl+点击跳转)
都是去实现LocalTransactionChecker这个接口的check方法实现回查
public class LocalTransactionCheckerImpl implements LocalTransactionChecker {
private final static Logger log = ClientLogger.getLog();
final BusinessService businessService = new BusinessService();
@Override
public TransactionStatus check(Message msg) {
//消息ID(有可能消息体一样,但消息ID不一样,当前消息属于半事务消息,所以消息ID在消息队列RocketMQ版控制台无法查询)。
String msgId = msg.getMsgID();
//消息体内容进行crc32,也可以使用其它的方法如MD5。
long crc32Id = HashUtil.crc32Code(msg.getBody());
//消息ID和crc32Id主要是用来防止消息重复。
//如果业务本身是幂等的,可以忽略,否则需要利用msgId或crc32Id来做幂等。
//如果要求消息绝对不重复,推荐做法是对消息体使用crc32或MD5来防止重复消息。
//业务自己的参数对象,这里只是一个示例,需要您根据实际情况来处理。
Object businessServiceArgs = new Object();
TransactionStatus transactionStatus = TransactionStatus.Unknow;
try {
boolean isCommit = businessService.checkbusinessService(businessServiceArgs);
if (isCommit) {
//本地事务已成功则提交消息。
transactionStatus = TransactionStatus.CommitTransaction;
} else {
//本地事务已失败则回滚消息。
transactionStatus = TransactionStatus.RollbackTransaction;
}
} catch (Exception e) {
log.error("Message Id:{}", msgId, e);
}
log.warn("Message Id:{}transactionStatus:{}", msgId, transactionStatus.name());
return transactionStatus;
}
}
顺序消息是消息队列RocketMQ版提供的一种对消息发送和消费顺序有严格要求的消息。对于一个指定的Topic,消息严格按照先进先出(FIFO)的原则进行消息发布和消费,即先发布的消息先消费,后发布的消息后消费。顺序消息主要分为分区顺序消息和全局顺序消息
对于指定的一个Topic,所有消息根据Sharding Key进行区块分区,同一个分区内的消息按照严格的先进先出(FIFO)原则进行发布和消费。同一分区内的消息保证顺序,不同分区之间的消息顺序不做要求。
基本概念
适用场景
适用于性能要求高,以Sharding Key作为分区字段,在同一个区块中严格地按照先进先出(FIFO)原则进行消息发布和消费的场景。
示例
对于指定的一个Topic,所有消息按照严格的先入先出(FIFO)的顺序来发布和消费。
适用场景
适用于性能要求不高,所有的消息严格按照FIFO原则来发布和消费的场景。
示例
在证券处理中,以人民币兑换美元为Topic,在价格相同的情况下,先出价者优先处理,则可以按照FIFO的方式发布和消费全局顺序消息。
package com.aliyun.openservices.ons.example.order;
import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.ONSFactory;
import com.aliyun.openservices.ons.api.PropertyKeyConst;
import com.aliyun.openservices.ons.api.SendResult;
import com.aliyun.openservices.ons.api.order.OrderProducer;
import java.util.Properties;
public class ProducerClient {
public static void main(String[] args) {
Properties properties = new Properties();
// 您在消息队列RocketMQ版控制台创建的Group ID。
properties.put(PropertyKeyConst.GROUP_ID,"XXX");
// AccessKey ID阿里云身份验证,在阿里云RAM控制台创建。
properties.put(PropertyKeyConst.AccessKey,"XXX");
// AccessKey Secret阿里云身份验证,在阿里云RAM控制台创建。
properties.put(PropertyKeyConst.SecretKey,"XXX");
// 设置TCP接入域名,进入消息队列RocketMQ版控制台实例详情页面的接入点区域查看。
properties.put(PropertyKeyConst.NAMESRV_ADDR,"XXX");
OrderProducer producer = ONSFactory.createOrderProducer(properties);
// 在发送消息前,必须调用start方法来启动Producer,只需调用一次即可。
producer.start();
for (int i = 0; i < 1000; i++) {
String orderId = "biz_" + i % 10;
Message msg = new Message(
// Message所属的Topic。
"Order_global_topic",
// Message Tag,可理解为Gmail中的标签,对消息进行再归类,方便Consumer指定过滤条件在消息队列RocketMQ版的服务器过滤。
"TagA",
// Message Body,可以是任何二进制形式的数据,消息队列RocketMQ版不做任何干预,需要Producer与Consumer协商好一致的序列化和反序列化方式。
"send order global msg".getBytes()
);
// 设置代表消息的业务关键属性,请尽可能全局唯一。
// 以方便您在无法正常收到消息情况下,可通过消息队列RocketMQ版控制台查询消息并补发。
// 注意:不设置也不会影响消息正常收发。
msg.setKey(orderId);
// 分区顺序消息中区分不同分区的关键字段,Sharding Key与普通消息的key是完全不同的概念。
// 全局顺序消息,该字段可以设置为任意非空字符串。
String shardingKey = String.valueOf(orderId);
try {
// 同步发送
SendResult sendResult = producer.send(msg, shardingKey);
// 发送消息,只要不抛异常就是成功。
if (sendResult != null) {
System.out.println(new Date() + " Send mq message success. Topic is:" + msg.getTopic() + " msgId is: " + sendResult.getMessageId());
}
}
catch (Exception e) {
// 消息发送失败,需要进行重试处理,可重新发送这条消息或持久化这条数据进行补偿处理。
System.out.println(new Date() + " Send mq message failed. Topic is:" + msg.getTopic());
e.printStackTrace();
}
}
// 在应用退出前,销毁Producer对象。
// 注意:如果不销毁也没有问题。
producer.shutdown();
}
}
package com.aliyun.openservices.ons.example.order;
import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.ONSFactory;
import com.aliyun.openservices.ons.api.PropertyKeyConst;
import com.aliyun.openservices.ons.api.order.ConsumeOrderContext;
import com.aliyun.openservices.ons.api.order.MessageOrderListener;
import com.aliyun.openservices.ons.api.order.OrderAction;
import com.aliyun.openservices.ons.api.order.OrderConsumer;
import java.util.Properties;
public class ConsumerClient {
public static void main(String[] args) {
Properties properties = new Properties();
// 您在消息队列RocketMQ版控制台创建的Group ID。
properties.put(PropertyKeyConst.GROUP_ID,"XXX");
// AccessKey ID阿里云身份验证,在阿里云RAM控制台创建。
properties.put(PropertyKeyConst.AccessKey,"XXX");
// AccessKey Secret阿里云身份验证,在阿里云RAM控制台创建。
properties.put(PropertyKeyConst.SecretKey,"XXX");
// 设置TCP接入域名,进入消息队列RocketMQ版控制台实例详情页面的接入点区域查看。
properties.put(PropertyKeyConst.NAMESRV_ADDR,"XXX");
// 顺序消息消费失败进行重试前的等待时间,单位(毫秒),取值范围: 10毫秒~30,000毫秒。
properties.put(PropertyKeyConst.SuspendTimeMillis,"100");
// 消息消费失败时的最大重试次数。
properties.put(PropertyKeyConst.MaxReconsumeTimes,"20");
// 在订阅消息前,必须调用start方法来启动Consumer,只需调用一次即可。
OrderConsumer consumer = ONSFactory.createOrderedConsumer(properties);
consumer.subscribe(
// Message所属的Topic。
"Order_global_topic",
// 订阅指定Topic下的Tags:
// 1. * 表示订阅所有消息。
// 2. TagA || TagB || TagC表示订阅TagA或TagB或TagC的消息。
"*",
new MessageOrderListener() {
/**
* 1. 消息消费处理失败或者处理出现异常,返回OrderAction.Suspend。
* 2. 消息处理成功,返回OrderAction.Success。
*/
@Override
public OrderAction consume(Message message, ConsumeOrderContext context) {
System.out.println(message);
return OrderAction.Success;
}
});
consumer.start();
}
}
消息类型 | 是否支持可靠同步发送 | 是否支持可靠异步发送 | 是否支持单向发送 | 是否支持多线程发送 | 性能 |
---|---|---|---|---|---|
普通消息 | 是 | 是 | 是 | 是 | 最高 |
事务消息 | 是 | 是 | 是 | 是 | 最高 |
定时和延时消息 | 是 | 是 | 是 | 是 | 最高 |
分区顺序消息 | 是 | 否 | 否 | 否 | 高 |
全局顺序消息 | 是 | 否 | 否 | 否 | 一般 |
同一个Group ID所标识的所有Consumer平均分摊消费消息。例如某个Topic有9条消息,一个Group ID有3个Consumer实例,那么在集群消费模式下每个实例平均分摊,只消费其中的3条消息。设置方式如下所示。
// 集群订阅方式设置(不设置的情况下,默认为集群订阅方式)。
properties.put(PropertyKeyConst.MessageModel, PropertyValueConst.CLUSTERING);
同一个Group ID所标识的所有Consumer都会各自消费某条消息一次。例如某个Topic有9条消息,一个Group ID有3个Consumer实例,那么在广播消费模式下每个实例都会各自消费9条消息。设置方式如下所示。
// 广播订阅方式设置。
properties.put(PropertyKeyConst.MessageModel, PropertyValueConst.BROADCASTING);
消息由消息队列RocketMQ版推送至Consumer。Push方式下,消息队列RocketMQ版还支持批量消费功能,可以将批量消息统一推送至Consumer进行消费
import com.aliyun.openservices.ons.api.Action;
import com.aliyun.openservices.ons.api.ConsumeContext;
import com.aliyun.openservices.ons.api.Consumer;
import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.MessageListener;
import com.aliyun.openservices.ons.api.ONSFactory;
import com.aliyun.openservices.ons.api.PropertyKeyConst;
import java.util.Properties;
public class ConsumerTest {
public static void main(String[] args) {
Properties properties = new Properties();
// 您在控制台创建的Group ID。
properties.put(PropertyKeyConst.GROUP_ID, "XXX");
// AccessKey ID阿里云身份验证,在阿里云RAM控制台创建。
properties.put(PropertyKeyConst.AccessKey, "XXX");
// Accesskey Secret阿里云身份验证,在阿里云服RAM控制台创建。
properties.put(PropertyKeyConst.SecretKey, "XXX");
// 设置TCP接入域名,进入控制台的实例详情页面的TCP协议客户端接入点区域查看。
properties.put(PropertyKeyConst.NAMESRV_ADDR, "XXX");
// 集群订阅方式(默认)。
// properties.put(PropertyKeyConst.MessageModel, PropertyValueConst.CLUSTERING);
// 广播订阅方式。
// properties.put(PropertyKeyConst.MessageModel, PropertyValueConst.BROADCASTING);
Consumer consumer = ONSFactory.createConsumer(properties);
consumer.subscribe("TopicTestMQ", "TagA||TagB", new MessageListener() { //订阅多个Tag。
public Action consume(Message message, ConsumeContext context) {
System.out.println("Receive: " + message);
return Action.CommitMessage;
}
});
//订阅另外一个Topic,如需取消订阅该Topic,请删除该部分的订阅代码,重新启动消费端即可。
consumer.subscribe("TopicTestMQ-Other", "*", new MessageListener() { //订阅全部Tag。
public Action consume(Message message, ConsumeContext context) {
System.out.println("Receive: " + message);
return Action.CommitMessage;
}
});
consumer.start();
System.out.println("Consumer Started");
}
}
import com.aliyun.openservices.ons.api.Action;
import com.aliyun.openservices.ons.api.ConsumeContext;
import com.aliyun.openservices.ons.api.Consumer;
import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.MessageListener;
import com.aliyun.openservices.ons.api.ONSFactory;
import com.aliyun.openservices.ons.api.PropertyKeyConst;
import java.util.Properties;
public class ConsumerTest {
public static void main(String[] args) {
Properties properties = new Properties();
// 您在控制台创建的Group ID。
properties.put(PropertyKeyConst.GROUP_ID, "XXX");
// AccessKey ID阿里云身份验证,在阿里云RAM控制台创建。
properties.put(PropertyKeyConst.AccessKey, "XXX");
// Accesskey Secret阿里云身份验证,在阿里云服RAM控制台创建。
properties.put(PropertyKeyConst.SecretKey, "XXX");
// 设置TCP接入域名,进入控制台的实例详情页面的TCP协议客户端接入点区域查看。
properties.put(PropertyKeyConst.NAMESRV_ADDR, "XXX");
// 集群订阅方式(默认)。
// properties.put(PropertyKeyConst.MessageModel, PropertyValueConst.CLUSTERING);
// 广播订阅方式。
// properties.put(PropertyKeyConst.MessageModel, PropertyValueConst.BROADCASTING);
Consumer consumer = ONSFactory.createConsumer(properties);
consumer.subscribe("TopicTestMQ", "TagA||TagB", new MessageListener() { //订阅多个Tag。
public Action consume(Message message, ConsumeContext context) {
System.out.println("Receive: " + message);
return Action.CommitMessage;
}
});
//订阅另外一个Topic,如需取消订阅该Topic,请删除该部分的订阅代码,重新启动消费端即可。
consumer.subscribe("TopicTestMQ-Other", "*", new MessageListener() { //订阅全部Tag。
public Action consume(Message message, ConsumeContext context) {
System.out.println("Receive: " + message);
return Action.CommitMessage;
}
});
consumer.start();
System.out.println("Consumer Started");
}
}
消息由Consumer主动从消息队列RocketMQ版拉取
功能 | 集群消费 | 广播消费 |
---|---|---|
TCP协议SDK | √ | √ |
HTTP协议SDK | √ | × |
顺序消息 | √ | × |
重置消费位点 | √ | × |
消息重试 | √ | × |
消息堆积查询、报警 | √ | × |
订阅关 | √ | × |
消费进度 | 服务端维护可靠性更高,客户端重启后,将按照上次的消费进度继续消费。支持服务端消费重试机制,详细信息,请参见消息重试。 | 客户端维护出现重复消费的概率稍大于集群模式,客户端每次重启都会从最新消息消费。 |
public Message() {
}
public Message(String topic, byte[] body) {
this(topic, "", "", 0, body, true);
}
public Message(String topic, String tags, String keys, int flag, byte[] body, boolean waitStoreMsgOK) {
this.topic = topic;
this.flag = flag;
this.body = body;
if (tags != null && tags.length() > 0) {
this.setTags(tags);
}
if (keys != null && keys.length() > 0) {
this.setKeys(keys);
}
this.setWaitStoreMsgOK(waitStoreMsgOK);
}
public Message(String topic, String tags, byte[] body) {
this(topic, tags, "", 0, body, true);
}
public Message(String topic, String tags, String keys, byte[] body) {
this(topic, tags, keys, 0, body, true);
}
生产者发送给服务端的一个消息的主题标识,消费者可以通过这个标识消费消息
也是相当于对消息的标识,能够让消费者过滤自己想要的tags,例如消费者只消费【用户购买的消息】user:purchase的消息
所有消息的唯一标识,以方便在无法正常收到消息情况下,可通过消息队列RocketMQ版控制台查询消息并补发。用于建立索引,之后可以通过命令工具/API/或者管理平台查询key,可以为一个消息设置多个key,用空格""进行分割
选填,消息的标记,完全由应用设置,RocketMQ不做任何处理,类似于memcached中flag的作用
消息内容,是字节数组
表示发送消息后,是否需要等待消息同步刷新到磁盘上。如果broker配置为ASYNC_MASTER,那么只需要消息在master上刷新到磁盘即可;如果配置为SYNC_MASTER,那么还需要等待slave也刷新到磁盘。需要注意的是,waitStoreMsgOK默认为false,只有将设置为true的情况下,才会等待刷盘成功再返回。