RockerMQ里有个非常重要的数据结构叫ProcessQueue,很多功能,例如消费进度,消费等等功能的底层核心数据保存都是有ProcessQueue提供,下面介绍一下ProcessQueue提供的功能,而整个涉及的流程不会在这展开,在另外的功能分析文章如果涉及才会深入分析
看下代码上的注释:
Queue consumption snapshot
即消息快照的意思,为什么要这样形容呢?主要是因为在消息拉取到的时候,会把消息存放在其中。另外在拉取消息的时候,使用是的PullRequest去请求,其内部结构如下:
public class PullRequest {
private String consumerGroup;
private MessageQueue messageQueue;
private ProcessQueue processQueue;
private long nextOffset;
private boolean lockedFirst = false;
}
可以看到,ProcessQueue和一个MessageQueue是对应的,即一个队列会有一个ProcessQueue的数据结构,看下其主要的字段
public class ProcessQueue {
public final static long RebalanceLockMaxLiveTime =
Long.parseLong(System.getProperty("rocketmq.client.rebalance.lockMaxLiveTime", "30000"));
public final static long RebalanceLockInterval = Long.parseLong(System.getProperty("rocketmq.client.rebalance.lockInterval", "20000"));
private final static long PullMaxIdleTime = Long.parseLong(System.getProperty("rocketmq.client.pull.pullMaxIdleTime", "120000"));
// 用来保存拉取到的消息
private final TreeMap<Long, MessageExt> msgTreeMap = new TreeMap<Long, MessageExt>();
// 当前保存的消息数,放进来的时候会加,移除的时候会减
private final AtomicLong msgCount = new AtomicLong();
// 消费锁,主要在顺序消费和移除ProcessQueue的时候使用
private final Lock lockConsume = new ReentrantLock();
// 顺序消费的时候使用
private final TreeMap<Long, MessageExt> msgTreeMapTemp = new TreeMap<Long, MessageExt>();
// 记录了废弃ProcessQueue的时候lockConsume的次数
private final AtomicLong tryUnlockTimes = new AtomicLong(0);
// ProcessQueue中保存的消息里的最大offset,为ConsumeQueue的offset
private volatile long queueOffsetMax = 0L;
// 该数据结构里的消息是否废弃
private volatile boolean dropped = false;
// 上次执行拉取消息的时间
private volatile long lastPullTimestamp = System.currentTimeMillis();
// 上次消费完消息后记录的时间
private volatile long lastConsumeTimestamp = System.currentTimeMillis();
private volatile boolean locked = false;
// 上次锁定的时间
private volatile long lastLockTimestamp = System.currentTimeMillis();
// 是否正在消息
private volatile boolean consuming = false;
// 该参数为调整线程池的时候提供了数据参考
private volatile long msgAccCnt = 0;
}
isLockExpired
public boolean isLockExpired() {
boolean result = (System.currentTimeMillis() - this.lastLockTimestamp) > RebalanceLockMaxLiveTime;
return result;
}
顺序消费的时候使用,消费之前会判断一下ProcessQueue锁定时间是否超过阈值(默认30000ms),如果没有超时,代表还是持有锁,具体细节在顺序消费的时候会详细说明.
负载
isPullExpired
public boolean isPullExpired() {
boolean result = (System.currentTimeMillis() - this.lastPullTimestamp) > PullMaxIdleTime;
return result;
}
在拉取的时候更新lastPullTimestamp的值,然后在rebalance的时候会去判断ProcessQueue已经超过一定的时间没有去拉取消息,如果是的话,则将ProcessQueue废弃(setDropped(true))且从ProcessQueue和MessageQueue的对应关系中移除该ProcessQueue,代码细节如下:
if (pq.isPullExpired()) {
switch (this.consumeType()) {
case CONSUME_ACTIVELY:
break;
case CONSUME_PASSIVELY:
pq.setDropped(true);
if (this.removeUnnecessaryMessageQueue(mq, pq)) {
it.remove();
changed = true;
log.error("[BUG]doRebalance, {}, remove unnecessary mq, {}, because pull is pause, so try to fixed it",
consumerGroup, mq);
}
break;
default:
break;<