本篇以应用角度讲解RocketMQ的本地安装、启动与简单配置,以及各主要类的功能及使用方式。更多其它了解请参考:
RocketMQ系列(一)——基础篇
RocketMQ系列(三)——原理篇
一、本地安装与部署
以windows平台为例,Linux、Mac OS类似
1、安装包下载地址
https://mirror.bit.edu.cn/apache/rocketmq/4.7.1/rocketmq-all-4.7.1-bin-release.zip
解压后即可使用,记得配置环境变量 ROCKETMQ_HOME=${yourPath}\rocketmq-all-4.2.0-bin-release以及加入到CLASSPATH
2、启动
cd %ROCKETMQ_HOME%\bin
start mqnamesrv.cmd # 首先需要启动NameServer
start mqbroker.cmd -n 127.0.0.1:9876 autoCreateTopicEnable=true # 启动broker并设置可以自动创建topic
3、配置文件
官方默认提供了4中broker的部署方式,分别是单节点、双主从同步双写(2m-2s-sync)、双主双从异步复制(2m-2s-async)和双主模式(2m-noslave),启动各个broker时可以在 mqbroker.cmd -c 参数后面跟配置文件指定部署模式。
每个默认配置项的含义:
配置项 | 含义 |
---|---|
brokerClusterName=DefaultCluster | 集群名称,相同的brokerClusterName组成集群 |
brokerId=0 | 节点id,0表示master,其它表示slave |
brokerName=broker-a | 节点名称,相同节点名的多个节点为主从关系 |
brokerRole=ASYNC_MASTER | 节点角色,SYNC_MASTER:同步双写的master,ASYNC_MASTER:异步复制的master,SLAVE:从节点 |
deleteWhen=04 | 在每天的几点删除已经超过文件保留时间的commit log |
fileReservedTime=48 | 消息保存的时间,小时为单位 |
flushDiskType=ASYNC_FLUSH | 持久化方式,SYNC_FLUSH:同步刷盘,ASYNC_FLUSH:异步刷盘 |
二、主要类介绍
1、生产者相关
DefaultMQProducer
默认生产者,应用程序通过各种重载的send和sendOneway方法发送消息,此外还可以创建topic;一个DefaultMQProducer实例必须且只能属于某一个producer group,可以向多个topic发送消息,一个producer group在同一套配置中也只能有一个producer实例,具体原因在系列三的原理篇会讲到。
TransactionMQProducer
事务消息生产者,继承自DefaultMQProducer,除了DefaultMQProducer提供的方法外,还可以通过sendMessageInTransaction方法发送事务消息,这要求注册一个事务监听接口(TransactionListener)用于完成本地事务的执行和执行状态判断。
SendCallBack
异步发送回调接口,定义了异步发送消息完成后的执行操作。
public interface SendCallback {
// 发送完成(注意有别于发送成功)回调方法
void onSuccess(final SendResult sendResult);
// 发送异常回调方法
void onException(final Throwable e);
}
RPCHook
顾名思义,rpc调用钩子,可以在客户端向服务端(这里指broker,下同)发起调用前(doBeforeRequest)后(doAfterResponse)执行特定操作,比如打印日志等;同样也适用于消费者
MessageQueueSelector
消息队列选择器,选择消息需要发送的队列,可以用于负载均衡和消息局部有序场景,如轮询队列或根据订单号取模选择发送队列;不传默认使用MQFaultFallbackStrategy选择队列
public interface MessageQueueSelector {
// 从消息列表mqs中选择一个队列发送msg消息
MessageQueue select(final List<MessageQueue> mqs, final Message msg, final Object arg);
}
TransactionListener
事务监听接口,用于事务消息发送时服务端回调客户端以执行本地事务或回查本地事务状态(LocalTransactionState )
public interface TransactionListener {
// 服务端通知客户端执行本地事务
LocalTransactionState executeLocalTransaction(final Message msg, final Object arg);
// 服务端在调用executeLocalTransaction超时后,回查本地事务执行状态
LocalTransactionState checkLocalTransaction(final MessageExt msg);
}
MessageQueue
消息队列实体类,包含所属topic,所在的broker name和队列id
Message
消息实体类,主要包含所属topic,应用程序需要发送的消息体body,以及Map类型的属性集合properties
在properties中保存的属性大致可以分为三类:
TAGS、KEYS、DELAY等预定义业务属性,用户可以通过接口访问
RETRY_TOPIC、REAL_TOPIC、TRAN_MSG等预定义系统属性,用于特定功能的实现
用户自定义属性,如设置age=8,amount=1000等,用于消息过滤,见系列一基础篇的SQL表达式过滤模式
状态相关
SendResult
同步发送消息时会立即返回发送结果,包含发送状态对象SendStatus
SendStatus
服务端返回的消息投递结果,对于事务消息,后三种情况下客户端会要求服务端回滚消息
枚举取值 | 解释 | 说明 |
---|---|---|
SEND_OK | 发送成功 | 服务端事务消息提交 |
FLUSH_DISK_TIMEOUT | 刷盘超时 | 服务端事务消息回滚 |
FLUSH_SLAVE_TIMEOUT | 同步slave超时 | 服务端事务消息回滚 |
SLAVE_NOT_AVAILABLE | 没有可用的slave | 服务端事务消息回滚 |
LocalTransactionState
本地事务状态,事务监听接口返回该状态
枚举取值 | 解释 |
---|---|
COMMIT_MESSAGE | 服务端提交消息 |
ROLLBACK_MESSAGE | 服务端回滚消息 |
UNKNOW | 未决状态,返回该状态后服务端会再次回查 |
2、消费者相关
DefaultMQPushConsumer
默认的push方式消费者,通过注册MessageListener实现类定义消费业务逻辑
DefaultMQPullConsumer
默认的pull方式消费者,可以获取到某topic下的消息队列并主动拉取消息进行消费
MessageFilter
消息过滤器接口,自定义消息过滤规则,哪些消息可以推送到消费者
public interface MessageFilter {
boolean match(final MessageExt msg, final FilterContext context);
}
AllocateMessageQueueStrategy
消息队列分配策略,定义同一个topic下的消息队列(注意不是消息,通常一个消息队列只由同一个consumer消费)列表在不同consumer之间如何分配,在创建consumer时可指定。
很明显,该策略只在集群模式下才会使用,广播模式下每个消费者都需要消费所有队列。
官方提供了6种实现,具体实现策略戳这里,默认策略是AllocateMessageQueueAveragely,接口定义如下:
public interface AllocateMessageQueueStrategy {
// 返回当前consumer消费的队列
List<MessageQueue> allocate(
final String consumerGroup, // 消费者组
final String currentCID, // 当前consumer id
final List<MessageQueue> mqAll, // 当前topic下的全部消息队列
final List<String> cidAll // 该consumer group下的所有consumer id列表
);
// 返回策略名称
String getName();
}
MessageListener
消息监听接口,有MessageListenerConcurrently和MessageListenerOrderly两种扩展方式
MessageListenerConcurrently
并发消息监听接口,可以批量获取消息,不保证消息的接收和消费顺序;返回并发消费状态ConsumeConcurrentlyStatus
public interface MessageListenerConcurrently extends MessageListener {
// 并发消费消息方法声明
ConsumeConcurrentlyStatus consumeMessage(final List<MessageExt> msgs, final ConsumeConcurrentlyContext context);
}
MessageListenerOrderly
顺序消息监听接口,单线程保证消息的消费顺序;返回顺序消费状态ConsumeOrderlyStatus
public interface MessageListenerOrderly extends MessageListener {
// 顺序消费消息方法声明
ConsumeOrderlyStatus consumeMessage(final List<MessageExt> msgs, final ConsumeOrderlyContext context);
}
类型/状态相关
MessageModel
消费者的消息模式
枚举取值 | 解释 | 说明 |
---|---|---|
BROADCASTING | 广播模式 | 消息偏移量保存在本地 没有负载均衡,消费时不存在队列选择问题 |
CLUSTERING | 集群模式 | 消息偏移量保存在服务端 push模式下有负载均衡问题,消费时可以指定消息队列选择器 pull模式下可以拉取全部队列,也可以均衡拉取队列进行消费 |
ConsumeFromWhere
定义消费者启动后从何处开始消费
枚举取值 | 解释 |
---|---|
CONSUME_FROM_LAST_OFFSET | 从最新的偏移量开始消费 |
CONSUME_FROM_FIRST_OFFSET | 从队列头开始消费 |
CONSUME_FROM_TIMESTAMP | 从某个时刻开始消费 |
ConsumeConcurrentlyStatus
并发消费状态
枚举取值 | 解释 |
---|---|
CONSUME_SUCCESS | 消费成功 |
ECONSUME_LATER | 消费失败,会重新投递到%RETRY%+${consumergroup}队列 |
ConsumeOrderlyStatus
顺序消费状态
枚举取值 | 解释 |
---|---|
SUCCESS | 消费成功 |
SUSPEND_CURRENT_QUEUE_A_MOMENT | 消费失败,稍后会重新消费 |
PullResult
拉取消息返回结果,包含拉取状态PullStatus
PullStatus
拉取结果
枚举取值 | 解释 |
---|---|
FOUND | 拉取成功 |
NO_NEW_MSG | 没有新消息 |
NO_MATCHED_MSG | 没有匹配的消息 |
OFFSET_ILLEGAL | 偏移量不合法 |
三、生产者
1、DefaultMQProducer
初始化并启动
创建并启动DefaultMQProducer
private DefaultMQProducer createDefaultProducer() throws MQClientException {
DefaultMQProducer defaultMQProducer = new DefaultMQProducer(defaultProducerGroup, new RPCHook() {
@Override
public void doBeforeRequest(String remoteAddr, RemotingCommand request) {
// TODO 向服务端发起请求前执行操作
}
@Override
public void doAfterResponse(String remoteAddr, RemotingCommand request, RemotingCommand response) {
// TODO 服务端发起请求返回后执行操作
}
});
defaultMQProducer.setNamesrvAddr(namesrvAddr);
// 默认队列数量
defaultMQProducer.setDefaultTopicQueueNums(2);
// 发送超时时间
defaultMQProducer.setSendMsgTimeout(100);
// 发送失败最大重试次数
defaultMQProducer.setRetryTimesWhenSendFailed(2);
defaultMQProducer.start();
return defaultMQProducer;
}
发送消息
// 单条消息发送
SendResult sendResult = defaultMQProducer.send(message);
// 批量消息发送
SendResult sendResult = defaultMQProducer.send(Lists.newArrayList(message));
// 设置发送超时时间
SendResult sendResult = defaultMQProducer.send(message, timeout);
// 指定消息队列
SendResult sendResult = defaultMQProducer.send(message, messageQueue);
// 指定消息队列选择器,可以实现相关消息的顺序发送
SendResult sendResult = defaultMQProducer.send(message, messageQueueSelector, null);
// 异步发送并指定回调接口
defaultMQProducer.send(message, sendCallback);
// 单向发送
defaultMQProducer.sendOneway(message);
// 单向发送并指定消息队列
defaultMQProducer.sendOneway(message, messageQueue);
// 单向发送并指定消息队列选择器
defaultMQProducer.sendOneway(message, messageQueueSelector, key);
……
以上发送方式的组合
2、TransactionMQProducer
初始化并启动
创建并启动TransactionMQProducer
private TransactionMQProducer createTransactionProducer() throws MQClientException {
TransactionMQProducer transactionMQProducer = new TransactionMQProducer(transactionProducerGroup);
transactionMQProducer.setNamesrvAddr(namesrvAddr);
transactionMQProducer.setRetryTimesWhenSendFailed(2);
transactionMQProducer.setTransactionListener(new TransactionListener() {
@Override
public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
// TODO 执行本地事务,成功返回COMMIT_MESSAGE,失败返回ROLLBACK_MESSAGE,未决返回UNKNOW
return LocalTransactionState.COMMIT_MESSAGE;
}
@Override
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
// TODO 查询本地事务状态,返回结果同上
return LocalTransactionState.COMMIT_MESSAGE;
}
});
transactionMQProducer.start();
return transactionMQProducer;
}
发送消息
SendResult sendResult = transactionMQProducer.sendMessageInTransaction(message, key);
四、消费者
1、PushConsumer
初始化与启动
public DefaultMQPushConsumer createConcurrentlyPushConsumer() throws Exception {
DefaultMQPushConsumer pushConsumer = new DefaultMQPushConsumer(pushConsumerGroup);
pushConsumer.setNamesrvAddr(namesrvAddr);
pushConsumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
// 过滤方式一、TAG方式
pushConsumer.subscribe(topic1, "TAG-A || TAG-B*");
// 过滤方式二、TSQL方式
pushConsumer.subscribe(topic2, MessageSelector.bySql("amount between 500 and 1000"));
// 过滤方式三、类过滤方式
/// 注意:由于是直接将filter文件直接传送到broker,因此文件中不能有非RocketMQ核心包之外的外部依赖
String filterClassSource = MixAll.file2String("D:\\workspace\\……\\rocketmq\\impl\\MessageFilterImpl.java");
String fullName = MessageFilterImpl.class.getCanonicalName();
pushConsumer.subscribe(topic3, fullName, filterClassSource);
// 集群模式,同一ConsumerGroup中只有一个消费者能消费
pushConsumer.setMessageModel(MessageModel.CLUSTERING);
// 消费线程池最小线程数
pushConsumer.setConsumeThreadMin(2);
// 消费线程池最大线程数
pushConsumer.setConsumeThreadMax(5);
// 批量消费,一次消费多少条消息,也即messageListener方法中msgs的size上限
pushConsumer.setConsumeMessageBatchMaxSize(10);
// 批量拉消息,一次最多拉多少条。问题:为什么push模式下会有拉取消息的操作?
pushConsumer.setPullBatchSize(10);
// 并发消息监听接口
pushConsumer.registerMessageListener(concurrentlyListener);
// 顺序消息监听接口
// pushConsumer.registerMessageListener(orderlyListener);
// 设置消息队列分配策略
pushConsumer.setAllocateMessageQueueStrategy(new AllocateMessageQueueAveragely());
pushConsumer.start();
return pushConsumer;
}
消费
Consumer消费消息是通过自定义MessageListener实现的。
1、并发消费,不保证实际的消费顺序
public class ConcurrentlyListener implements MessageListenerConcurrently {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
try {
msgs.forEach(msg -> {
// TODO 消费业务逻辑
});
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
} catch (Exception e) {
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
}
}
}
2、顺序消费,同一队列的消息只有一个线程消费以保证顺序,需要配合producer的MessageQueueSelector使用
public class OrderlyListener implements MessageListenerOrderly {
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
try {
msgs.forEach(msg -> {
// TODO 消费业务逻辑
});
return ConsumeOrderlyStatus.SUCCESS;
} catch (Exception e) {
return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;
}
}
}
2、PullConsumer
初始化与启动
public DefaultMQPullConsumer createPullConsumer() throws Exception {
DefaultMQPullConsumer pullConsumer = new DefaultMQPullConsumer(pullConsumerGroup);
pullConsumer.setNamesrvAddr(namesrvAddr);
pullConsumer.setMessageModel(MessageModel.CLUSTERING);
pullConsumer.start();
return pullConsumer;
}
消费
private void consume() throws Exception {
// 获取topic下的全部队列
Set<MessageQueue> queues = pullConsumer.fetchSubscribeMessageQueues(topic);
// 均衡获取topic下的部分队列
// Set<MessageQueue> queues = pullConsumer.fetchMessageQueuesInBalance(topic);
for (MessageQueue queue : queues) {
SINGLE_MQ:
for (; ; ) {
PullResult pullResult = pullConsumer.pullBlockIfNotFound(queue, "", getOffset(queue) /* 获取队列的消费位移 */, 32);
PullStatus pullStatus = pullResult.getPullStatus();
// 本地记录当前queue的消费位移
setOffset(queue, pullResult.getNextBeginOffset());
switch (pullStatus) {
case FOUND:
List<MessageExt> messageExts = pullResult.getMsgFoundList();
log.info("pull success, msg size: ", messageExts.size());
for (MessageExt messageExt : pullResult.getMsgFoundList()) {
// TODO 业务处理逻辑
}
break;
case NO_NEW_MSG:
break SINGLE_MQ;
case NO_MATCHED_MSG:
case OFFSET_ILLEGAL:
default:
break;
}
}
}
}
【参考资料】
https://blog.youkuaiyun.com/yewandemty/article/details/81989695
https://www.jianshu.com/p/75badea5ac1e