/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.rocketmq.client.consumer; import org.apache.rocketmq.client.ClientConfig; import org.apache.rocketmq.client.QueryResult; import org.apache.rocketmq.client.consumer.listener.MessageListener; import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently; import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly; import org.apache.rocketmq.client.consumer.rebalance.AllocateMessageQueueAveragely; import org.apache.rocketmq.client.consumer.store.OffsetStore; import org.apache.rocketmq.client.exception.MQBrokerException; import org.apache.rocketmq.client.exception.MQClientException; import org.apache.rocketmq.client.impl.consumer.DefaultMQPushConsumerImpl; import org.apache.rocketmq.common.MixAll; import org.apache.rocketmq.common.UtilAll; import org.apache.rocketmq.common.consumer.ConsumeFromWhere; import org.apache.rocketmq.common.message.MessageDecoder; import org.apache.rocketmq.common.message.MessageExt; import org.apache.rocketmq.common.message.MessageQueue; import org.apache.rocketmq.common.protocol.heartbeat.MessageModel; import org.apache.rocketmq.remoting.RPCHook; import org.apache.rocketmq.remoting.exception.RemotingException; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * In most scenarios, this is the mostly recommended class to consume messages. * </p> * * Technically speaking, this push client is virtually a wrapper of the underlying pull service. Specifically, on * arrival of messages pulled from brokers, it roughly invokes the registered callback handler to feed the messages. * </p> * * See quickstart/Consumer in the example module for a typical usage. * </p> * * <p> * <strong>Thread Safety:</strong> After initialization, the instance can be regarded as thread-safe. * </p> */ public class DefaultMQPushConsumer extends ClientConfig implements MQPushConsumer { /** * Internal implementation. Most of the functions herein are delegated to it. */ protected final transient DefaultMQPushConsumerImpl defaultMQPushConsumerImpl; /** * Consumers of the same role is required to have exactly same subscriptions and consumerGroup to correctly achieve * load balance. It's required and needs to be globally unique. * </p> * * See <a href="http://rocketmq.incubator.apache.org/docs/core-concept/">here</a> for further discussion. */ private String consumerGroup; /** * Message model defines the way how messages are delivered to each consumer clients. * </p> * * RocketMQ supports two message models: clustering and broadcasting. If clustering is set, consumer clients with * the same {@link #consumerGroup} would only consume shards of the messages subscribed, which achieves load * balances; Conversely, if the broadcasting is set, each consumer client will consume all subscribed messages * separately. * </p> * * This field defaults to clustering. */ private MessageModel messageModel = MessageModel.CLUSTERING; /** * Consuming point on consumer booting. * </p> * * There are three consuming points: * <ul> * <li> * <code>CONSUME_FROM_LAST_OFFSET</code>: consumer clients pick up where it stopped previously. * If it were a newly booting up consumer client, according aging of the consumer group, there are two * cases: * <ol> * <li> * if the consumer group is created so recently that the earliest message being subscribed has yet * expired, which means the consumer group represents a lately launched business, consuming will * start from the very beginning; * </li> * <li> * if the earliest message being subscribed has expired, consuming will start from the latest * messages, meaning messages born prior to the booting timestamp would be ignored. * </li> * </ol> * </li> * <li> * <code>CONSUME_FROM_FIRST_OFFSET</code>: Consumer client will start from earliest messages available. * </li> * <li> * <code>CONSUME_FROM_TIMESTAMP</code>: Consumer client will start from specified timestamp, which means * messages born prior to {@link #consumeTimestamp} will be ignored * </li> * </ul> */ private ConsumeFromWhere consumeFromWhere = ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET; /** * Backtracking consumption time with second precision. Time format is * 20131223171201<br> * Implying Seventeen twelve and 01 seconds on December 23, 2013 year<br> * Default backtracking consumption time Half an hour ago. */ private String consumeTimestamp = UtilAll.timeMillisToHumanString3(System.currentTimeMillis() - (1000 * 60 * 30)); /** * Queue allocation algorithm specifying how message queues are allocated to each consumer clients. */ private AllocateMessageQueueStrategy allocateMessageQueueStrategy; /** * Subscription relationship */ private Map<String /* topic */, String /* sub expression */> subscription = new HashMap<>(); /** * Message listener */ private MessageListener messageListener; /** * Offset Storage */ private OffsetStore offsetStore; /** * Minimum consumer thread number */ private int consumeThreadMin = 20; /** * Max consumer thread number */ private int consumeThreadMax = 64; /** * Threshold for dynamic adjustment of the number of thread pool */ private long adjustThreadPoolNumsThreshold = 100000; /** * Concurrently max span offset.it has no effect on sequential consumption */ private int consumeConcurrentlyMaxSpan = 2000; /** * Flow control threshold */ private int pullThresholdForQueue = 1000; /** * Message pull Interval */ private long pullInterval = 0; /** * Batch consumption size */ private int consumeMessageBatchMaxSize = 1; /** * Batch pull size */ private int pullBatchSize = 32; /** * Whether update subscription relationship when every pull */ private boolean postSubscriptionWhenPull = false; /** * Whether the unit of subscription group */ private boolean unitMode = false; /** * Max re-consume times. -1 means 16 times. * </p> * * If messages are re-consumed more than {@link #maxReconsumeTimes} before success, it's be directed to a deletion * queue waiting. */ private int maxReconsumeTimes = -1; /** * Suspending pulling time for cases requiring slow pulling like flow-control scenario. */ private long suspendCurrentQueueTimeMillis = 1000; /** * Maximum amount of time in minutes a message may block the consuming thread. */ private long consumeTimeout = 15; /** * Default constructor. */ public DefaultMQPushConsumer() { this(MixAll.DEFAULT_CONSUMER_GROUP, null, new AllocateMessageQueueAveragely()); } /** * Constructor specifying consumer group, RPC hook and message queue allocating algorithm. * @param consumerGroup Consume queue. * @param rpcHook RPC hook to execute before each remoting command. * @param allocateMessageQueueStrategy message queue allocating algorithm. */ public DefaultMQPushConsumer(final String consumerGroup, RPCHook rpcHook, AllocateMessageQueueStrategy allocateMessageQueueStrategy) { this.consumerGroup = consumerGroup; this.allocateMessageQueueStrategy = allocateMessageQueueStrategy; defaultMQPushConsumerImpl = new DefaultMQPushConsumerImpl(this, rpcHook); } /** * Constructor specifying RPC hook. * @param rpcHook RPC hook to execute before each remoting command. */ public DefaultMQPushConsumer(RPCHook rpcHook) { this(MixAll.DEFAULT_CONSUMER_GROUP, rpcHook, new AllocateMessageQueueAveragely()); } /** * Constructor specifying consumer group. * @param consumerGroup Consumer group. */ public DefaultMQPushConsumer(final String consumerGroup) { this(consumerGroup, null, new AllocateMessageQueueAveragely()); } @Override public void createTopic(String key, String newTopic, int queueNum) throws MQClientException { createTopic(key, newTopic, queueNum, 0); } @Override public void createTopic(String key, String newTopic, int queueNum, int topicSysFlag) throws MQClientException { this.defaultMQPushConsumerImpl.createTopic(key, newTopic, queueNum, topicSysFlag); } @Override public long searchOffset(MessageQueue mq, long timestamp) throws MQClientException { return this.defaultMQPushConsumerImpl.searchOffset(mq, timestamp); } @Override public long maxOffset(MessageQueue mq) throws MQClientException { return this.defaultMQPushConsumerImpl.maxOffset(mq); } @Override public long minOffset(MessageQueue mq) throws MQClientException { return this.defaultMQPushConsumerImpl.minOffset(mq); } @Override public long earliestMsgStoreTime(MessageQueue mq) throws MQClientException { return this.defaultMQPushConsumerImpl.earliestMsgStoreTime(mq); } @Override public MessageExt viewMessage(String offsetMsgId) throws RemotingException, MQBrokerException, InterruptedException, MQClientException { return this.defaultMQPushConsumerImpl.viewMessage(offsetMsgId); } @Override public QueryResult queryMessage(String topic, String key, int maxNum, long begin, long end) throws MQClientException, InterruptedException { return this.defaultMQPushConsumerImpl.queryMessage(topic, key, maxNum, begin, end); } @Override public MessageExt viewMessage(String topic, String msgId) throws RemotingException, MQBrokerException, InterruptedException, MQClientException { try { MessageDecoder.decodeMessageId(msgId); return this.viewMessage(msgId); } catch (Exception e) { // Ignore } return this.defaultMQPushConsumerImpl.queryMessageByUniqKey(topic, msgId); } public AllocateMessageQueueStrategy getAllocateMessageQueueStrategy() { return allocateMessageQueueStrategy; } public void setAllocateMessageQueueStrategy(AllocateMessageQueueStrategy allocateMessageQueueStrategy) { this.allocateMessageQueueStrategy = allocateMessageQueueStrategy; } public int getConsumeConcurrentlyMaxSpan() { return consumeConcurrentlyMaxSpan; } public void setConsumeConcurrentlyMaxSpan(int consumeConcurrentlyMaxSpan) { this.consumeConcurrentlyMaxSpan = consumeConcurrentlyMaxSpan; } public ConsumeFromWhere getConsumeFromWhere() { return consumeFromWhere; } public void setConsumeFromWhere(ConsumeFromWhere consumeFromWhere) { this.consumeFromWhere = consumeFromWhere; } public int getConsumeMessageBatchMaxSize() { return consumeMessageBatchMaxSize; } public void setConsumeMessageBatchMaxSize(int consumeMessageBatchMaxSize) { this.consumeMessageBatchMaxSize = consumeMessageBatchMaxSize; } public String getConsumerGroup() { return consumerGroup; } public void setConsumerGroup(String consumerGroup) { this.consumerGroup = consumerGroup; } public int getConsumeThreadMax() { return consumeThreadMax; } public void setConsumeThreadMax(int consumeThreadMax) { this.consumeThreadMax = consumeThreadMax; } public int getConsumeThreadMin() { return consumeThreadMin; } public void setConsumeThreadMin(int consumeThreadMin) { this.consumeThreadMin = consumeThreadMin; } public DefaultMQPushConsumerImpl getDefaultMQPushConsumerImpl() { return defaultMQPushConsumerImpl; } public MessageListener getMessageListener() { return messageListener; } public void setMessageListener(MessageListener messageListener) { this.messageListener = messageListener; } public MessageModel getMessageModel() { return messageModel; } public void setMessageModel(MessageModel messageModel) { this.messageModel = messageModel; } public int getPullBatchSize() { return pullBatchSize; } public void setPullBatchSize(int pullBatchSize) { this.pullBatchSize = pullBatchSize; } public long getPullInterval() { return pullInterval; } public void setPullInterval(long pullInterval) { this.pullInterval = pullInterval; } public int getPullThresholdForQueue() { return pullThresholdForQueue; } public void setPullThresholdForQueue(int pullThresholdForQueue) { this.pullThresholdForQueue = pullThresholdForQueue; } public Map<String, String> getSubscription() { return subscription; } public void setSubscription(Map<String, String> subscription) { this.subscription = subscription; } /** * Send message back to broker which will be re-delivered in future. * @param msg Message to send back. * @param delayLevel delay level. * @throws RemotingException if there is any network-tier error. * @throws MQBrokerException if there is any broker error. * @throws InterruptedException if the thread is interrupted. * @throws MQClientException if there is any client error. */ @Override public void sendMessageBack(MessageExt msg, int delayLevel) throws RemotingException, MQBrokerException, InterruptedException, MQClientException { this.defaultMQPushConsumerImpl.sendMessageBack(msg, delayLevel, null); } /** * Send message back to the broker whose name is <code>brokerName</code> and the message will be re-delivered in * future. * * @param msg Message to send back. * @param delayLevel delay level. * @param brokerName broker name. * @throws RemotingException if there is any network-tier error. * @throws MQBrokerException if there is any broker error. * @throws InterruptedException if the thread is interrupted. * @throws MQClientException if there is any client error. */ @Override public void sendMessageBack(MessageExt msg, int delayLevel, String brokerName) throws RemotingException, MQBrokerException, InterruptedException, MQClientException { this.defaultMQPushConsumerImpl.sendMessageBack(msg, delayLevel, brokerName); } @Override public Set<MessageQueue> fetchSubscribeMessageQueues(String topic) throws MQClientException { return this.defaultMQPushConsumerImpl.fetchSubscribeMessageQueues(topic); } /** * This method gets internal infrastructure readily to serve. Instances must call this method after configuration. * @throws MQClientException if there is any client error. */ @Override public void start() throws MQClientException { this.defaultMQPushConsumerImpl.start(); } /** * Shut down this client and releasing underlying resources. */ @Override public void shutdown() { this.defaultMQPushConsumerImpl.shutdown(); } @Override @Deprecated public void registerMessageListener(MessageListener messageListener) { this.messageListener = messageListener; this.defaultMQPushConsumerImpl.registerMessageListener(messageListener); } /** * Register a callback to execute on message arrival for concurrent consuming. * * @param messageListener message handling callback. */ @Override public void registerMessageListener(MessageListenerConcurrently messageListener) { this.messageListener = messageListener; this.defaultMQPushConsumerImpl.registerMessageListener(messageListener); } /** * Register a callback to execute on message arrival for orderly consuming. * * @param messageListener message handling callback. */ @Override public void registerMessageListener(MessageListenerOrderly messageListener) { this.messageListener = messageListener; this.defaultMQPushConsumerImpl.registerMessageListener(messageListener); } /** * Subscribe a topic to consuming subscription. * * @param topic topic to subscribe. * @param subExpression subscription expression.it only support or operation such as "tag1 || tag2 || tag3" <br> * if null or * expression,meaning subscribe all * @throws MQClientException if there is any client error. */ @Override public void subscribe(String topic, String subExpression) throws MQClientException { this.defaultMQPushConsumerImpl.subscribe(topic, subExpression); } /** * Subscribe a topic to consuming subscription. * @param topic topic to consume. * @param fullClassName full class name,must extend org.apache.rocketmq.common.filter. MessageFilter * @param filterClassSource class source code,used UTF-8 file encoding,must be responsible for your code safety * @throws MQClientException */ @Override public void subscribe(String topic, String fullClassName, String filterClassSource) throws MQClientException { this.defaultMQPushConsumerImpl.subscribe(topic, fullClassName, filterClassSource); } /** * Un-subscribe the specified topic from subscription. * @param topic message topic */ @Override public void unsubscribe(String topic) { this.defaultMQPushConsumerImpl.unsubscribe(topic); } /** * Update the message consuming thread core pool size. * * @param corePoolSize new core pool size. */ @Override public void updateCorePoolSize(int corePoolSize) { this.defaultMQPushConsumerImpl.updateCorePoolSize(corePoolSize); } /** * Suspend pulling new messages. */ @Override public void suspend() { this.defaultMQPushConsumerImpl.suspend(); } /** * Resume pulling. */ @Override public void resume() { this.defaultMQPushConsumerImpl.resume(); } public OffsetStore getOffsetStore() { return offsetStore; } public void setOffsetStore(OffsetStore offsetStore) { this.offsetStore = offsetStore; } public String getConsumeTimestamp() { return consumeTimestamp; } public void setConsumeTimestamp(String consumeTimestamp) { this.consumeTimestamp = consumeTimestamp; } public boolean isPostSubscriptionWhenPull() { return postSubscriptionWhenPull; } public void setPostSubscriptionWhenPull(boolean postSubscriptionWhenPull) { this.postSubscriptionWhenPull = postSubscriptionWhenPull; } public boolean isUnitMode() { return unitMode; } public void setUnitMode(boolean isUnitMode) { this.unitMode = isUnitMode; } public long getAdjustThreadPoolNumsThreshold() { return adjustThreadPoolNumsThreshold; } public void setAdjustThreadPoolNumsThreshold(long adjustThreadPoolNumsThreshold) { this.adjustThreadPoolNumsThreshold = adjustThreadPoolNumsThreshold; } public int getMaxReconsumeTimes() { return maxReconsumeTimes; } public void setMaxReconsumeTimes(final int maxReconsumeTimes) { this.maxReconsumeTimes = maxReconsumeTimes; } public long getSuspendCurrentQueueTimeMillis() { return suspendCurrentQueueTimeMillis; } public void setSuspendCurrentQueueTimeMillis(final long suspendCurrentQueueTimeMillis) { this.suspendCurrentQueueTimeMillis = suspendCurrentQueueTimeMillis; } public long getConsumeTimeout() { return consumeTimeout; } public void setConsumeTimeout(final long consumeTimeout) { this.consumeTimeout = consumeTimeout; } }