RocketMQ
RocketMQ
准备工作
下载RocketMQ
这次下载rocketMQ通过docker安装,
- 先安装相应版本的jdk,我暂时安装的是jdk1.8,
- 使用docker 将rocketMQ的镜像给爬下来
docker pull rocketmqinc/rocketmq
3. 启动namesrv服务
docker run -d -p 9876:9876 -v {RmHome}/data/namesrv/logs:/root/logs -v {RmHome}/data/namesrv/store:/root/store --name rmqnamesrv -e "MAX_POSSIBLE_HEAP=100000000" rocketmqinc/rocketmq sh mqnamesrv
4. 启动broker服务
brokerClusterName = DefaultCluster
brokerName = broker-a
brokerId = 0
deleteWhen = 04
fileReservedTime = 48
brokerRole = ASYNC_MASTER
flushDiskType = ASYNC_FLUSH
brokerIP1 = {docker宿主机IP}
- 启动服务
docker run -d -p 10911:10911 -p 10909:10909 -v {RmHome}/data/broker/logs:/root/logs -v {RmHome}/rocketmq/data/broker/store:/root/store -v {RmHome}/conf/broker.conf:/opt/rocketmq/conf/broker.conf --name rmqbroker --link rmqnamesrv:namesrv -e "NAMESRV_ADDR=namesrv:9876" -e "MAX_POSSIBLE_HEAP=200000000" rocketmqinc/rocketmq sh mqbroker -c /opt/rocketmq/conf/broker.conf
- 安装控制台
docker pull styletang/rocketmq-console-ng
- 启动该容器
docker run -e "JAVA_OPTS=-Drocketmq.config.namesrvAddr={docker宿主机ip}:9876 -Drocketmq.config.isVIPChannel=false" -p 8080:8080 -t styletang/rocketmq-console-ng
- 输入网址,查看控制台

测试RocketMQ
发送消息
#设置环境变量
export NAMESRV_ADDR=localhost:9876
# 使用按爪个包的demo发送消息
# sh bin/tools.sh org.apache.rocketmq.example.quickstart.Producer
接受消息
# 设置环境变量
export NAMESRV_ADDR=localhost:9876
# 接受消息
# sh bin/tools.sh org.apache.rocketmq.example.quickstart.Consumer
关闭RocketMQ
# 1.关闭NAMESERVER
sh bin/mqshutdown namesrv
# 2.关闭broker
集群搭建

角色介绍
- Producer:消息的发送者,
- Consumer:消息接收者
- Broker:暂存和传输消息
- NameServer :管理Broker
- Topic:区分消息的种类,一个发送者可以发送消息给一个或者多个Topic:
- Message Queue:相当于Topic的分区;用于并行发送和接受消息。
集群搭建方式
集群工作流程
- 启动NameServer,NameServer起来后监听端口,等待Broker,Producer,Consumer练上来,相当于一个路由控制中心。
- Broker启动,跟所有的NameServer保持长连接,定时的去发送心跳检测,注册成功后,NameServer集群就有了Topic跟Broker的映射关系。
- 收发消息前,先创建Topic,创建Topic时需要指定该Topic要存储在哪些Broker上,也可以在发送消息时自动创建Topic。
- Producer发送消息,启动时先跟NameServer集群中的其中一台建立长连接,并从NameServer中获取当前Topic存放在哪些的Broker上,轮询从队列列表上选择一个队列,然后与队列所在的Broker建立长连接从而向Broker发消息。
- Consumer与Producer类似。
消费者的源码解析
消费者获取消息的方式有两种,一种时主动从broker中pull消息,另一种broker主动push给消费者,其实broker主动push消息给消费者,说到底还是消费者先发送请求给broker,表明自己需要哪些topic的消息,然后broker定期扫描,然后收到该topic的消息再发送给消费者。
groupName:创建消费者对象的时候,一般都会指定消费者组名,这个组名groupName也就是订阅组,一般情况下,假如有多个消费者,每一个消费者都使用相同的groupName,那么这些消费者构成的就属于一个组也就是订阅组。Consumer的groupName用于把多个Consumer组织到一起,形成一个订阅组,这样订阅组里的每个消费者都会收到Topic里面的消息
创建pushConsumer
// 实例化DefaultMQPushConsumer ,参数为groupName 消费组组名
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(topic);
// 设置nameServer的地址
consumer.setNamesrvAddr(nameServer);
// 从哪个位置上接受,此设置只对消费者的第一次启动的消费者有效
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
// 设置消息
consumer.setMessageModel(MessageModel.CLUSTERING);
setConsumeFromWhere具有的属性
| CONSUME_FROM_LAST_OFFSET | 第一次启动从队列的最后的位置消费,后续接着上次消费的进度进行消费 |
|---|---|
| CONSUME_FROM_FIRST_OFFSET | 第一次启动从队列的开始位置消费,后续接着上次消费的进度进行消费 |
| CONSUME_FROM_TIMESTAMP | 第一次启动从指定时间点位置消费,后续再启动接着上次消费的进度开始消费 |
setMessageModel具有的属性
RocketMQ 支持两种消息模式: Clustering 和Broadcasting 。
| BROADCASTING("BROADCASTING") | 同一个ConsumerGroup(GroupName相同)里的每一个Consumer都能订阅到所订阅Topic的全部消息,也就是一个消息会被多次分发,被多个消费者消费。 |
|---|---|
| CLUSTERING("CLUSTERING"); | 同一个ConsumerGroup(GroupName相同)里的每个Consumer只消费所订阅消息的一部分内容,同一个ConsumerGroup里所有的Consumer 消费的内容合起来才是所订阅Topic 内容的整体,从而达到负载均衡的目的。 |
consumer.setMaxReconsumeTimes(3); // 设置最大重复消费的次数
MaxReconsumeTimes可以设置最大的重复消费的次数,如果超过了最大的消费次数,则该消息将被删除,默认为-1,为队列等待。
consumerThreadMin和consumerThreadMax属性
/**
* Minimum consumer thread number
*/
private int consumeThreadMin = 20;
/**
* Max consumer thread number
*/
private int consumeThreadMax = 20;
消费者使用一个ThreadPoolExecutor来处理内部的消费,因此可以设置这两个属性进行更改,提高处理的消费者服务器的处理进程数。
pullBatchSize和consumeMessageBatchMaxSize属性
pullBatchSize是consumer每次从broker拉取的消息最大数,拉取后新消息先存储到consumer内存中。而consumeMessageBatchMaxSize参数代表了,每次将内存中存储的新消息(pullBatchSize参数拉取的消息)传递给开发者自定义的处理方法的最多条数,也是为了提高消费速度的配置项。
/**
* Batch pull size
* defalut size of 32
*/
private int pullBatchSize = 32;
/**
* Batch consumption size
*/
private int consumeMessageBatchMaxSize = 1;
设置完属性之后,就要对消费者进行订阅
// 消费者开始订阅
consumer.subscribe(topic, tags);
并且需要注册消息监听器
consumer.registerMessageListener(new MessageListenerConcurrently() {
@SneakyThrows
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
log.info("消费者开始消费消息,消息长度: " + msgs.size());
System.out.println(msgs.size());
for (int i = 0; i < msgs.size(); i++) {
MessageExt msg = msgs.get(i);
log.info("topic -> {}, tags -> {}, msg -> {}" + msg.getTopic() + "->" + msg.getTags() + "->" + new String(msg.getBody()));
try {
//此处对获取到的消息进行处理
//saveData(topic, tags, new String(msg.getBody(), "utf-8"));
} catch (Exception e) {
e.printStackTrace();
if (msgs.get(i).getReconsumeTimes() >= 2) {
log.info("失败消息:" + new String(msgs.get(i).getBody(), "utf-8"));
//这里写失败消息处理机制,或者不处理
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
} else {
log.info("消息消费失败,尝试重试");
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
}
}
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
pullMessageService
private final LinkedBlockingQueue<PullRequest> pullRequestQueue = new LinkedBlockingQueue<PullRequest>();
@Override
public void run() {
log.info(this.getServiceName() + " service started");
while (!this.isStopped()) {
try {
PullRequest pullRequest = this.pullRequestQueue.take();
this.pullMessage(pullRequest);
} catch (InterruptedException ignored) {
} catch (Exception e) {
log.error("Pull Message Service Run Method exception", e);
}
}
log.info(this.getServiceName() + " service end");
}

-----待补充
事务消息

大致分为两个流程:正常事务消息的发送及提交和事务消息的补偿流程。
- 事务消息发送及提交
- 发送消息(half消息)。
- 服务段响应消息写入结果。
- 根据发送结果执行本地事务(如果写入失败,此时half消息对业务不可见,本地逻辑不执行)
- 根据本地事务状态执行Commit或者Rollback(Commit操作生成消息索引,消息对消费者可见)
- 事务补偿
- 对没有Commit/Rollback的事务消息(pending状态的消息),从服务端发起一次
回查; - Producer收到回查消息,检查消息对应的本地事务的状态。
- 根据本地事务的状态,重新commit或者Rollback
其中补偿截断用于解决消息Commit或者Rollback发生超时或者失败的情况。
- 事务消息状态
事务消息共有三种状态,提交状态,回滚状态,中间状态:
- TransactionStatus.CommitTransaction:提交状态,它允许消费者消费此消息。
- TransactionStatus.RollbackTransaction:回滚事务,它代码该消息将被删除,不允许被消费
- TransactionStatus.Unknown:中间状态,它代表需要检查消息队列来确定状态。
使用限制
- 事务消息不支持延时消息和批量消息
- 为了避免单个消息被检查太多次二导致半队列消息累计,我们默认将单个消息检查次数限制为15次,可以通过Broker配置文件的transactionCheckMax参数来修改此限制。
- 事务性消息可能不止一次被检查或消费
- 建议使用同步的双重写入机制
- 事务允许反向查询

浙公网安备 33010602011771号