说在前面
请求处理 重置消费者client的offset
源码解析
进入这个方法,org.apache.rocketmq.client.impl.ClientRemotingProcessor#resetOffset
public RemotingCommand resetOffset(ChannelHandlerContext ctx,
RemotingCommand request) throws RemotingCommandException {
final ResetOffsetRequestHeader requestHeader =
(ResetOffsetRequestHeader) request.decodeCommandCustomHeader(ResetOffsetRequestHeader.class);
log.info("invoke reset offset operation from broker. brokerAddr={}, topic={}, group={}, timestamp={}",
RemotingHelper.parseChannelRemoteAddr(ctx.channel()), requestHeader.getTopic(), requestHeader.getGroup(),
requestHeader.getTimestamp());
Map
offsetTable = new HashMap (); if (request.getBody() != null) {
ResetOffsetBody body = ResetOffsetBody.decode(request.getBody(), ResetOffsetBody.class);
offsetTable = body.getOffsetTable();
}
// 重置offset=》
this.mqClientFactory.resetOffset(requestHeader.getTopic(), requestHeader.getGroup(), offsetTable);
return null; }
进入这个方法,重置offset,org.apache.rocketmq.client.impl.factory.MQClientInstance#resetOffset
public void resetOffset(String topic, String group, Map
offsetTable) { DefaultMQPushConsumerImpl consumer = null;
try {
// 获取消费组的消费者
MQConsumerInner impl = this.consumerTable.get(group);
if (impl != null && impl instanceof DefaultMQPushConsumerImpl) {
consumer = (DefaultMQPushConsumerImpl) impl;
} else {
log.info("[reset-offset] consumer dose not exist. group={}", group);
return;
}
// 消费者暂停
consumer.suspend();
ConcurrentMap
processQueueTable = consumer.getRebalanceImpl().getProcessQueueTable(); for (Map.Entry
entry : processQueueTable.entrySet()) { MessageQueue mq = entry.getKey();
if (topic.equals(mq.getTopic()) && offsetTable.containsKey(mq)) {
ProcessQueue pq = entry.getValue();
// 处理队列删除
pq.setDropped(true);
// 清空处理队列=》
pq.clear();
}
}
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
}
Iterator
iterator = processQueueTable.keySet().iterator(); while (iterator.hasNext()) {
MessageQueue mq = iterator.next();
// 获取处理队列中的消息队列的offset
Long offset = offsetTable.get(mq);
if (topic.equals(mq.getTopic()) && offset != null) {
try {
// 更新消费的offset=》
consumer.updateConsumeOffset(mq, offset);
// 删除不需要的消息队列=》
consumer.getRebalanceImpl().removeUnnecessaryMessageQueue(mq, processQueueTable.get(mq));
iterator.remove();
} catch (Exception e) {
log.warn("reset offset failed. group={}, {}", group, mq, e);
}
}
}
} finally {
if (consumer != null) {
// 消费者暂停取消=》
consumer.resume();
}
} }
进入这个方法,清空处理队列,org.apache.rocketmq.client.impl.consumer.ProcessQueue#clear
public void clear() {
try {
this.lockTreeMap.writeLock().lockInterruptibly();
try {
this.msgTreeMap.clear();
// 消息排序
this.consumingMsgOrderlyTreeMap.clear();
this.msgCount.set(0);
this.msgSize.set(0);
this.queueOffsetMax = 0L;
} finally {
this.lockTreeMap.writeLock().unlock();
}
} catch (InterruptedException e) {
log.error("rollback exception", e);
} }
进入这个方法,更新消费的offset,org.apache.rocketmq.client.consumer.store.RemoteBrokerOffsetStore#updateOffset
@Override
public void updateOffset(MessageQueue mq, long offset, boolean increaseOnly) {
if (mq != null) {
AtomicLong offsetOld = this.offsetTable.get(mq);
if (null == offsetOld) {
offsetOld = this.offsetTable.putIfAbsent(mq, new AtomicLong(offset));
}
if (null != offsetOld) {
if (increaseOnly) {
MixAll.compareAndIncreaseOnly(offsetOld, offset);
} else {
offsetOld.set(offset);
}
}
} }
进入这个方法,删除不需要的消息队列,org.apache.rocketmq.client.impl.consumer.RebalancePullImpl#removeUnnecessaryMessageQueue,defaultMQPullConsumerImpl
@Override
public boolean removeUnnecessaryMessageQueue(MessageQueue mq, ProcessQueue pq) {
// 消息队列持久化=》
this.defaultMQPullConsumerImpl.getOffsetStore().persist(mq);
// 删除消息队列=》
this.defaultMQPullConsumerImpl.getOffsetStore().removeOffset(mq);
return true; }
进入这个方法,消息队列持久化,org.apache.rocketmq.client.consumer.store.RemoteBrokerOffsetStore#persist
@Override
public void persist(MessageQueue mq) {
AtomicLong offset = this.offsetTable.get(mq);
if (offset != null) {
try {
// 更新broker消费的offset=》
this.updateConsumeOffsetToBroker(mq, offset.get());
log.info("[persist] Group: {} ClientId: {} updateConsumeOffsetToBroker {} {}",
this.groupName,
this.mQClientFactory.getClientId(),
mq,
offset.get());
} catch (Exception e) {
log.error("updateConsumeOffsetToBroker exception, " + mq.toString(), e);
}
} }
进入这个方法,更新broker消费的offset,org.apache.rocketmq.client.consumer.store.RemoteBrokerOffsetStore#updateConsumeOffsetToBroker(org.apache.rocketmq.common.message.MessageQueue, long, boolean)
@Override
public void updateConsumeOffsetToBroker(MessageQueue mq, long offset, boolean isOneway) throws RemotingException,
MQBrokerException, InterruptedException, MQClientException {
// 查询broker=》
FindBrokerResult findBrokerResult = this.mQClientFactory.findBrokerAddressInAdmin(mq.getBrokerName());
if (null == findBrokerResult) {
// 从namesrv更新topic路由信息=》
this.mQClientFactory.updateTopicRouteInfoFromNameServer(mq.getTopic());
// 查找broker的地址 =》
findBrokerResult = this.mQClientFactory.findBrokerAddressInAdmin(mq.getBrokerName());
}
if (findBrokerResult != null) {
UpdateConsumerOffsetRequestHeader requestHeader = new UpdateConsumerOffsetRequestHeader();
requestHeader.setTopic(mq.getTopic());
requestHeader.setConsumerGroup(this.groupName);
requestHeader.setQueueId(mq.getQueueId());
requestHeader.setCommitOffset(offset);
if (isOneway) {
// 单途更新消费者的offset=》
this.mQClientFactory.getMQClientAPIImpl().updateConsumerOffsetOneway(
findBrokerResult.getBrokerAddr(), requestHeader, 1000 * 5);
} else {
// 更新消费者的offset=》
this.mQClientFactory.getMQClientAPIImpl().updateConsumerOffset(
findBrokerResult.getBrokerAddr(), requestHeader, 1000 * 5);
}
} else {
throw new MQClientException("The broker[" + mq.getBrokerName() + "] not exist", null);
} }
进入这个方法,查询broker,org.apache.rocketmq.client.impl.factory.MQClientInstance#findBrokerAddressInAdmin
public FindBrokerResult findBrokerAddressInAdmin(final String brokerName) {
String brokerAddr = null;
boolean slave = false;
boolean found = false;
// 查询broker的地址列表
HashMap
map = this.brokerAddrTable.get(brokerName); if (map != null && !map.isEmpty()) {
for (Map.Entry
entry : map.entrySet()) { Long id = entry.getKey();
brokerAddr = entry.getValue();
if (brokerAddr != null) {
found = true;
if (MixAll.MASTER_ID == id) {
slave = false;
} else {
slave = true;
}
break;
}
} // end of for
}
if (found) {
// =》
return new FindBrokerResult(brokerAddr, slave, findBrokerVersion(brokerName, brokerAddr));
}
return null; }
进入这个方法, 从namesrv更新topic路由信息,org.apache.rocketmq.client.impl.factory.MQClientInstance#updateTopicRouteInfoFromNameServer(java.lang.String, boolean, org.apache.rocketmq.client.producer.DefaultMQProducer)
public boolean updateTopicRouteInfoFromNameServer(final String topic, boolean isDefault,
DefaultMQProducer defaultMQProducer) {
try {
if (this.lockNamesrv.tryLock(LOCK_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)) {
try {
TopicRouteData topicRouteData;
if (isDefault && defaultMQProducer != null) {
// 获取默认的topic路由信息 =》
topicRouteData = this.mQClientAPIImpl.getDefaultTopicRouteInfoFromNameServer(defaultMQProducer.getCreateTopicKey(),
1000 * 3);
if (topicRouteData != null) {
// 获取队列信息
for (QueueData data : topicRouteData.getQueueDatas()) {
// 读写队列最大数量4
int queueNums = Math.min(defaultMQProducer.getDefaultTopicQueueNums(), data.getReadQueueNums());
data.setReadQueueNums(queueNums);
data.setWriteQueueNums(queueNums);
}
}
} else {
// 获取topic路由信息=》
topicRouteData = this.mQClientAPIImpl.getTopicRouteInfoFromNameServer(topic, 1000 * 3);
}
if (topicRouteData != null) {
TopicRouteData old = this.topicRouteTable.get(topic);
// 判断topic路由是否改变=》
boolean changed = topicRouteDataIsChange(old, topicRouteData);
if (!changed) {
// 需要更新路由信息
changed = this.isNeedUpdateTopicRouteInfo(topic);
} else {
log.info("the topic[{}] route info changed, old[{}] ,new[{}]", topic, old, topicRouteData);
}
if (changed) {
TopicRouteData cloneTopicRouteData = topicRouteData.cloneTopicRouteData();
for (BrokerData bd : topicRouteData.getBrokerDatas()) {
// 更新broker的地址列表
this.brokerAddrTable.put(bd.getBrokerName(), bd.getBrokerAddrs());
}
// Update Pub info
{
// topic路由信息转换成topic发布信息=》
TopicPublishInfo publishInfo = topicRouteData2TopicPublishInfo(topic, topicRouteData);
publishInfo.setHaveTopicRouterInfo(true);
// 遍历生产者信息
Iterator
> it = this.producerTable.entrySet().iterator(); while (it.hasNext()) {
Entry
entry = it.next(); MQProducerInner impl = entry.getValue();
if (impl != null) {
// 更新topic发布信息=》
impl.updateTopicPublishInfo(topic, publishInfo);
}
}
}
// Update sub info
{
// 获取消息队列订阅信息
Set
subscribeInfo = topicRouteData2TopicSubscribeInfo(topic, topicRouteData); // 遍历消费者
Iterator
> it = this.consumerTable.entrySet().iterator(); while (it.hasNext()) {
Entry
entry = it.next(); MQConsumerInner impl = entry.getValue();
if (impl != null) {
// 更新topic的订阅信息=》
impl.updateTopicSubscribeInfo(topic, subscribeInfo);
}
}
}
log.info("topicRouteTable.put. Topic = {}, TopicRouteData[{}]", topic, cloneTopicRouteData);
this.topicRouteTable.put(topic, cloneTopicRouteData);
return true;
}
} else {
log.warn("updateTopicRouteInfoFromNameServer, getTopicRouteInfoFromNameServer return null, Topic: {}", topic);
}
} catch (Exception e) {
if (!topic.startsWith(MixAll.RETRY_GROUP_TOPIC_PREFIX) && !topic.equals(MixAll.AUTO_CREATE_TOPIC_KEY_TOPIC)) {
log.warn("updateTopicRouteInfoFromNameServer Exception", e);
}
} finally {
this.lockNamesrv.unlock();
}
} else {
log.warn("updateTopicRouteInfoFromNameServer tryLock timeout {}ms", LOCK_TIMEOUT_MILLIS);
}
} catch (InterruptedException e) {
log.warn("updateTopicRouteInfoFromNameServer Exception", e);
}
return false; }
进入这个方法,获取默认的topic路由信息,org.apache.rocketmq.client.impl.MQClientAPIImpl#getTopicRouteInfoFromNameServer(java.lang.String, long, boolean)
public TopicRouteData getTopicRouteInfoFromNameServer(final String topic, final long timeoutMillis,
boolean allowTopicNotExist) throws MQClientException, InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException {
GetRouteInfoRequestHeader requestHeader = new GetRouteInfoRequestHeader();
requestHeader.setTopic(topic);
RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_ROUTEINTO_BY_TOPIC, requestHeader);
// 同步获取topic的路由信息=》
RemotingCommand response = this.remotingClient.invokeSync(null, request, timeoutMillis);
assert response != null;
switch (response.getCode()) {
case ResponseCode.TOPIC_NOT_EXIST: {
if (allowTopicNotExist && !topic.equals(MixAll.AUTO_CREATE_TOPIC_KEY_TOPIC)) {
log.warn("get Topic [{}] RouteInfoFromNameServer is not exist value", topic);
}
break;
}
case ResponseCode.SUCCESS: {
byte[] body = response.getBody();
if (body != null) {
return TopicRouteData.decode(body, TopicRouteData.class);
}
}
default:
break;
}
throw new MQClientException(response.getCode(), response.getRemark()); }
进入这个方法,同步获取topic的路由信息,org.apache.rocketmq.remoting.netty.NettyRemotingClient#invokeSync 前面介绍过了。
往上返回到这个方法,获取topic路由信息,org.apache.rocketmq.client.impl.MQClientAPIImpl#getTopicRouteInfoFromNameServer(java.lang.String, long) 上面介绍过了。
往上返回到这个方法,判断topic路由是否改变,org.apache.rocketmq.client.impl.factory.MQClientInstance#topicRouteDataIsChange
private boolean topicRouteDataIsChange(TopicRouteData olddata, TopicRouteData nowdata) {
if (olddata == null || nowdata == null)
return true;
TopicRouteData old = olddata.cloneTopicRouteData();
TopicRouteData now = nowdata.cloneTopicRouteData();
Collections.sort(old.getQueueDatas());
Collections.sort(old.getBrokerDatas());
Collections.sort(now.getQueueDatas());
Collections.sort(now.getBrokerDatas());
return !old.equals(now);
}
往上返回到这个方法,单途更新消费者的offset,org.apache.rocketmq.client.impl.MQClientAPIImpl#updateConsumerOffsetOneway
public void updateConsumerOffsetOneway(
final String addr,
final UpdateConsumerOffsetRequestHeader requestHeader,
final long timeoutMillis
) throws RemotingConnectException, RemotingTooMuchRequestException, RemotingTimeoutException, RemotingSendRequestException,
InterruptedException {
RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.UPDATE_CONSUMER_OFFSET, requestHeader);
// =》
this.remotingClient.invokeOneway(MixAll.brokerVIPChannel(this.clientConfig.isVipChannelEnabled(), addr), request, timeoutMillis); }
进入这个方法,org.apache.rocketmq.remoting.netty.NettyRemotingClient#invokeOneway前面介绍过了。
往上返回都这个方法,更新消费者的offset,org.apache.rocketmq.client.impl.MQClientAPIImpl#updateConsumerOffset
public void updateConsumerOffset(
final String addr,
final UpdateConsumerOffsetRequestHeader requestHeader,
final long timeoutMillis
) throws RemotingException, MQBrokerException, InterruptedException {
RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.UPDATE_CONSUMER_OFFSET, requestHeader);
// =》
RemotingCommand response = this.remotingClient.invokeSync(MixAll.brokerVIPChannel(this.clientConfig.isVipChannelEnabled(), addr),
request, timeoutMillis);
assert response != null;
switch (response.getCode()) {
case ResponseCode.SUCCESS: {
return;
}
default:
break;
}
throw new MQBrokerException(response.getCode(), response.getRemark()); }
进入到这个方法,org.apache.rocketmq.remoting.netty.NettyRemotingClient#invokeSync前面介绍过了。
往上返回到这个方法,删除消息队列,org.apache.rocketmq.client.consumer.store.RemoteBrokerOffsetStore#removeOffset
public void removeOffset(MessageQueue mq) {
if (mq != null) {
this.offsetTable.remove(mq);
log.info("remove unnecessary messageQueue offset. group={}, mq={}, offsetTableSize={}", this.groupName, mq,
offsetTable.size());
} }
往上返回到这个方法,org.apache.rocketmq.client.impl.consumer.RebalancePushImpl#removeUnnecessaryMessageQueue,defaultMQPushConsumerImpl
@Override
public boolean removeUnnecessaryMessageQueue(MessageQueue mq, ProcessQueue pq) {
// 持久化消息队列
this.defaultMQPushConsumerImpl.getOffsetStore().persist(mq);
// 删除消息队列的offset=》
this.defaultMQPushConsumerImpl.getOffsetStore().removeOffset(mq);
// 如果是有序消费,消息类型是集群消费
if (this.defaultMQPushConsumerImpl.isConsumeOrderly()
&& MessageModel.CLUSTERING.equals(this.defaultMQPushConsumerImpl.messageModel())) {
try {
if (pq.getLockConsume().tryLock(1000, TimeUnit.MILLISECONDS)) {
try {
// 解锁延迟=》
return this.unlockDelay(mq, pq);
} finally {
pq.getLockConsume().unlock();
}
} else {
log.warn("[WRONG]mq is consuming, so can not unlock it, {}. maybe hanged for a while, {}",
mq,
pq.getTryUnlockTimes());
pq.incTryUnlockTimes();
}
} catch (Exception e) {
log.error("removeUnnecessaryMessageQueue Exception", e);
}
return false;
}
return true; }
进入这个方法,持久化消息队列,org.apache.rocketmq.client.consumer.store.RemoteBrokerOffsetStore#persist 前面介绍过了。
往上返回到这个方法, 删除消息队列的offset,org.apache.rocketmq.client.consumer.store.RemoteBrokerOffsetStore#removeOffset 前面介绍过了。
进入这个方法,解锁延迟,org.apache.rocketmq.client.impl.consumer.RebalancePushImpl#unlockDelay
private boolean unlockDelay(final MessageQueue mq, final ProcessQueue pq) {
// 处理队列中有临时消息=》
if (pq.hasTempMessage()) {
log.info("[{}]unlockDelay, begin {} ", mq.hashCode(), mq);
this.defaultMQPushConsumerImpl.getmQClientFactory().getScheduledExecutorService().schedule(new Runnable() {
@Override
public void run() {
log.info("[{}]unlockDelay, execute at once {}", mq.hashCode(), mq);
// 解锁消息队列=》
RebalancePushImpl.this.unlock(mq, true);
}
}, UNLOCK_DELAY_TIME_MILLS, TimeUnit.MILLISECONDS);
} else {
this.unlock(mq, true);
}
return true; }
进入这个方法,处理队列中有临时消息,org.apache.rocketmq.client.impl.consumer.ProcessQueue#hasTempMessage
public boolean hasTempMessage() {
try {
this.lockTreeMap.readLock().lockInterruptibly();
try {
return !this.msgTreeMap.isEmpty();
} finally {
this.lockTreeMap.readLock().unlock();
}
} catch (InterruptedException e) {
}
return true; }
进入这个方法,解锁消息队列,org.apache.rocketmq.client.impl.consumer.RebalanceImpl#unlock
public void unlock(final MessageQueue mq, final boolean oneway) {
// 按brokerName查询broker地址在订阅信息中=》
FindBrokerResult findBrokerResult = this.mQClientFactory.findBrokerAddressInSubscribe(mq.getBrokerName(), MixAll.MASTER_ID, true);
if (findBrokerResult != null) {
UnlockBatchRequestBody requestBody = new UnlockBatchRequestBody();
requestBody.setConsumerGroup(this.consumerGroup);
requestBody.setClientId(this.mQClientFactory.getClientId());
requestBody.getMqSet().add(mq);
try {
// 解锁批量消息队列,1s超时
this.mQClientFactory.getMQClientAPIImpl().unlockBatchMQ(findBrokerResult.getBrokerAddr(), requestBody, 1000, oneway);
log.warn("unlock messageQueue. group:{}, clientId:{}, mq:{}",
this.consumerGroup,
this.mQClientFactory.getClientId(),
mq);
} catch (Exception e) {
log.error("unlockBatchMQ exception, " + mq, e);
}
} }
进入这个方法,按brokerName查询broker地址在订阅信息中,org.apache.rocketmq.client.impl.factory.MQClientInstance#findBrokerAddressInSubscribe
public FindBrokerResult findBrokerAddressInSubscribe(
final String brokerName,
final long brokerId,
final boolean onlyThisBroker
) {
String brokerAddr = null;
boolean slave = false;
boolean found = false;
// 获取broker的缓存信息
HashMap
map = this.brokerAddrTable.get(brokerName); if (map != null && !map.isEmpty()) {
brokerAddr = map.get(brokerId);
slave = brokerId != MixAll.MASTER_ID;
found = brokerAddr != null;
if (!found && !onlyThisBroker) {
Entry
entry = map.entrySet().iterator().next(); brokerAddr = entry.getValue();
slave = entry.getKey() != MixAll.MASTER_ID;
found = true;
}
}
if (found) {
return new FindBrokerResult(brokerAddr, slave, findBrokerVersion(brokerName, brokerAddr));
}
return null; }
进入这个方法,解锁批量消息队列,1s超时,org.apache.rocketmq.client.impl.MQClientAPIImpl#unlockBatchMQ
public void unlockBatchMQ(
final String addr,
final UnlockBatchRequestBody requestBody,
final long timeoutMillis,
final boolean oneway
) throws RemotingException, MQBrokerException, InterruptedException {
RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.UNLOCK_BATCH_MQ, null);
// json编码
request.setBody(requestBody.encode());
if (oneway) {
// 单途请求=》
this.remotingClient.invokeOneway(addr, request, timeoutMillis);
} else {
// 同步执行
RemotingCommand response = this.remotingClient
.invokeSync(MixAll.brokerVIPChannel(this.clientConfig.isVipChannelEnabled(), addr), request, timeoutMillis);
switch (response.getCode()) {
case ResponseCode.SUCCESS: {
return;
}
default:
break;
}
throw new MQBrokerException(response.getCode(), response.getRemark());
} }
进入这个方法,单途请求,org.apache.rocketmq.remoting.netty.NettyRemotingClient#invokeOneway 前面介绍过了。
进入这个方法,同步执行,org.apache.rocketmq.remoting.netty.NettyRemotingClient#invokeSync 前面介绍过了。
往上返回到这个方法,消费者暂停取消,org.apache.rocketmq.client.impl.consumer.DefaultMQPushConsumerImpl#resume
public void resume() {
this.pause = false;
// 负载均衡=》
doRebalance();
log.info("resume this consumer, {}", this.defaultMQPushConsumer.getConsumerGroup()); }
进入这个方法,负载均衡,org.apache.rocketmq.client.impl.consumer.RebalanceImpl#doRebalance 前面介绍过了。
往上返回到这个方法,org.apache.rocketmq.client.impl.ClientRemotingProcessor#resetOffset结束。
说在最后
本次解析仅代表个人观点,仅供参考。
加入技术微信群
钉钉技术群