RocketMQ系列(二)——应用篇

本篇以应用角度讲解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.csdn.net/yewandemty/article/details/81989695
 https://www.jianshu.com/p/75badea5ac1e

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值