优化MessageConsumer

This commit is contained in:
redkale
2023-10-05 19:47:40 +08:00
parent f4b4609acc
commit e280061dff
8 changed files with 189 additions and 127 deletions

View File

@@ -67,13 +67,14 @@
MQ管理接口配置
不同MQ节点所配置的MQ集群不能重复。
MQ跟着协议走所以mq的属性值需要被赋值在rest节点上, 由于SncpServlet是自动生成的故SNCP协议下mq属性值被赋值在service/services节点上
name: 服务的名称用于监控识别多个mq节点时只能有一个name为空的节点mq.name不能重复,命名规则: 字母、数字、下划线
type 实现类名必须是org.redkale.mq.MessageAgent的子类
coder: MessageRecord的解析器类必须是org.redkale.mq.MessageCoder<MessageRecord>的实现类,
可对数据包进行加密解密默认值org.redkale.mq.MessageRecordCoder
name: 服务的名称用于监控识别多个mq节点时只能有一个name为空的节点mq.name不能重复,命名规则: 字母、数字、下划线
type 实现类名必须是org.redkale.mq.MessageAgent的子类
threads线程数为0表示使用workExecutor。默认: CPU核数, 核数=1的情况下默认值为2JDK 21以上版本默认使用虚拟线程池
coder: MessageRecord的解析器类必须是org.redkale.mq.MessageCoder<MessageRecord>的实现类,
可对数据包进行加密解密默认值org.redkale.mq.MessageRecordCoder
MQ节点下的子节点配置没有固定格式, 根据MessageAgent实现方的定义来配置
-->
<mq name="" type="org.redkalex.mq.kafka.KafkaMessageAgent">
<mq name="" type="org.redkalex.mq.kafka.KafkaMessageAgent" threads="4">
<servers value="127.0.0.1:9101"/>
<!--
加载所有的MessageConsumer实例;

View File

@@ -1656,7 +1656,7 @@ public final class Application {
long s = System.currentTimeMillis();
final StringBuffer sb = new StringBuffer();
Set<String> names = new HashSet<>();
ResourceFactory resourceFactory = ResourceFactory.create();
ResourceFactory resourceFactory = this.resourceFactory.createChild();
List<ResourceFactory> factorys = new ArrayList<>();
for (NodeServer ns : this.servers) {
factorys.add(ns.getResourceFactory());
@@ -2658,13 +2658,14 @@ public final class Application {
this.clientAsyncGroup.dispose();
logger.info("AsyncGroup destroy in " + (System.currentTimeMillis() - s) + " ms");
}
if (this.workExecutor != null) {
this.workExecutor.shutdownNow();
}
long intms = System.currentTimeMillis() - f;
String ms = String.valueOf(intms);
int repeat = ms.length() > 7 ? 0 : (7 - ms.length()) / 2;
logger.info(colorMessage(logger, 36, 1, "-".repeat(repeat) + "------------------------ Redkale shutdown in " + ms + " ms " + (ms.length() / 2 == 0 ? " " : "") + "-".repeat(repeat) + "------------------------") + "\r\n" + "\r\n");
LoggingBaseHandler.traceFlag = true;
}
public ExecutorService getWorkExecutor() {

View File

@@ -650,27 +650,10 @@ public abstract class NodeServer {
if (Modifier.isAbstract(serviceImplClass.getModifiers()) || Modifier.isInterface(serviceImplClass.getModifiers())) {
return false;
}
if (MessageConsumer.class.isAssignableFrom(serviceImplClass)) {
ResourceConsumer mqConsumer = serviceImplClass.getAnnotation(ResourceConsumer.class);
if (mqConsumer == null) {
return false;
}
MessageAgent mqAgent = application.getMessageAgent(mqConsumer.mq());
if (mqAgent == null) {
logger.info("not found MessageAgent(mq = " + mqConsumer.mq() + ")");
return false;
}
}
return true;
}
protected boolean interceptComponent(Service service) {
if (service instanceof MessageConsumer) {
ResourceConsumer mqConsumer = service.getClass().getAnnotation(ResourceConsumer.class);
MessageAgent mqAgent = application.getMessageAgent(mqConsumer.mq());
mqAgent.addMessageConsumer(mqConsumer, (MessageConsumer) service);
return true;
}
return false;
}

View File

@@ -5,11 +5,14 @@
*/
package org.redkale.mq;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.IntFunction;
import java.util.logging.*;
import java.util.stream.Collectors;
import org.redkale.annotation.*;
@@ -22,6 +25,7 @@ import org.redkale.convert.Convert;
import org.redkale.convert.ConvertFactory;
import org.redkale.convert.ConvertType;
import org.redkale.net.Servlet;
import org.redkale.net.WorkThread;
import org.redkale.net.http.*;
import org.redkale.net.sncp.*;
import org.redkale.service.*;
@@ -54,44 +58,52 @@ public abstract class MessageAgent implements Resourcable {
protected AnyValue config;
private ExecutorService workExecutor;
protected final ReentrantLock messageProducerLock = new ReentrantLock();
protected MessageProducer baseMessageProducer;
protected MessageProducer messageBaseProducer;
protected Map<ConvertType, ConvertMessageProducer> messageProducers = new ConcurrentHashMap<>();
protected Map<ConvertType, ConvertMessageProducer> messageProducerMap = new ConcurrentHashMap<>();
protected final CopyOnWriteArrayList<MessageConsumer> messageConsumerList = new CopyOnWriteArrayList<>();
//key: group, sub-key: topic
protected final ConcurrentHashMap<String, ConcurrentHashMap<String, MessageConsumer>> consumerMap = new ConcurrentHashMap<>();
protected final Map<String, Map<String, MessageConsumerWrapper>> messageConsumerMap = new HashMap<>();
protected final CopyOnWriteArrayList<MessageConsumer> consumerList = new CopyOnWriteArrayList<>();
protected MessageClientProducer httpClientProducer;
protected MessageClientProducer httpProducer;
protected MessageClientProducer sncpProducer;
protected MessageClientProducer sncpClientProducer;
protected HttpMessageClient httpMessageClient;
protected SncpMessageClient sncpMessageClient;
protected final ReentrantLock consumerLock = new ReentrantLock();
protected final ReentrantLock clientConsumerLock = new ReentrantLock();
protected final ReentrantLock producerLock = new ReentrantLock();
protected final ReentrantLock clientProducerLock = new ReentrantLock();
protected final ReentrantLock serviceLock = new ReentrantLock();
protected final List<MessageConsumer> consumerListeners = new CopyOnWriteArrayList<>();
protected MessageCoder<MessageRecord> clientMessageCoder = MessageRecordCoder.getInstance();
//本地Service消息接收处理器 key:consumerid
protected HashMap<String, MessageClientConsumerNode> clientConsumerNodes = new LinkedHashMap<>();
protected final AtomicLong msgSeqno = new AtomicLong(System.nanoTime());
protected ScheduledThreadPoolExecutor timeoutExecutor;
protected MessageCoder<MessageRecord> messageCoder = MessageRecordCoder.getInstance();
//本地Service消息接收处理器 key:consumerid
protected HashMap<String, MessageClientConsumerNode> clientConsumerNodes = new LinkedHashMap<>();
public void init(AnyValue config) {
this.name = checkName(config.getValue("name", ""));
int threads = config.getIntValue("threads", -1);
if (threads == 0) {
this.workExecutor = application.getWorkExecutor();
}
if (this.workExecutor == null) {
this.workExecutor = threads > 0 ? WorkThread.createExecutor(threads, "Redkale-MessageConsumerThread-[" + name + "]-%s")
: WorkThread.createWorkExecutor(Utility.cpus(), "Redkale-MessageConsumerThread-[" + name + "]-%s");
}
this.httpMessageClient = new HttpMessageClient(this);
this.sncpMessageClient = new SncpMessageClient(this);
String coderType = config.getValue("coder", "");
@@ -106,7 +118,7 @@ public abstract class MessageAgent implements Resourcable {
if (coder instanceof Service) {
((Service) coder).init(config);
}
this.messageCoder = coder;
this.clientMessageCoder = coder;
} catch (RuntimeException ex) {
throw ex;
} catch (Exception e) {
@@ -122,13 +134,17 @@ public abstract class MessageAgent implements Resourcable {
this.timeoutExecutor.setRemoveOnCancelPolicy(true);
}
public Future submit(Runnable event) {
return workExecutor.submit(event);
}
public Map<String, Long> start(List<MessageConsumer> consumers) {
this.consumerListeners.addAll(consumers);
initMessageConsumer(consumers);
startMessageConsumer();
final LinkedHashMap<String, Long> map = new LinkedHashMap<>();
final List<CompletableFuture> futures = new ArrayList<>();
this.clientConsumerNodes.values().forEach(node -> {
long s = System.currentTimeMillis();
node.consumer.startup();
node.consumer.start();
long e = System.currentTimeMillis() - s;
map.put(node.consumer.consumerid, e);
});
@@ -137,30 +153,37 @@ public abstract class MessageAgent implements Resourcable {
//Application.shutdown 在执行server.shutdown之前执行
public void stop() {
this.stopMessageConsumer();
this.stopMessageProducer();
this.clientConsumerNodes.values().forEach(node -> {
node.consumer.shutdown();
node.consumer.stop();
});
}
//Application.shutdown 在所有server.shutdown执行后执行
public void destroy(AnyValue config) {
this.httpMessageClient.close();
this.sncpMessageClient.close();
for (MessageConsumer consumer : consumerListeners) {
for (MessageConsumer consumer : messageConsumerList) {
consumer.destroy(config);
}
this.consumerListeners.clear();
this.messageConsumerList.clear();
this.messageConsumerMap.clear();
this.httpMessageClient.close();
this.sncpMessageClient.close();
if (this.sncpClientProducer != null) {
this.sncpClientProducer.shutdown();
}
if (this.httpClientProducer != null) {
this.httpClientProducer.shutdown();
}
if (this.clientMessageCoder instanceof Service) {
((Service) this.clientMessageCoder).destroy(config);
}
if (this.timeoutExecutor != null) {
this.timeoutExecutor.shutdown();
this.timeoutExecutor.shutdownNow();
}
if (this.sncpProducer != null) {
this.sncpProducer.shutdown().join();
}
if (this.httpProducer != null) {
this.httpProducer.shutdown().join();
}
if (this.messageCoder instanceof Service) {
((Service) this.messageCoder).destroy(config);
if (this.workExecutor != null && this.workExecutor != application.getWorkExecutor()) {
this.workExecutor.shutdownNow();
}
}
@@ -169,25 +192,55 @@ public abstract class MessageAgent implements Resourcable {
}
public MessageProducer loadMessageProducer(ResourceProducer ann) {
MessageProducer baseProducer = this.baseMessageProducer;
if (this.baseMessageProducer == null) {
MessageProducer baseProducer = this.messageBaseProducer;
if (this.messageBaseProducer == null) {
messageProducerLock.lock();
try {
if (this.baseMessageProducer == null) {
this.baseMessageProducer = createMessageProducer();
if (this.messageBaseProducer == null) {
startMessageProducer();
}
} finally {
messageProducerLock.unlock();
}
baseProducer = this.baseMessageProducer;
baseProducer = this.messageBaseProducer;
}
MessageProducer producer = baseProducer;
Objects.requireNonNull(producer);
return messageProducers.computeIfAbsent(ann.convertType(), t -> new ConvertMessageProducer(producer, ConvertFactory.findConvert(t)));
return messageProducerMap.computeIfAbsent(ann.convertType(), t -> new ConvertMessageProducer(producer, ConvertFactory.findConvert(t)));
}
public void loadMessageConsumer(MessageConsumer consumer) {
protected void initMessageConsumer(List<MessageConsumer> consumers) {
clientConsumerLock.lock();
try {
Map<String, Map<String, MessageConsumerWrapper>> maps = new HashMap<>();
for (MessageConsumer consumer : consumers) {
ResourceConsumer res = consumer.getClass().getAnnotation(ResourceConsumer.class);
String group = application.getPropertyValue(res.group());
group = consumer.getClass().getName() + (group.isEmpty() ? "" : ("-" + group));
Map<String, MessageConsumerWrapper> map = maps.computeIfAbsent(group, g -> new HashMap<>());
for (String t : res.topics()) {
String topic = application.getPropertyValue(t);
if (!topic.trim().isEmpty()) {
if (map.containsKey(topic.trim())) {
throw new RedkaleException(MessageConsumer.class.getSimpleName()
+ " consume topic (" + topic + ") repeat with " + map.get(topic).getClass().getName() + " and " + consumer.getClass().getName());
}
for (MessageConsumerWrapper wrapper : map.values()) {
if (!Objects.equals(res.convertType(), wrapper.convertType)) {
throw new RedkaleException(MessageConsumer.class.getSimpleName()
+ " " + consumer.getClass().getName() + " convertType(" + res.convertType() + ") differ in "
+ wrapper.consumer.getClass().getName() + " convertType(" + wrapper.convertType + ")");
}
}
map.put(topic.trim(), new MessageConsumerWrapper(this, consumer, res.convertType()));
}
}
}
messageConsumerList.addAll(consumers);
messageConsumerMap.putAll(maps);
} finally {
clientConsumerLock.unlock();
}
}
@Override
@@ -211,6 +264,10 @@ public abstract class MessageAgent implements Resourcable {
this.config = config;
}
public ExecutorService getWorkExecutor() {
return workExecutor;
}
public HttpMessageClient getHttpMessageClient() {
return httpMessageClient;
}
@@ -234,7 +291,6 @@ public abstract class MessageAgent implements Resourcable {
return name;
}
@Deprecated
protected List<MessageClientConsumer> getMessageClientConsumers() {
List<MessageClientConsumer> consumers = new ArrayList<>();
MessageClientConsumer one = this.httpMessageClient == null ? null : this.httpMessageClient.respConsumer;
@@ -249,14 +305,13 @@ public abstract class MessageAgent implements Resourcable {
return consumers;
}
@Deprecated
protected List<MessageClientProducer> getMessageClientProducers() {
List<MessageClientProducer> producers = new ArrayList<>();
if (this.httpProducer != null) {
producers.add(this.httpProducer);
if (this.httpClientProducer != null) {
producers.add(this.httpClientProducer);
}
if (this.sncpProducer != null) {
producers.add(this.sncpProducer);
if (this.sncpClientProducer != null) {
producers.add(this.sncpClientProducer);
}
MessageClientProducer one = this.httpMessageClient == null ? null : this.httpMessageClient.getProducer();
if (one != null) {
@@ -269,56 +324,57 @@ public abstract class MessageAgent implements Resourcable {
return producers;
}
@Deprecated
public MessageCoder<MessageRecord> getMessageCoder() {
return this.messageCoder;
public MessageCoder<MessageRecord> getClientMessageCoder() {
return this.clientMessageCoder;
}
@Deprecated
//获取指定topic的生产处理器
public MessageClientProducer getSncpMessageClientProducer() {
if (this.sncpProducer == null) {
producerLock.lock();
if (this.sncpClientProducer == null) {
clientProducerLock.lock();
try {
if (this.sncpProducer == null) {
if (this.sncpClientProducer == null) {
long s = System.currentTimeMillis();
this.sncpProducer = createMessageClientProducer("SncpProducer");
this.sncpClientProducer = createMessageClientProducer("SncpProducer");
long e = System.currentTimeMillis() - s;
if (logger.isLoggable(Level.FINEST)) {
logger.log(Level.FINEST, "MessageAgent.SncpProducer startup all in " + e + "ms");
}
}
} finally {
producerLock.unlock();
clientProducerLock.unlock();
}
}
return this.sncpProducer;
return this.sncpClientProducer;
}
@Deprecated
public MessageClientProducer getHttpMessageClientProducer() {
if (this.httpProducer == null) {
producerLock.lock();
if (this.httpClientProducer == null) {
clientProducerLock.lock();
try {
if (this.httpProducer == null) {
if (this.httpClientProducer == null) {
long s = System.currentTimeMillis();
this.httpProducer = createMessageClientProducer("HttpProducer");
this.httpClientProducer = createMessageClientProducer("HttpProducer");
long e = System.currentTimeMillis() - s;
if (logger.isLoggable(Level.FINEST)) {
logger.log(Level.FINEST, "MessageAgent.HttpProducer startup all in " + e + "ms");
}
}
} finally {
producerLock.unlock();
clientProducerLock.unlock();
}
}
return this.httpProducer;
return this.httpClientProducer;
}
//
protected abstract MessageProducer createMessageProducer();
protected abstract void startMessageConsumer();
protected abstract void closeMessageProducer(MessageProducer messageProducer) throws Exception;
protected abstract void stopMessageConsumer();
protected abstract void startMessageProducer();
protected abstract void stopMessageProducer();
@ResourceListener
public abstract void onResourceChange(ResourceEvent[] events);
@@ -335,33 +391,12 @@ public abstract class MessageAgent implements Resourcable {
//ServiceLoader时判断配置是否符合当前实现类
public abstract boolean acceptsConf(AnyValue config);
@Deprecated
//创建指定topic的生产处理器
protected abstract MessageClientProducer createMessageClientProducer(String producerName);
//创建指定topic的消费处理器
public abstract MessageClientConsumer createMessageClientConsumer(String[] topics, String group, MessageClientProcessor processor);
public void addMessageConsumer(ResourceConsumer res, MessageConsumer consumer) {
consumerLock.lock();
try {
ConcurrentHashMap<String, MessageConsumer> map = consumerMap.computeIfAbsent(res.group(), g -> new ConcurrentHashMap<>());
for (String topic : res.topics()) {
if (!topic.trim().isEmpty()) {
if (map.containsKey(topic.trim())) {
throw new RedkaleException(MessageConsumer.class.getSimpleName()
+ " consume topic (" + topic + ") repeat with "
+ map.get(topic).getClass().getName() + " and " + consumer.getClass().getName());
}
map.put(topic.trim(), consumer);
}
}
consumerList.add(consumer);
} finally {
consumerLock.unlock();
}
}
public final void putService(NodeHttpServer ns, Service service, HttpServlet servlet) {
AutoLoad al = service.getClass().getAnnotation(AutoLoad.class);
if (al != null && !al.value() && service.getClass().getAnnotation(Local.class) != null) {
@@ -471,21 +506,68 @@ public abstract class MessageAgent implements Resourcable {
}
protected static class MessageConsumerWrapper {
public static class MessageConsumerWrapper<T> {
private final MessageAgent messageAgent;
private final MessageConsumer consumer;
public MessageConsumerWrapper(MessageConsumer consumer) {
private final ConvertType convertType;
private final Convert convert;
private final Type messageType;
private final IntFunction<T[]> arrayCreator;
public MessageConsumerWrapper(MessageAgent messageAgent, MessageConsumer<T> consumer, ConvertType convertType) {
Objects.requireNonNull(messageAgent);
Objects.requireNonNull(consumer);
Objects.requireNonNull(convertType);
this.messageAgent = messageAgent;
this.convertType = convertType;
this.consumer = consumer;
this.convert = ConvertFactory.findConvert(convertType);
this.messageType = parseMessageType(consumer.getClass());
this.arrayCreator = Creator.funcArray(TypeToken.typeToClass(messageType));
}
private static Type parseMessageType(Class<? extends MessageConsumer> clazz) {
if (Objects.equals(Object.class, clazz)) {
throw new RedkaleException(clazz.getName() + " not implements " + MessageConsumer.class.getName());
}
Type messageType = null;
Class[] clzs = clazz.getInterfaces();
for (int i = 0; i < clzs.length; i++) {
if (MessageConsumer.class.isAssignableFrom(clzs[i])) {
messageType = clazz.getGenericInterfaces()[i];
break;
}
}
if (messageType == null) {
return parseMessageType((Class) clazz.getSuperclass());
}
return TypeToken.getGenericType(((ParameterizedType) messageType).getActualTypeArguments()[0], clazz);
}
public void init(AnyValue config) {
consumer.init(config);
}
public void onMessage(MessageConext context, Object[] messages) {
consumer.onMessage(context, messages);
public Future onMessage(MessageConext context, List<byte[]> messages) {
return messageAgent.submit(() -> {
try {
T[] msgs = this.arrayCreator.apply(messages.size());
int index = -1;
for (byte[] bs : messages) {
msgs[++index] = (T) convert.convertFrom(messageType, bs);
}
consumer.onMessage(context, msgs);
} catch (Throwable t) {
messageAgent.getLogger().log(Level.SEVERE, MessageConsumer.class.getSimpleName() + " execute error, topic: " + context.getTopic()
+ ", messages: " + messages.stream().map(v -> new String(v, StandardCharsets.UTF_8)).collect(Collectors.toList()));
}
});
}
public void destroy(AnyValue config) {

View File

@@ -51,7 +51,7 @@ public abstract class MessageClient {
protected void close() {
if (this.respConsumer != null) {
this.respConsumer.shutdown();
this.respConsumer.stop();
}
}
@@ -100,7 +100,7 @@ public abstract class MessageClient {
};
long ones = System.currentTimeMillis();
MessageClientConsumer one = messageAgent.createMessageClientConsumer(new String[]{appRespTopic}, appRespConsumerid, processor);
one.startup();
one.start();
long onee = System.currentTimeMillis() - ones;
if (finest) {
messageAgent.logger.log(Level.FINEST, clazzName + ".MessageRespFutureNode.startup " + onee + "ms ");

View File

@@ -52,9 +52,9 @@ public abstract class MessageClientConsumer {
return topics;
}
public abstract void startup();
public abstract void start();
public abstract void shutdown();
public abstract void stop();
public boolean isClosed() {
return closed;

View File

@@ -33,9 +33,9 @@ public abstract class MessageClientProducer {
public abstract CompletableFuture<Void> apply(MessageRecord message);
public abstract CompletableFuture<Void> startup();
public abstract void startup();
public abstract CompletableFuture<Void> shutdown();
public abstract void shutdown();
public boolean isClosed() {
return closed.get();

View File

@@ -58,9 +58,4 @@ public @interface Cacheable {
*/
boolean direct() default false;
@Deprecated
boolean sequent() default false;
@Deprecated
boolean continuousid() default false;
}