/** * $Id: PullMessageProcessor.java 1831 2013-05-16 01:39:51Z shijia.wxr $ */ package com.alibaba.rocketmq.broker.processor; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.FileRegion; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.rocketmq.broker.BrokerController; import com.alibaba.rocketmq.broker.longpolling.PullRequest; import com.alibaba.rocketmq.broker.pagecache.ManyMessageTransfer; import com.alibaba.rocketmq.common.MixAll; import com.alibaba.rocketmq.common.TopicConfig; import com.alibaba.rocketmq.common.protocol.MQProtos.MQResponseCode; import com.alibaba.rocketmq.common.protocol.header.PullMessageRequestHeader; import com.alibaba.rocketmq.common.protocol.header.PullMessageResponseHeader; import com.alibaba.rocketmq.common.sysflag.PullSysFlag; import com.alibaba.rocketmq.remoting.exception.RemotingCommandException; import com.alibaba.rocketmq.remoting.netty.NettyRequestProcessor; import com.alibaba.rocketmq.remoting.protocol.RemotingCommand; import com.alibaba.rocketmq.remoting.protocol.RemotingProtos.ResponseCode; import com.alibaba.rocketmq.store.GetMessageResult; /** * ����Ϣ������ * * @author vintage.wang@gmail.com shijia.wxr@taobao.com * */ public class PullMessageProcessor implements NettyRequestProcessor { private static final Logger log = LoggerFactory.getLogger(MixAll.BrokerLoggerName); private final BrokerController brokerController; public PullMessageProcessor(final BrokerController brokerController) { this.brokerController = brokerController; } @Override public RemotingCommand processRequest(final ChannelHandlerContext ctx, RemotingCommand request) throws RemotingCommandException { return this.processRequest(ctx.channel(), request, true); } public void excuteRequestWhenWakeup(final Channel channel, final RemotingCommand request) throws RemotingCommandException { final RemotingCommand response = this.processRequest(channel, request, false); if (response != null) { response.setOpaque(request.getOpaque()); response.markResponseType(); try { channel.write(response).addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { if (!future.isSuccess()) { log.error("processRequestWrapper response to " + future.channel().remoteAddress() + " failed", future.cause()); log.error(request.toString()); log.error(response.toString()); } } }); } catch (Throwable e) { log.error("processRequestWrapper process request over, but response failed", e); log.error(request.toString()); log.error(response.toString()); } } } private RemotingCommand processRequest(final Channel channel, RemotingCommand request, boolean brokerAllowSuspend) throws RemotingCommandException { RemotingCommand response = RemotingCommand.createResponseCommand(PullMessageResponseHeader.class); final PullMessageResponseHeader responseHeader = (PullMessageResponseHeader) response.getCustomHeader(); final PullMessageRequestHeader requestHeader = (PullMessageRequestHeader) request.decodeCommandCustomHeader(PullMessageRequestHeader.class); // ����ʹ��sendfile�����Ա���Ҫ���� response.setOpaque(request.getOpaque()); if (log.isDebugEnabled()) { log.debug("receive PullMessage request command, " + request); } // ���BrokerȨ�� if (!MixAll.isReadable(this.brokerController.getBrokerConfig().getBrokerPermission())) { response.setCode(MQResponseCode.NO_PERMISSION_VALUE); response.setRemark("the broker[" + this.brokerController.getBrokerConfig().getBrokerIP1() + "] pulling message is forbidden"); return response; } final boolean hasSuspendFlag = PullSysFlag.hasSuspendFlag(requestHeader.getSysFlag()); final boolean hasCommitOffsetFlag = PullSysFlag.hasCommitOffsetFlag(requestHeader.getSysFlag()); final boolean hasSubscriptionFlag = PullSysFlag.hasSubscriptionFlag(requestHeader.getSysFlag()); final long suspendTimeoutMillisLong = hasSuspendFlag ? requestHeader.getSuspendTimeoutMillis() : 0; // ���topic�Ƿ���� TopicConfig topicConfig = this.brokerController.getTopicConfigManager().selectTopicConfig(requestHeader.getTopic()); if (null == topicConfig) { log.error("the topic " + requestHeader.getTopic() + " not exist, producer: " + channel.remoteAddress()); response.setCode(MQResponseCode.TOPIC_NOT_EXIST_VALUE); response.setRemark("topic not exist, apply first please!"); return response; } // ���topicȨ�� if (!MixAll.isReadable(topicConfig.getPerm())) { response.setCode(MQResponseCode.NO_PERMISSION_VALUE); response.setRemark("the topic[" + requestHeader.getTopic() + "] pulling message is forbidden"); return response; } // ��������Ч�� if (requestHeader.getQueueId() < 0 || requestHeader.getQueueId() >= topicConfig.getReadQueueNums()) { String errorInfo = "queueId[" + requestHeader.getQueueId() + "] is illagal, topicConfig.readQueueNums: " + topicConfig.getReadQueueNums() + " consumer: " + channel.remoteAddress(); log.warn(errorInfo); response.setCode(ResponseCode.SYSTEM_ERROR_VALUE); response.setRemark(errorInfo); return response; } final GetMessageResult getMessageResult = this.brokerController.getMessageStore().getMessage(requestHeader.getTopic(), requestHeader.getQueueId(), requestHeader.getQueueOffset(), requestHeader.getMaxMsgNums(), null); if (getMessageResult != null) { response.setRemark(getMessageResult.getStatus().name()); responseHeader.setNextBeginOffset(getMessageResult.getNextBeginOffset()); responseHeader.setSuggestPullingFromSlave(getMessageResult.isSuggestPullingFromSlave()); responseHeader.setMinOffset(getMessageResult.getMinOffset()); responseHeader.setMaxOffset(getMessageResult.getMaxOffset()); switch (getMessageResult.getStatus()) { case FOUND: response.setCode(ResponseCode.SUCCESS_VALUE); break; case MESSAGE_WAS_REMOVING: response.setCode(MQResponseCode.PULL_RETRY_IMMEDIATELY_VALUE); break; // ����������ֵ����ʾ��������ʱû��������У�Ӧ�����̽��ͻ���Offset����Ϊ0 case NO_MATCHED_LOGIC_QUEUE: case NO_MESSAGE_IN_QUEUE: response.setCode(MQResponseCode.PULL_NOT_FOUND_VALUE); break; case NO_MATCHED_MESSAGE: response.setCode(MQResponseCode.PULL_RETRY_IMMEDIATELY_VALUE); break; case OFFSET_FOUND_NULL: response.setCode(MQResponseCode.PULL_NOT_FOUND_VALUE); break; case OFFSET_OVERFLOW_BADLY: response.setCode(MQResponseCode.PULL_OFFSET_MOVED_VALUE); log.info("the request offset: " + requestHeader.getQueueOffset() + " over flow badly, broker max offset: " + getMessageResult.getMaxOffset() + ", consumer: " + channel.remoteAddress()); break; case OFFSET_OVERFLOW_ONE: response.setCode(MQResponseCode.PULL_NOT_FOUND_VALUE); break; case OFFSET_TOO_SMALL: response.setCode(MQResponseCode.PULL_OFFSET_MOVED_VALUE); log.info("the request offset: " + requestHeader.getQueueOffset() + " too small, broker min offset: " + getMessageResult.getMinOffset() + ", consumer: " + channel.remoteAddress()); break; default: assert false; break; } switch (response.getCode()) { case ResponseCode.SUCCESS_VALUE: try { FileRegion fileRegion = new ManyMessageTransfer(response.encodeHeader(getMessageResult.getBufferTotalSize()), getMessageResult); channel.sendFile(fileRegion).addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { getMessageResult.release(); if (!future.isSuccess()) { log.error("transfer many message by pagecache failed, " + channel.remoteAddress(), future.cause()); } } }); } catch (Throwable e) { log.error("", e); getMessageResult.release(); } response = null; break; case MQResponseCode.PULL_NOT_FOUND_VALUE: // ����ѯ if (brokerAllowSuspend && hasSuspendFlag) { PullRequest pullRequest = new PullRequest(request, channel, suspendTimeoutMillisLong, this.brokerController .getMessageStore().now(), requestHeader.getQueueOffset()); this.brokerController.getPullRequestHoldService().suspendPullRequest(requestHeader.getTopic(), requestHeader.getQueueId(), pullRequest); response = null; break; } // ��Consumer����Ӧ�� case MQResponseCode.PULL_RETRY_IMMEDIATELY_VALUE: case MQResponseCode.PULL_OFFSET_MOVED_VALUE: break; default: assert false; } } else { response.setCode(ResponseCode.SYSTEM_ERROR_VALUE); response.setRemark("store getMessage return null"); } // �洢Consumer���ѽ��� if (brokerAllowSuspend) { // ˵�����״ε��ã�����ڳ���ѯ֪ͨ if (hasCommitOffsetFlag) { this.brokerController.getConsumerOffsetManager().commitOffset(requestHeader.getConsumerGroup(), requestHeader.getTopic(), requestHeader.getQueueId(), requestHeader.getCommitOffset()); } } return response; } }