文章目录
- 1.消费入口PushConsumer
- 2.org.apache.rocketmq.client.consumer.DefaultMQPushConsumer#start
- 3.org.apache.rocketmq.client.impl.consumer.DefaultMQPushConsumerImpl#start
- 4.生成对应的消费线程池,如:ConsumeMessageConcurrentlyService
- 5.开始拉取消息org.apache.rocketmq.client.impl.factory.MQClientInstance#start
- 6.消费者拉取消息线程服务org.apache.rocketmq.client.impl.consumer.PullMessageService
- 7.拉取消息主方法 org.apache.rocketmq.client.impl.consumer.DefaultMQPushConsumerImpl#pullMessage
- 8.拉取消息
- 9.filtersrv 处理请求
- 10.filtersrv拉取消息
- 11. broker内部拉取消息方法
- 12. messageStore获取消息
消息消费的原理对于我们理解消息队列的运行十分有帮助,今天我们来了解rocketmq的push模式消费。
消费全过程:
- 消费者启动
- 检查配置
- 启动客户端netty服务
- 开启定时任务:(1)每隔30S定时更新 Topic路由配置 (2)每隔30S清空下线的Broker(Master或Slave),向Broker发送心跳,传递生产者或订阅信息 (3)每隔5S持久化消费进度到远程
- 负载均衡开始:获取topic和路由数据
- 循环每个topic,按照平均策略,环形策略,队列分配算法或者机房算法(默认是平均策略)给当前consumer计算该topic的对应brokerName的那些队列(一般是0,1,2,3四个队列)进行分配。
- 组装拉取请求,(从Broker读取MessageQueue的消费OffSet), 将所有的消息拉取请求放入到消息拉取请求队列
- 消费者拉取线程启动,循环从消息拉取队列中获取拉取请求,去请求broker从该topic对应的broker下面的queue,从指定的消费Offset拉取指定数量的消息
- broker开始处理: 通过topic,queueId,offset从ConsumeQueue里获取新加入消息的消费信息(消息物理位置offset,消息长度,消息tagsCode),然后循环从CommitLog里提取消息
- 然后broker将消息 内容返回给consumer client端
- 提取ByteBuffer生成List
- 循环处理每一个条数据,进入consumeMessage方法,若消费成功则返回CONSUME_SUCCESS,否则消费失败
- 移除消费成功消息,并返回消费的最新进度, 更新本地的offsetTable,对各个队列的offset进行更新,定时任务会每5秒对其进行持久化到broker.
1.消费入口PushConsumer
import java.util.List;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.MessageExt;
public class PushConsumer {
public static void main(String[] args) throws InterruptedException, MQClientException {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("CID_JODIE_1");
consumer.subscribe("TopicTest1", "*");
consumer.setNamesrvAddr("127.0.0.1:9876");
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
System.out.printf(Thread.currentThread().getName() + " Receive New Messages: " + msgs + "%n");
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//开启,开始消费消息
consumer.start();
System.out.printf("Consumer Started.%n");
}
}
2.org.apache.rocketmq.client.consumer.DefaultMQPushConsumer#start
3.org.apache.rocketmq.client.impl.consumer.DefaultMQPushConsumerImpl#start
public void start() throws MQClientException {
switch (this.serviceState) {
case CREATE_JUST:
log.info("the consumer [{}] start beginning. messageModel={}, isUnitMode={}", this.defaultMQPushConsumer.getConsumerGroup(),
this.defaultMQPushConsumer.getMessageModel(), this.defaultMQPushConsumer.isUnitMode());
this.serviceState = ServiceState.START_FAILED;
// 检查配置
this.checkConfig();
// Rebalance负载均衡 复制订阅数据
this.copySubscription();
// 设置instanceName,为一个字符串化的数字,比如10072
if (this.defaultMQPushConsumer.getMessageModel() == MessageModel.CLUSTERING) {
this.defaultMQPushConsumer.changeInstanceNameToPID();
}
// 获取MQClient对象,clientId为ip@instanceName,比如192.168.0.1@10072
this.mQClientFactory = MQClientManager.getInstance().getAndCreateMQClientInstance(this.defaultMQPushConsumer, this.rpcHook);
// 设置负载均衡器
this.rebalanceImpl.setConsumerGroup(this.defaultMQPushConsumer.getConsumerGroup());
//默认这是消费模式为集群模式,每条消息被同一组的消费者中的一个消费
//还可以设置为广播模式,每条消息被同一个组的所有消费者都消费一次
this.rebalanceImpl.setMessageModel(this.defaultMQPushConsumer.getMessageModel());
//默认是AllocateMessageQueueAveragely,均分策略
this.rebalanceImpl.setAllocateMessageQueueStrategy(this.defaultMQPushConsumer.getAllocateMessageQueueStrategy());
this.rebalanceImpl.setmQClientFactory(this.mQClientFactory);
// 拉取API封装
this.pullAPIWrapper = new PullAPIWrapper(mQClientFactory, this.defaultMQPushConsumer.getConsumerGroup(), isUnitMode());
this.pullAPIWrapper.registerFilterMessageHook(filterMessageHookList);
//生成消费进度处理器,集群模式下消费进度保存在Broker上,因为同一组内的消费者要共享进度;广播模式下进度保存在消费者端
if (this.defaultMQPushConsumer.getOffsetStore() != null) {
this.offsetStore = this.defaultMQPushConsumer.getOffsetStore();
} else {
switch (this.defaultMQPushConsumer.getMessageModel()) {
case BROADCASTING:
this.offsetStore = new LocalFileOffsetStore(this.mQClientFactory, this.defaultMQPushConsumer.getConsumerGroup());
break;
case CLUSTERING:
this.offsetStore = new RemoteBrokerOffsetStore(this.mQClientFactory, this.defaultMQPushConsumer.getConsumerGroup());
break;
default:
break;
}
}
//若是广播模式,加载本地的消费进度文件
this.offsetStore.load();
// 根据监听是顺序模式还是并发模式来生成相应的ConsumerService
if (this.getMessageListenerInner() instanceof MessageListenerOrderly) {
this.consumeOrderly = true;
this.consumeMessageService = new ConsumeMessageOrderlyService(this, (MessageListenerOrderly)this.getMessageListenerInner());
} else if (this.getMessageListenerInner() instanceof MessageListenerConcurrently) {
this.consumeOrderly = false;
this.consumeMessageService = new ConsumeMessageConcurrentlyService(this, (MessageListenerConcurrently)this.getMessageListenerInner());
}
this.consumeMessageService.start();
// 设置MQClient对象
boolean registerOK = mQClientFactory.registerConsumer(this.defaultMQPushConsumer.getConsumerGroup(), this);
if (!registerOK) {
this.serviceState = ServiceState.CREATE_JUST;
this.consumeMessageService.shutdown();
throw new MQClientException("The consumer group[" + this.defaultMQPushConsumer.getConsumerGroup()
+ "] has been created before, specify another name please." + FAQUrl.suggestTodo(FAQUrl.GROUP_NAME_DUPLICATE_URL),
null);
}
mQClientFactory.start();
log.info("the consumer [{}] start OK.", this.defaultMQPushConsumer.getConsumerGroup());
// 设置服务状态
this.serviceState = ServiceState.RUNNING;
break;
case RUNNING:
case START_FAILED:
case SHUTDOWN_ALREADY:
throw new MQClientException("The PushConsumer service state not OK, maybe started once, "//
+ this.serviceState//
+ FAQUrl.suggestTodo(FAQUrl.CLIENT_SERVICE_NOT_OK),
null);
default:
break;
}
// 从Namesrv获取TopicRouteData,更新TopicPublishInfo和MessageQueue (在Consumer start时马上调用,之后每隔一段时间调用一次)
this.updateTopicSubscribeInfoWhenSubscriptionChanged();
// 向TopicRouteData里的所有Broker发送心跳,注册Consumer/Producer信息到Broker上 (在Consumer start时马上调用,之后每隔一段时间调用一次)
this.mQClientFactory.sendHeartbeatToAllBrokerWithLock();
// 唤醒MessageQueue均衡服务,负载均衡后马上开启第一次拉取消息
this.mQClientFactory.rebalanceImmediately();
}
4.生成对应的消费线程池,如:ConsumeMessageConcurrentlyService
一般最小消费线程数20,最大64
public ConsumeMessageConcurrentlyService(DefaultMQPushConsumerImpl defaultMQPushConsumerImpl,
MessageListenerConcurrently messageListener) {
//指定消费者
this.defaultMQPushConsumerImpl = defaultMQPushConsumerImpl;
//指定监听器对象
this.messageListener = messageListener;
//获取默认的消费者对象
this.defaultMQPushConsumer = this.defaultMQPushConsumerImpl.getDefaultMQPushConsumer();
//获取消费者组
this.consumerGroup = this.defaultMQPushConsumer.getConsumerGroup();
//指定顺序消息队列
this.consumeRequestQueue = new LinkedBlockingQueue<>();
//指定消费连接池
this.consumeExecutor = new ThreadPoolExecutor(//
this.defaultMQPushConsumer.getConsumeThreadMin(), //
this.defaultMQPushConsumer.getConsumeThreadMax(), //
1000 * 60, //
TimeUnit.MILLISECONDS, //
this.consumeRequestQueue, //
new ThreadFactoryImpl("ConsumeMessageThread_"));
this.scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl("ConsumeMessageScheduledThread_"));
this.cleanExpireMsgExecutors = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl("CleanExpireMsgScheduledThread_"));
}
5.开始拉取消息org.apache.rocketmq.client.impl.factory.MQClientInstance#start
public void start() throws MQClientException {
synchronized (this) {
switch (this.serviceState) {
case CREATE_JUST:
this.serviceState = ServiceState.START_FAILED;
// If not specified,looking address from name server
if (null == this.clientConfig.getNamesrvAddr()) {
//如果url未指定,可以通过Http请求从其他处获取
this.mQClientAPIImpl.fetchNameServerAddr();
}
// Start request-response channel
this.mQClientAPIImpl.start();
// 启动多个定时任务
this.startScheduledTask();
// Start pull service
this.pullMessageService.start();
// Start Consumer rebalance service
this.rebalanceService.start();
//启动内部默认的生产者,用于消费者SendMessageBack,但不会执行MQClientInstance.start(),也就是当前方法不会被执行
this.defaultMQProducer.getDefaultMQProducerImpl().start(false);
log.info("the client factory [{}] start OK", this.clientId);
this.serviceState = ServiceState.RUNNING;
break;
case RUNNING:
break;
case SHUTDOWN_ALREADY:
break;
case START_FAILED:
throw new MQClientException("The Factory object[" + this.getClientId() + "] has been created before, and failed.", null);
default:
break;
}
}
}
6.消费者拉取消息线程服务org.apache.rocketmq.client.impl.consumer.PullMessageService
@Override
public void run() {
log.info(this.getServiceName() + " service started");
while (!this.isStopped()) {
try {
//使用BlockingQueue阻塞队列,当提交了消息拉取请求后,马上执行
PullRequest pullRequest = this.pullRequestQueue.take();
if (pullRequest != null) {
this.pullMessage(pullRequest);
}
} catch (InterruptedException e) {
} catch (Exception e) {
log.error("Pull Message Service Run Method exception", e);
}
}
log.info(this.getServiceName() + " service end");
}
/**
* 拉取消息
*
* @param pullRequest 拉取消息请求
*/
private void pullMessage(final PullRequest pullRequest) {
// 通过消费者组 组名获取消费者
final MQConsumerInner consumer = this.mQClientFactory.selectConsumer(pullRequest.getConsumerGroup());
if (consumer != null) {
DefaultMQPushConsumerImpl impl = (DefaultMQPushConsumerImpl) consumer;
impl.pullMessage(pullRequest);
} else {
log.warn("No matched consumer for the PullRequest {}, drop it", pullRequest);
}
}
7.拉取消息主方法 org.apache.rocketmq.client.impl.consumer.DefaultMQPushConsumerImpl#pullMessage
...
// 执行拉取。如果拉取请求发生异常时,提交延迟拉取消息请求。
try {
this.pullAPIWrapper.pullKernelImpl(//
pullRequest.getMessageQueue(), // 1
subExpression, // 2
subscriptionData.getSubVersion(), // 3
pullRequest.getNextOffset(), // 4
this.defaultMQPushConsumer.getPullBatchSize(), // 5
sysFlag, // 6
commitOffsetValue, // 7
BROKER_SUSPEND_MAX_TIME_MILLIS, // 8
CONSUMER_TIMEOUT_MILLIS_WHEN_SUSPEND, // 9
CommunicationMode.ASYNC, // 10
pullCallback// 11
);
} catch (Exception e) {
log.error("pullKernelImpl exception", e);
this.executePullRequestLater(pullRequest, PULL_TIME_DELAY_MILLS_WHEN_EXCEPTION);
}
...
8.拉取消息
/**
* 拉取消息核心方法
* 拉取消息的开始位置 >= 当前消费进度 ,因为可能消费者还有线程在消费已拉取到的消息
*
* @param mq 消息队列
* @param subExpression 订阅表达式
* @param subVersion 订阅版本号
* @param offset 下次请求从ConsumeQueue拉取的开始位置
* @param maxNums 批量拉 取消息数量
* @param sysFlag 拉取系统标识
* @param commitOffset 提交ConsumeQueue的消费进度
* @param brokerSuspendMaxTimeMillis broker挂起请求最大时间
* @param timeoutMillis 请求broker超时时间
* @param communicationMode 通讯模式
* @param pullCallback 拉取回调
* @return 拉取消息结果。只有通讯模式为同步时,才返回结果,否则返回null。
* @throws MQClientException 当寻找不到 broker 时,或发生其他client异常
* @throws RemotingException 当远程调用发生异常时
* @throws MQBrokerException 当 broker 发生异常时。只有通讯模式为同步时才会发生该异常。
* @throws InterruptedException 当发生中断异常时
*/
protected PullResult pullKernelImpl(
final MessageQueue mq,
final String subExpression,
final long subVersion,
final long offset,
final int maxNums,
final int sysFlag,
final long commitOffset,
final long brokerSuspendMaxTimeMillis,
final long timeoutMillis,
final CommunicationMode communicationMode,
final PullCallback pullCallback
) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
// 获取Broker信息
FindBrokerResult findBrokerResult =
this.mQClientFactory.findBrokerAddressInSubscribe(mq.getBrokerName(), this.recalculatePullFromWhichNode(mq), false);
if (null == findBrokerResult) {
this.mQClientFactory.updateTopicRouteInfoFromNameServer(mq.getTopic());
findBrokerResult = this.mQClientFactory.findBrokerAddressInSubscribe(mq.getBrokerName(), this.recalculatePullFromWhichNode(mq), false);
}
// 请求拉取消息
if (findBrokerResult != null) {
int sysFlagInner = sysFlag;
if (findBrokerResult.isSlave()) { //如果是从Slave拉取消息,则不提交消费进度
sysFlagInner = PullSysFlag.clearCommitOffsetFlag(sysFlagInner);
}
PullMessageRequestHeader requestHeader = new PullMessageRequestHeader();
requestHeader.setConsumerGroup(this.consumerGroup);
requestHeader.setTopic(mq.getTopic());
requestHeader.setQueueId(mq.getQueueId());
requestHeader.setQueueOffset(offset);
requestHeader.setMaxMsgNums(maxNums);
requestHeader.setSysFlag(sysFlagInner);
requestHeader.setCommitOffset(commitOffset);
requestHeader.setSuspendTimeoutMillis(brokerSuspendMaxTimeMillis);
requestHeader.setSubscription(subExpression);
requestHeader.setSubVersion(subVersion);
// 若订阅topic使用过滤类,使用filtersrv获取消息
String brokerAddr = findBrokerResult.getBrokerAddr();
if (PullSysFlag.hasClassFilterFlag(sysFlagInner)) {
brokerAddr = computPullFromWhichFilterServer(mq.getTopic(), brokerAddr);
}
PullResult pullResult = this.mQClientFactory.getMQClientAPIImpl().pullMessage(
brokerAddr,
requestHeader,
timeoutMillis,
communicationMode,
pullCallback);
return pullResult;
}
// Broker信息不存在,则抛出异常
throw new MQClientException("The broker[" + mq.getBrokerName() + "] not exist", null);
}
/**
* 拉取消息
*
* @param addr broker地址
* @param requestHeader 请求头
* @param timeoutMillis 请求超时
* @param communicationMode 通讯方式
* @param pullCallback 回调
* @return 消息。只有通讯模式为同步时,才返回结果,否则返回null。
* @throws RemotingException 当远程调用发生异常时
* @throws MQBrokerException 当 broker 发生异常时。只有通讯模式为同步时才会发生该异常。
* @throws InterruptedException 当发生中断异常时
*/
public PullResult pullMessage(//
final String addr, //
final PullMessageRequestHeader requestHeader, //
final long timeoutMillis, //
final CommunicationMode communicationMode, //
final PullCallback pullCallback//
) throws RemotingException, MQBrokerException, InterruptedException {
RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.PULL_MESSAGE, requestHeader);
switch (communicationMode) {
case ONEWAY:
assert false;
return null;
case ASYNC:
this.pullMessageAsync(addr, request, timeoutMillis, pullCallback);
return null;
case SYNC:
return this.pullMessageSync(addr, request, timeoutMillis);
default:
assert false;
break;
}
return null;
}
public void invokeAsyncImpl(final Channel channel, final RemotingCommand request, final long timeoutMillis,
final InvokeCallback invokeCallback)
throws InterruptedException, RemotingTooMuchRequestException, RemotingTimeoutException, RemotingSendRequestException {
final int opaque = request.getOpaque();
boolean acquired = this.semaphoreAsync.tryAcquire(timeoutMillis, TimeUnit.MILLISECONDS);
if (acquired) {
final SemaphoreReleaseOnlyOnce once = new SemaphoreReleaseOnlyOnce(this.semaphoreAsync);
final ResponseFuture responseFuture = new ResponseFuture(opaque, timeoutMillis, invokeCallback, once);
this.responseTable.put(opaque, responseFuture);
try {
channel.writeAndFlush(request).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture f) throws Exception {
if (f.isSuccess()) {
responseFuture.setSendRequestOK(true);
return;
} else {
responseFuture.setSendRequestOK(false);
}
responseFuture.putResponse(null);
responseTable.remove(opaque);
try {
executeInvokeCallback(responseFuture);
} catch (Throwable e) {
PLOG.warn("excute callback in writeAndFlush addListener, and callback throw", e);
} finally {
responseFuture.release();
}
PLOG.warn("send a request command to channel <{}> failed.", RemotingHelper.parseChannelRemoteAddr(channel));
}
});
} catch (Exception e) {
responseFuture.release();
PLOG.warn("send a request command to channel <" + RemotingHelper.parseChannelRemoteAddr(channel) + "> Exception", e);
throw new RemotingSendRequestException(RemotingHelper.parseChannelRemoteAddr(channel), e);
}
} else {
String info =
String.format("invokeAsyncImpl tryAcquire semaphore timeout, %dms, waiting thread nums: %d semaphoreAsyncValue: %d", //
timeoutMillis, //
this.semaphoreAsync.getQueueLength(), //
this.semaphoreAsync.availablePermits()//
);
PLOG.warn(info);
throw new RemotingTooMuchRequestException(info);
}
}
9.filtersrv 处理请求
@Override
public RemotingCommand processRequest(ChannelHandlerContext ctx, RemotingCommand request) throws Exception {
if (log.isDebugEnabled()) {
log.debug("receive request, {} {} {}",
request.getCode(),
RemotingHelper.parseChannelRemoteAddr(ctx.channel()),
request);
}
switch (request.getCode()) {
case RequestCode.REGISTER_MESSAGE_FILTER_CLASS:
return registerMessageFilterClass(ctx, request);
case RequestCode.PULL_MESSAGE:
return pullMessageForward(ctx, request);
}
return null;
}
10.filtersrv拉取消息
/**
* 拉取消息
*
* @param ctx 拉取消息context
* @param request 拉取消息请求
* @return 响应
* @throws Exception 当发生异常时
*/
private RemotingCommand pullMessageForward(final ChannelHandlerContext ctx, final RemotingCommand request) throws Exception {
final RemotingCommand response = RemotingCommand.createResponseCommand(PullMessageResponseHeader.class);
final PullMessageResponseHeader responseHeader = (PullMessageResponseHeader) response.readCustomHeader();
final PullMessageRequestHeader requestHeader =
(PullMessageRequestHeader) request.decodeCommandCustomHeader(PullMessageRequestHeader.class);
final FilterContext filterContext = new FilterContext();
filterContext.setConsumerGroup(requestHeader.getConsumerGroup());
response.setOpaque(request.getOpaque());
DefaultMQPullConsumer pullConsumer = this.filtersrvController.getDefaultMQPullConsumer();
// 校验Topic过滤类是否完整
final FilterClassInfo findFilterClass = this.filtersrvController.getFilterClassManager().findFilterClass(requestHeader.getConsumerGroup(), requestHeader.getTopic());
if (null == findFilterClass) {
response.setCode(ResponseCode.SYSTEM_ERROR);
response.setRemark("Find Filter class failed, not registered");
return response;
}
if (null == findFilterClass.getMessageFilter()) {
response.setCode(ResponseCode.SYSTEM_ERROR);
response.setRemark("Find Filter class failed, registered but no class");
return response;
}
// 设置下次请求从 Broker主节点。
responseHeader.setSuggestWhichBrokerId(MixAll.MASTER_ID);
MessageQueue mq = new MessageQueue();
mq.setTopic(requestHeader.getTopic());
mq.setQueueId(requestHeader.getQueueId());
mq.setBrokerName(this.filtersrvController.getBrokerName());
long offset = requestHeader.getQueueOffset();
int maxNums = requestHeader.getMaxMsgNums();
final PullCallback pullCallback = new PullCallback() {
@Override
public void onSuccess(PullResult pullResult) {
responseHeader.setMaxOffset(pullResult.getMaxOffset());
responseHeader.setMinOffset(pullResult.getMinOffset());
responseHeader.setNextBeginOffset(pullResult.getNextBeginOffset());
response.setRemark(null);
switch (pullResult.getPullStatus()) {
case FOUND:
response.setCode(ResponseCode.SUCCESS);
List<MessageExt> msgListOK = new ArrayList<MessageExt>();
try {
for (MessageExt msg : pullResult.getMsgFoundList()) {
// 使用过滤类过滤消息
boolean match = findFilterClass.getMessageFilter().match(msg, filterContext);
if (match) {
msgListOK.add(msg);
}
}
if (!msgListOK.isEmpty()) {
returnResponse(requestHeader.getConsumerGroup(), requestHeader.getTopic(), ctx, response, msgListOK);
return;
} else {
response.setCode(ResponseCode.PULL_RETRY_IMMEDIATELY);
}
} catch (Throwable e) {
final String error =
String.format("do Message Filter Exception, ConsumerGroup: %s Topic: %s ",
requestHeader.getConsumerGroup(), requestHeader.getTopic());
log.error(error, e);
response.setCode(ResponseCode.SYSTEM_ERROR);
response.setRemark(error + RemotingHelper.exceptionSimpleDesc(e));
returnResponse(requestHeader.getConsumerGroup(), requestHeader.getTopic(), ctx, response, null);
return;
}
break;
case NO_MATCHED_MSG:
response.setCode(ResponseCode.PULL_RETRY_IMMEDIATELY);
break;
case NO_NEW_MSG:
response.setCode(ResponseCode.PULL_NOT_FOUND);
break;
case OFFSET_ILLEGAL:
response.setCode(ResponseCode.PULL_OFFSET_MOVED);
break;
default:
break;
}
returnResponse(requestHeader.getConsumerGroup(), requestHeader.getTopic(), ctx, response, null);
}
@Override
public void onException(Throwable e) {
response.setCode(ResponseCode.SYSTEM_ERROR);
response.setRemark("Pull Callback Exception, " + RemotingHelper.exceptionSimpleDesc(e));
returnResponse(requestHeader.getConsumerGroup(), requestHeader.getTopic(), ctx, response, null);
return;
}
};
// 拉取消息
pullConsumer.pullBlockIfNotFound(mq, null, offset, maxNums, pullCallback);
return null;
}
11. broker内部拉取消息方法
org.apache.rocketmq.broker.processor.PullMessageProcessor#processRequest(io.netty.channel.Channel, org.apache.rocketmq.remoting.protocol.RemotingCommand, boolean)
/**
* 处理拉取消息请求,返回响应
*
* @param channel channel
* @param request 请求
* @param brokerAllowSuspend broker是否允许挂起
* @return 响应
* @throws RemotingCommandException 当解析请求发生异常时
*/
private RemotingCommand processRequest(final Channel channel, RemotingCommand request, boolean brokerAllowSuspend)
throws RemotingCommandException {
...
// 通过topic,queueId,offset从ConsumeQueue里获取新加入消息的消费信息,然后从CommitLog里提取消息
final GetMessageResult getMessageResult = this.brokerController.getMessageStore().getMessage(requestHeader.getConsumerGroup(), requestHeader.getTopic(),
requestHeader.getQueueId(), requestHeader.getQueueOffset(), requestHeader.getMaxMsgNums(), subscriptionData);
...
12. messageStore获取消息
/**
* 通过topic,queueId,offset从ConsumeQueue里获取新加入消息的消费信息,然后从CommitLog里提取消息
* 从ConsumeQueue里提取Message时会根据tagsCode过滤不符合的Message
* 提取的总数(32)条消息都是符合tag的
*
* @param group 消费分组
* @param topic 主题
* @param queueId 队列编号
* @param offset ConsumeQueue Offset
* @param maxMsgNums 消息数量 32
* @param subscriptionData 订阅信息
* @return 消息结果
*/
@Override
public GetMessageResult getMessage(final String group, final String topic, final int queueId, final long offset, final int maxMsgNums,
final SubscriptionData subscriptionData) {
...
// 获取消费队列(里面存放了队列的索引信息)
ConsumeQueue consumeQueue = findConsumeQueue(topic, queueId);
if (consumeQueue != null) {
...
// 获取队列的索引信息
SelectMappedBufferResult bufferConsumeQueue = consumeQueue.getIndexBuffer(offset);
if (bufferConsumeQueue != null) {
...
for (; i < bufferConsumeQueue.getSize() && i < maxFilterMessageCount; i += ConsumeQueue.CQ_STORE_UNIT_SIZE) {
long offsetPy = bufferConsumeQueue.getByteBuffer().getLong(); // 消息物理位置offset
int sizePy = bufferConsumeQueue.getByteBuffer().getInt(); // 消息长度
long tagsCode = bufferConsumeQueue.getByteBuffer().getLong(); // 消息tagsCode
...
// 判断消息是否在订阅的tag里
if (this.messageFilter.isMessageMatched(subscriptionData, tagsCode)) {
// 从commitLog获取对应消息ByteBuffer(这里才是真正的获取消息)
SelectMappedBufferResult selectResult = this.commitLog.getMessage(offsetPy, sizePy);
if (selectResult != null) {
this.storeStatsService.getGetMessageTransferedMsgCount().incrementAndGet();
getResult.addMessage(selectResult);
status = GetMessageStatus.FOUND;
nextPhyFileStartOffset = Long.MIN_VALUE;
} else {
// 从commitLog无法读取到消息,说明该消息对应的文件(MappedFile)已经删除,计算下一个MappedFile的起始位置
if (getResult.getBufferTotalSize() == 0) {
status = GetMessageStatus.MESSAGE_WAS_REMOVING;
}
nextPhyFileStartOffset = this.commitLog.rollNextFile(offsetPy);
}