Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ExecutorService;

/**
* In most scenarios, this is the mostly recommended class to consume messages.
Expand Down Expand Up @@ -160,6 +161,8 @@ public class DefaultMQPushConsumer extends ClientConfig implements MQPushConsume
*/
private int consumeThreadMin = 20;

private ExecutorService consumeExecutor;

/**
* Max consumer thread number
*/
Expand Down Expand Up @@ -558,6 +561,37 @@ public void setConsumerGroup(String consumerGroup) {
this.consumerGroup = consumerGroup;
}

/**
* Returns the externally managed consumption executor, or null for a dedicated pool.
*/
public ExecutorService getConsumeExecutor() {
return consumeExecutor;
}

/**
* Sets an externally managed executor before starting this consumer.
*
* <p>This is an advanced API intended for controlled integrations such as Proxy. Ordinary
* applications should use the default consumption pool instead of injecting an executor.
* The executor may be shared with other consumers. Virtual-thread executors are also supported
* when supplied by applications running on a compatible JDK.
*
* <p>While consumers are running, the external executor must avoid capacity-based rejection
* and must not discard or cancel pending consumption tasks. The client does not guarantee
* automatic recovery from rejected tasks. Discarding or cancelling tasks can retain cached
* messages and pin consumption offsets, eventually stalling consumption.
*
* <p>The caller controls concurrency and owns the executor's lifecycle. This consumer never
* shuts down or resizes an external executor. Consumer shutdown does not await or cancel tasks
* submitted to it; the caller must stop all consumers using the executor before shutting it
* down and awaiting its termination.
*
* @param consumeExecutor external executor, or null to use the default dedicated pool
*/
public void setConsumeExecutor(ExecutorService consumeExecutor) {
this.consumeExecutor = consumeExecutor;
}

public int getConsumeThreadMax() {
return consumeThreadMax;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* 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.impl.consumer;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.common.utils.ThreadUtils;

public abstract class AbstractConsumeMessageService implements ConsumeMessageService {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里感觉引入了不必要的抽象

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

不然要写 4 次,+ 100 行

protected final DefaultMQPushConsumer defaultMQPushConsumer;
protected final ExecutorService consumeExecutor;
private final boolean ownsConsumeExecutor;

protected AbstractConsumeMessageService(DefaultMQPushConsumer defaultMQPushConsumer, ThreadFactory threadFactory) {
this.defaultMQPushConsumer = defaultMQPushConsumer;
ExecutorService externalExecutor = defaultMQPushConsumer.getConsumeExecutor();
this.ownsConsumeExecutor = externalExecutor == null;
if (this.ownsConsumeExecutor) {
this.consumeExecutor = new ThreadPoolExecutor(
defaultMQPushConsumer.getConsumeThreadMin(),
defaultMQPushConsumer.getConsumeThreadMax(),
1000 * 60,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(),
threadFactory);
} else {
this.consumeExecutor = externalExecutor;
}
}

protected static String getConsumerGroupTag(String consumerGroup) {
return (consumerGroup.length() > 100 ? consumerGroup.substring(0, 100) : consumerGroup) + "_";
}

protected void shutdownConsumeExecutor(long awaitTerminateMillis) {
if (this.ownsConsumeExecutor) {
ThreadUtils.shutdownGracefully(this.consumeExecutor, awaitTerminateMillis, TimeUnit.MILLISECONDS);
}
}

@Override
public void updateCorePoolSize(int corePoolSize) {
if (this.ownsConsumeExecutor
&& corePoolSize > 0
&& corePoolSize <= Short.MAX_VALUE
&& corePoolSize < this.defaultMQPushConsumer.getConsumeThreadMax()) {
((ThreadPoolExecutor) this.consumeExecutor).setCorePoolSize(corePoolSize);
}
}

@Override
public void incCorePoolSize() {
}

@Override
public void decCorePoolSize() {
}

@Override
public int getCorePoolSize() {
return this.ownsConsumeExecutor ? ((ThreadPoolExecutor) this.consumeExecutor).getCorePoolSize() : -1;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,10 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
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.ConsumeReturnType;
Expand All @@ -42,42 +38,30 @@
import org.apache.rocketmq.common.message.MessageAccessor;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.common.utils.ThreadUtils;
import org.apache.rocketmq.remoting.protocol.body.CMResult;
import org.apache.rocketmq.remoting.protocol.body.ConsumeMessageDirectlyResult;
import org.apache.rocketmq.logging.org.slf4j.Logger;
import org.apache.rocketmq.logging.org.slf4j.LoggerFactory;

public class ConsumeMessageConcurrentlyService implements ConsumeMessageService {
public class ConsumeMessageConcurrentlyService extends AbstractConsumeMessageService {
private static final Logger log = LoggerFactory.getLogger(ConsumeMessageConcurrentlyService.class);
private final DefaultMQPushConsumerImpl defaultMQPushConsumerImpl;
private final DefaultMQPushConsumer defaultMQPushConsumer;
private final MessageListenerConcurrently messageListener;
private final BlockingQueue<Runnable> consumeRequestQueue;
private final ThreadPoolExecutor consumeExecutor;
private final String consumerGroup;

private final ScheduledExecutorService scheduledExecutorService;
private final ScheduledExecutorService cleanExpireMsgExecutors;

public ConsumeMessageConcurrentlyService(DefaultMQPushConsumerImpl defaultMQPushConsumerImpl,
MessageListenerConcurrently messageListener) {
super(defaultMQPushConsumerImpl.getDefaultMQPushConsumer(), new ThreadFactoryImpl("ConsumeMessageThread_"
+ getConsumerGroupTag(defaultMQPushConsumerImpl.getDefaultMQPushConsumer().getConsumerGroup())));
this.defaultMQPushConsumerImpl = defaultMQPushConsumerImpl;
this.messageListener = messageListener;

this.defaultMQPushConsumer = this.defaultMQPushConsumerImpl.getDefaultMQPushConsumer();
this.consumerGroup = this.defaultMQPushConsumer.getConsumerGroup();
this.consumeRequestQueue = new LinkedBlockingQueue<>();

String consumerGroupTag = (consumerGroup.length() > 100 ? consumerGroup.substring(0, 100) : consumerGroup) + "_";
this.consumeExecutor = new ThreadPoolExecutor(
this.defaultMQPushConsumer.getConsumeThreadMin(),
this.defaultMQPushConsumer.getConsumeThreadMax(),
1000 * 60,
TimeUnit.MILLISECONDS,
this.consumeRequestQueue,
new ThreadFactoryImpl("ConsumeMessageThread_" + consumerGroupTag));

String consumerGroupTag = getConsumerGroupTag(consumerGroup);
this.scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl("ConsumeMessageScheduledThread_" + consumerGroupTag));
this.cleanExpireMsgExecutors = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl("CleanExpireMsgScheduledThread_" + consumerGroupTag));
}
Expand All @@ -99,34 +83,10 @@ public void run() {

public void shutdown(long awaitTerminateMillis) {
this.scheduledExecutorService.shutdown();
ThreadUtils.shutdownGracefully(this.consumeExecutor, awaitTerminateMillis, TimeUnit.MILLISECONDS);
shutdownConsumeExecutor(awaitTerminateMillis);
this.cleanExpireMsgExecutors.shutdown();
}

@Override
public void updateCorePoolSize(int corePoolSize) {
if (corePoolSize > 0
&& corePoolSize <= Short.MAX_VALUE
&& corePoolSize < this.defaultMQPushConsumer.getConsumeThreadMax()) {
this.consumeExecutor.setCorePoolSize(corePoolSize);
}
}

@Override
public void incCorePoolSize() {

}

@Override
public void decCorePoolSize() {

}

@Override
public int getCorePoolSize() {
return this.consumeExecutor.getCorePoolSize();
}

@Override
public ConsumeMessageDirectlyResult consumeMessageDirectly(MessageExt msg, String brokerName) {
ConsumeMessageDirectlyResult result = new ConsumeMessageDirectlyResult();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,10 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus;
import org.apache.rocketmq.client.consumer.listener.ConsumeReturnType;
Expand All @@ -42,46 +38,34 @@
import org.apache.rocketmq.common.message.MessageConst;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.common.utils.ThreadUtils;
import org.apache.rocketmq.remoting.protocol.NamespaceUtil;
import org.apache.rocketmq.remoting.protocol.body.CMResult;
import org.apache.rocketmq.remoting.protocol.body.ConsumeMessageDirectlyResult;
import org.apache.rocketmq.remoting.protocol.heartbeat.MessageModel;
import org.apache.rocketmq.logging.org.slf4j.Logger;
import org.apache.rocketmq.logging.org.slf4j.LoggerFactory;

public class ConsumeMessageOrderlyService implements ConsumeMessageService {
public class ConsumeMessageOrderlyService extends AbstractConsumeMessageService {
private static final Logger log = LoggerFactory.getLogger(ConsumeMessageOrderlyService.class);
private final static long MAX_TIME_CONSUME_CONTINUOUSLY =
Long.parseLong(System.getProperty("rocketmq.client.maxTimeConsumeContinuously", "60000"));
private final DefaultMQPushConsumerImpl defaultMQPushConsumerImpl;
private final DefaultMQPushConsumer defaultMQPushConsumer;
private final MessageListenerOrderly messageListener;
private final BlockingQueue<Runnable> consumeRequestQueue;
private final ThreadPoolExecutor consumeExecutor;
private final String consumerGroup;
private final MessageQueueLock messageQueueLock = new MessageQueueLock();
private final ScheduledExecutorService scheduledExecutorService;
private volatile boolean stopped = false;

public ConsumeMessageOrderlyService(DefaultMQPushConsumerImpl defaultMQPushConsumerImpl,
MessageListenerOrderly messageListener) {
super(defaultMQPushConsumerImpl.getDefaultMQPushConsumer(), new ThreadFactoryImpl("ConsumeMessageThread_"
+ getConsumerGroupTag(defaultMQPushConsumerImpl.getDefaultMQPushConsumer().getConsumerGroup())));
this.defaultMQPushConsumerImpl = defaultMQPushConsumerImpl;
this.messageListener = messageListener;

this.defaultMQPushConsumer = this.defaultMQPushConsumerImpl.getDefaultMQPushConsumer();
this.consumerGroup = this.defaultMQPushConsumer.getConsumerGroup();
this.consumeRequestQueue = new LinkedBlockingQueue<>();

String consumerGroupTag = (consumerGroup.length() > 100 ? consumerGroup.substring(0, 100) : consumerGroup) + "_";
this.consumeExecutor = new ThreadPoolExecutor(
this.defaultMQPushConsumer.getConsumeThreadMin(),
this.defaultMQPushConsumer.getConsumeThreadMax(),
1000 * 60,
TimeUnit.MILLISECONDS,
this.consumeRequestQueue,
new ThreadFactoryImpl("ConsumeMessageThread_" + consumerGroupTag));

String consumerGroupTag = getConsumerGroupTag(consumerGroup);
this.scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl("ConsumeMessageScheduledThread_" + consumerGroupTag));
}

Expand All @@ -105,7 +89,7 @@ public void run() {
public void shutdown(long awaitTerminateMillis) {
this.stopped = true;
this.scheduledExecutorService.shutdown();
ThreadUtils.shutdownGracefully(this.consumeExecutor, awaitTerminateMillis, TimeUnit.MILLISECONDS);
shutdownConsumeExecutor(awaitTerminateMillis);
if (MessageModel.CLUSTERING.equals(this.defaultMQPushConsumerImpl.messageModel())) {
this.unlockAllMQ();
}
Expand All @@ -115,28 +99,6 @@ public synchronized void unlockAllMQ() {
this.defaultMQPushConsumerImpl.getRebalanceImpl().unlockAll(false);
}

@Override
public void updateCorePoolSize(int corePoolSize) {
if (corePoolSize > 0
&& corePoolSize <= Short.MAX_VALUE
&& corePoolSize < this.defaultMQPushConsumer.getConsumeThreadMax()) {
this.consumeExecutor.setCorePoolSize(corePoolSize);
}
}

@Override
public void incCorePoolSize() {
}

@Override
public void decCorePoolSize() {
}

@Override
public int getCorePoolSize() {
return this.consumeExecutor.getCorePoolSize();
}

@Override
public ConsumeMessageDirectlyResult consumeMessageDirectly(MessageExt msg, String brokerName) {
ConsumeMessageDirectlyResult result = new ConsumeMessageDirectlyResult();
Expand Down
Loading
Loading