业务原因,需要在一个项目中与多方MQ进行业务通信;

步骤一,复制一份RocketMQProperties配置文件,避免与原来的冲突

package com.heit.road.web.config;

import org.apache.rocketmq.common.topic.TopicValidator;

import java.util.HashMap;
import java.util.Map; public class MultipleRocketMQProperties { /**
* The name server for rocketMQ, formats: `host:port;host:port`.
*/
private String nameServer; /**
* Enum type for accessChannel, values: LOCAL, CLOUD
*/
private String accessChannel; private org.apache.rocketmq.spring.autoconfigure.RocketMQProperties.Producer producer; /**
* Configure enable listener or not.
* In some particular cases, if you don't want the the listener is enabled when container startup,
* the configuration pattern is like this :
* rocketmq.consumer.listeners.<group-name>.<topic-name>.enabled=<boolean value, true or false>
* <p>
* the listener is enabled by default.
*/
private org.apache.rocketmq.spring.autoconfigure.RocketMQProperties.Consumer consumer = new org.apache.rocketmq.spring.autoconfigure.RocketMQProperties.Consumer(); public String getNameServer() {
return nameServer;
} public void setNameServer(String nameServer) {
this.nameServer = nameServer;
} public String getAccessChannel() {
return accessChannel;
} public void setAccessChannel(String accessChannel) {
this.accessChannel = accessChannel;
} public org.apache.rocketmq.spring.autoconfigure.RocketMQProperties.Producer getProducer() {
return producer;
} public void setProducer(org.apache.rocketmq.spring.autoconfigure.RocketMQProperties.Producer producer) {
this.producer = producer;
} public static class Producer { /**
* Group name of producer.
*/
private String group; /**
* Millis of send message timeout.
*/
private int sendMessageTimeout = 3000; /**
* Compress message body threshold, namely, message body larger than 4k will be compressed on default.
*/
private int compressMessageBodyThreshold = 1024 * 4; /**
* Maximum number of retry to perform internally before claiming sending failure in synchronous mode.
* This may potentially cause message duplication which is up to application developers to resolve.
*/
private int retryTimesWhenSendFailed = 2; /**
* <p> Maximum number of retry to perform internally before claiming sending failure in asynchronous mode. </p>
* This may potentially cause message duplication which is up to application developers to resolve.
*/
private int retryTimesWhenSendAsyncFailed = 2; /**
* Indicate whether to retry another broker on sending failure internally.
*/
private boolean retryNextServer = false; /**
* Maximum allowed message size in bytes.
*/
private int maxMessageSize = 1024 * 1024 * 4; /**
* The property of "access-key".
*/
private String accessKey; /**
* The property of "secret-key".
*/
private String secretKey; /**
* Switch flag instance for message trace.
*/
private boolean enableMsgTrace = true; /**
* The name value of message trace topic.If you don't config,you can use the default trace topic name.
*/
private String customizedTraceTopic = TopicValidator.RMQ_SYS_TRACE_TOPIC; public String getGroup() {
return group;
} public void setGroup(String group) {
this.group = group;
} public int getSendMessageTimeout() {
return sendMessageTimeout;
} public void setSendMessageTimeout(int sendMessageTimeout) {
this.sendMessageTimeout = sendMessageTimeout;
} public int getCompressMessageBodyThreshold() {
return compressMessageBodyThreshold;
} public void setCompressMessageBodyThreshold(int compressMessageBodyThreshold) {
this.compressMessageBodyThreshold = compressMessageBodyThreshold;
} public int getRetryTimesWhenSendFailed() {
return retryTimesWhenSendFailed;
} public void setRetryTimesWhenSendFailed(int retryTimesWhenSendFailed) {
this.retryTimesWhenSendFailed = retryTimesWhenSendFailed;
} public int getRetryTimesWhenSendAsyncFailed() {
return retryTimesWhenSendAsyncFailed;
} public void setRetryTimesWhenSendAsyncFailed(int retryTimesWhenSendAsyncFailed) {
this.retryTimesWhenSendAsyncFailed = retryTimesWhenSendAsyncFailed;
} public boolean isRetryNextServer() {
return retryNextServer;
} public void setRetryNextServer(boolean retryNextServer) {
this.retryNextServer = retryNextServer;
} public int getMaxMessageSize() {
return maxMessageSize;
} public void setMaxMessageSize(int maxMessageSize) {
this.maxMessageSize = maxMessageSize;
} public String getAccessKey() {
return accessKey;
} public void setAccessKey(String accessKey) {
this.accessKey = accessKey;
} public String getSecretKey() {
return secretKey;
} public void setSecretKey(String secretKey) {
this.secretKey = secretKey;
} public boolean isEnableMsgTrace() {
return enableMsgTrace;
} public void setEnableMsgTrace(boolean enableMsgTrace) {
this.enableMsgTrace = enableMsgTrace;
} public String getCustomizedTraceTopic() {
return customizedTraceTopic;
} public void setCustomizedTraceTopic(String customizedTraceTopic) {
this.customizedTraceTopic = customizedTraceTopic;
}
} public org.apache.rocketmq.spring.autoconfigure.RocketMQProperties.Consumer getConsumer() {
return consumer;
} public void setConsumer(org.apache.rocketmq.spring.autoconfigure.RocketMQProperties.Consumer consumer) {
this.consumer = consumer;
} public static final class Consumer {
/**
* listener configuration container
* the pattern is like this:
* group1.topic1 = false
* group2.topic2 = true
* group3.topic3 = false
*/
private Map<String, Map<String, Boolean>> listeners = new HashMap<>(); public Map<String, Map<String, Boolean>> getListeners() {
return listeners;
} public void setListeners(Map<String, Map<String, Boolean>> listeners) {
this.listeners = listeners;
}
} }

步骤二,复制一份@RocketMQMessageListener,并新增数据源参数soruce

,这里不采用原来的nameServer参数,可能是版本原因,这个参数目前并不支持多数据源

/*
* 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 com.heit.road.web.config; import org.apache.rocketmq.spring.annotation.ConsumeMode;
import org.apache.rocketmq.spring.annotation.MessageModel;
import org.apache.rocketmq.spring.annotation.SelectorType; import java.lang.annotation.*; @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MultipleRocketMQMessageListener { String NAME_SERVER_PLACEHOLDER = "";
String ACCESS_KEY_PLACEHOLDER = "";
String SECRET_KEY_PLACEHOLDER = "";
String TRACE_TOPIC_PLACEHOLDER = "";
String ACCESS_CHANNEL_PLACEHOLDER = ""; /**
* 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>
* <p>
* See <a href="http://rocketmq.apache.org/docs/core-concept/">here</a> for further discussion.
*/
String soruce(); String consumerGroup(); /**
* Topic name.
*/
String topic(); /**
* Control how to selector message.
*
* @see SelectorType
*/
SelectorType selectorType() default SelectorType.TAG; /**
* Control which message can be select. Grammar please see {@link SelectorType#TAG} and {@link SelectorType#SQL92}
*/
String selectorExpression() default "*"; /**
* Control consume mode, you can choice receive message concurrently or orderly.
*/
ConsumeMode consumeMode() default ConsumeMode.CONCURRENTLY; /**
* Control message mode, if you want all subscribers receive message all message, broadcasting is a good choice.
*/
MessageModel messageModel() default MessageModel.CLUSTERING; /**
* Max consumer thread number.
*/
int consumeThreadMax() default 64; /**
* Maximum amount of time in minutes a message may block the consuming thread.
*/
long consumeTimeout() default 15L; /**
* The property of "access-key".
*/
String accessKey() default ACCESS_KEY_PLACEHOLDER; /**
* The property of "secret-key".
*/
String secretKey() default SECRET_KEY_PLACEHOLDER; /**
* Switch flag instance for message trace.
*/
boolean enableMsgTrace() default true; /**
* The name value of message trace topic.If you don't config,you can use the default trace topic name.
*/
String customizedTraceTopic() default TRACE_TOPIC_PLACEHOLDER; /**
* The property of "name-server".
*/
String nameServer() default NAME_SERVER_PLACEHOLDER; /**
* The property of "access-channel".
*/
String accessChannel() default ACCESS_CHANNEL_PLACEHOLDER;
}

步骤三,复制一份RocketMQListenerContainer,替换参数RocketMQMessageListener

/*
* 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 com.heit.road.web.config; import org.apache.rocketmq.client.AccessChannel;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.MessageSelector;
import org.apache.rocketmq.client.consumer.listener.*;
import org.apache.rocketmq.client.consumer.rebalance.AllocateMessageQueueAveragely;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.utils.MessageUtil;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.remoting.RPCHook;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.apache.rocketmq.spring.annotation.ConsumeMode;
import org.apache.rocketmq.spring.annotation.MessageModel;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.annotation.SelectorType;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.apache.rocketmq.spring.core.RocketMQPushConsumerLifecycleListener;
import org.apache.rocketmq.spring.core.RocketMQReplyListener;
import org.apache.rocketmq.spring.support.RocketMQListenerContainer;
import org.apache.rocketmq.spring.support.RocketMQUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.SmartLifecycle;
import org.springframework.core.MethodParameter;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.converter.MessageConversionException;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.converter.SmartMessageConverter;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.util.Assert;
import org.springframework.util.MimeTypeUtils; import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Objects; @SuppressWarnings("WeakerAccess")
public class MultipleRocketMQListenerContainer implements InitializingBean,
RocketMQListenerContainer, SmartLifecycle, ApplicationContextAware {
private final static Logger log = LoggerFactory.getLogger(MultipleRocketMQListenerContainer.class); private ApplicationContext applicationContext; /**
* The name of the DefaultRocketMQListenerContainer instance
*/
private String name; private long suspendCurrentQueueTimeMillis = 1000; /**
* Message consume retry strategy<br> -1,no retry,put into DLQ directly<br> 0,broker control retry frequency<br>
* >0,client control retry frequency.
*/
private int delayLevelWhenNextConsume = 0; private String nameServer; private AccessChannel accessChannel = AccessChannel.LOCAL; private String consumerGroup; private String topic; private int consumeThreadMax = 64; private String charset = "UTF-8"; private MessageConverter messageConverter; private RocketMQListener rocketMQListener; private RocketMQReplyListener rocketMQReplyListener; private MultipleRocketMQMessageListener rocketMQMessageListener; private DefaultMQPushConsumer consumer; private Type messageType; private MethodParameter methodParameter; private boolean running; // The following properties came from @RocketMQMessageListener.
private ConsumeMode consumeMode;
private SelectorType selectorType;
private String selectorExpression;
private MessageModel messageModel;
private long consumeTimeout; public long getSuspendCurrentQueueTimeMillis() {
return suspendCurrentQueueTimeMillis;
} public void setSuspendCurrentQueueTimeMillis(long suspendCurrentQueueTimeMillis) {
this.suspendCurrentQueueTimeMillis = suspendCurrentQueueTimeMillis;
} public int getDelayLevelWhenNextConsume() {
return delayLevelWhenNextConsume;
} public void setDelayLevelWhenNextConsume(int delayLevelWhenNextConsume) {
this.delayLevelWhenNextConsume = delayLevelWhenNextConsume;
} public String getNameServer() {
return nameServer;
} public void setNameServer(String nameServer) {
this.nameServer = nameServer;
} public AccessChannel getAccessChannel() {
return accessChannel;
} public void setAccessChannel(AccessChannel accessChannel) {
this.accessChannel = accessChannel;
} public String getConsumerGroup() {
return consumerGroup;
} public void setConsumerGroup(String consumerGroup) {
this.consumerGroup = consumerGroup;
} public String getTopic() {
return topic;
} public void setTopic(String topic) {
this.topic = topic;
} public int getConsumeThreadMax() {
return consumeThreadMax;
} public String getCharset() {
return charset;
} public void setCharset(String charset) {
this.charset = charset;
} public MessageConverter getMessageConverter() {
return messageConverter;
} public MultipleRocketMQListenerContainer setMessageConverter(MessageConverter messageConverter) {
this.messageConverter = messageConverter;
return this;
} public RocketMQListener getRocketMQListener() {
return rocketMQListener;
} public void setRocketMQListener(RocketMQListener rocketMQListener) {
this.rocketMQListener = rocketMQListener;
} public RocketMQReplyListener getRocketMQReplyListener() {
return rocketMQReplyListener;
} public void setRocketMQReplyListener(RocketMQReplyListener rocketMQReplyListener) {
this.rocketMQReplyListener = rocketMQReplyListener;
} public MultipleRocketMQMessageListener getRocketMQMessageListener() {
return rocketMQMessageListener;
} public void setRocketMQMessageListener(MultipleRocketMQMessageListener anno) {
this.rocketMQMessageListener = anno; this.consumeMode = anno.consumeMode();
this.consumeThreadMax = anno.consumeThreadMax();
this.messageModel = anno.messageModel();
this.selectorType = anno.selectorType();
this.selectorExpression = anno.selectorExpression();
this.consumeTimeout = anno.consumeTimeout();
} public ConsumeMode getConsumeMode() {
return consumeMode;
} public SelectorType getSelectorType() {
return selectorType;
} public void setSelectorExpression(String selectorExpression) {
this.selectorExpression = selectorExpression;
} public String getSelectorExpression() {
return selectorExpression;
} public MessageModel getMessageModel() {
return messageModel;
} public DefaultMQPushConsumer getConsumer() {
return consumer;
} public void setConsumer(DefaultMQPushConsumer consumer) {
this.consumer = consumer;
} @Override
public void destroy() {
this.setRunning(false);
if (Objects.nonNull(consumer)) {
consumer.shutdown();
}
log.info("container destroyed, {}", this.toString());
} @Override
public boolean isAutoStartup() {
return true;
} @Override
public void stop(Runnable callback) {
stop();
callback.run();
} @Override
public void start() {
if (this.isRunning()) {
throw new IllegalStateException("container already running. " + this.toString());
} try {
consumer.start();
} catch (MQClientException e) {
throw new IllegalStateException("Failed to start RocketMQ push consumer", e);
}
this.setRunning(true); log.info("running container: {}", this.toString());
} @Override
public void stop() {
if (this.isRunning()) {
if (Objects.nonNull(consumer)) {
consumer.shutdown();
}
setRunning(false);
}
} @Override
public boolean isRunning() {
return running;
} private void setRunning(boolean running) {
this.running = running;
} @Override
public int getPhase() {
// Returning Integer.MAX_VALUE only suggests that
// we will be the first bean to shutdown and last bean to start
return Integer.MAX_VALUE;
} @Override
public void afterPropertiesSet() throws Exception {
initRocketMQPushConsumer(); this.messageType = getMessageType();
this.methodParameter = getMethodParameter();
log.debug("RocketMQ messageType: {}", messageType);
} @Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
} @Override
public String toString() {
return "DefaultRocketMQListenerContainer{" +
"consumerGroup='" + consumerGroup + '\'' +
", nameServer='" + nameServer + '\'' +
", topic='" + topic + '\'' +
", consumeMode=" + consumeMode +
", selectorType=" + selectorType +
", selectorExpression='" + selectorExpression + '\'' +
", messageModel=" + messageModel +
'}';
} public void setName(String name) {
this.name = name;
} public class DefaultMessageListenerConcurrently implements MessageListenerConcurrently { @SuppressWarnings("unchecked")
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
for (MessageExt messageExt : msgs) {
log.debug("received msg: {}", messageExt);
try {
long now = System.currentTimeMillis();
handleMessage(messageExt);
long costTime = System.currentTimeMillis() - now;
log.debug("consume {} cost: {} ms", messageExt.getMsgId(), costTime);
} catch (Exception e) {
log.warn("consume message failed. messageExt:{}, error:{}", messageExt, e);
context.setDelayLevelWhenNextConsume(delayLevelWhenNextConsume);
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
}
} return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
} public class DefaultMessageListenerOrderly implements MessageListenerOrderly { @SuppressWarnings("unchecked")
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
for (MessageExt messageExt : msgs) {
log.debug("received msg: {}", messageExt);
try {
long now = System.currentTimeMillis();
handleMessage(messageExt);
long costTime = System.currentTimeMillis() - now;
log.info("consume {} cost: {} ms", messageExt.getMsgId(), costTime);
} catch (Exception e) {
log.warn("consume message failed. messageExt:{}", messageExt, e);
context.setSuspendCurrentQueueTimeMillis(suspendCurrentQueueTimeMillis);
return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;
}
} return ConsumeOrderlyStatus.SUCCESS;
}
} private void handleMessage(
MessageExt messageExt) throws MQClientException, RemotingException, InterruptedException {
if (rocketMQListener != null) {
rocketMQListener.onMessage(doConvertMessage(messageExt));
} else if (rocketMQReplyListener != null) {
Object replyContent = rocketMQReplyListener.onMessage(doConvertMessage(messageExt));
Message<?> message = MessageBuilder.withPayload(replyContent).build(); org.apache.rocketmq.common.message.Message replyMessage = MessageUtil.createReplyMessage(messageExt, convertToBytes(message));
consumer.getDefaultMQPushConsumerImpl().getmQClientFactory().getDefaultMQProducer().send(replyMessage, new SendCallback() {
@Override public void onSuccess(SendResult sendResult) {
if (sendResult.getSendStatus() != SendStatus.SEND_OK) {
log.error("Consumer replies message failed. SendStatus: {}", sendResult.getSendStatus());
} else {
log.info("Consumer replies message success.");
}
} @Override public void onException(Throwable e) {
log.error("Consumer replies message failed. error: {}", e.getLocalizedMessage());
}
});
}
} private byte[] convertToBytes(Message<?> message) {
Message<?> messageWithSerializedPayload = doConvert(message.getPayload(), message.getHeaders());
Object payloadObj = messageWithSerializedPayload.getPayload();
byte[] payloads;
try {
if (null == payloadObj) {
throw new RuntimeException("the message cannot be empty");
}
if (payloadObj instanceof String) {
payloads = ((String) payloadObj).getBytes(Charset.forName(charset));
} else if (payloadObj instanceof byte[]) {
payloads = (byte[]) messageWithSerializedPayload.getPayload();
} else {
String jsonObj = (String) this.messageConverter.fromMessage(messageWithSerializedPayload, payloadObj.getClass());
if (null == jsonObj) {
throw new RuntimeException(String.format(
"empty after conversion [messageConverter:%s,payloadClass:%s,payloadObj:%s]",
this.messageConverter.getClass(), payloadObj.getClass(), payloadObj));
}
payloads = jsonObj.getBytes(Charset.forName(charset));
}
} catch (Exception e) {
throw new RuntimeException("convert to bytes failed.", e);
}
return payloads;
} private Message<?> doConvert(Object payload, MessageHeaders headers) {
Message<?> message = this.messageConverter instanceof SmartMessageConverter ?
((SmartMessageConverter) this.messageConverter).toMessage(payload, headers, null) :
this.messageConverter.toMessage(payload, headers);
if (message == null) {
String payloadType = payload.getClass().getName();
Object contentType = headers != null ? headers.get(MessageHeaders.CONTENT_TYPE) : null;
throw new MessageConversionException("Unable to convert payload with type='" + payloadType +
"', contentType='" + contentType + "', converter=[" + this.messageConverter + "]");
}
MessageBuilder<?> builder = MessageBuilder.fromMessage(message);
builder.setHeaderIfAbsent(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.TEXT_PLAIN);
return builder.build();
} @SuppressWarnings("unchecked")
private Object doConvertMessage(MessageExt messageExt) {
if (Objects.equals(messageType, MessageExt.class)) {
return messageExt;
} else {
String str = new String(messageExt.getBody(), Charset.forName(charset));
if (Objects.equals(messageType, String.class)) {
return str;
} else {
// If msgType not string, use objectMapper change it.
try {
if (messageType instanceof Class) {
//if the messageType has not Generic Parameter
return this.getMessageConverter().fromMessage(MessageBuilder.withPayload(str).build(), (Class<?>) messageType);
} else {
//if the messageType has Generic Parameter, then use SmartMessageConverter#fromMessage with third parameter "conversionHint".
//we have validate the MessageConverter is SmartMessageConverter in this#getMethodParameter.
return ((SmartMessageConverter) this.getMessageConverter()).fromMessage(MessageBuilder.withPayload(str).build(), (Class<?>) ((ParameterizedType) messageType).getRawType(), methodParameter);
}
} catch (Exception e) {
log.info("convert failed. str:{}, msgType:{}", str, messageType);
throw new RuntimeException("cannot convert message to " + messageType, e);
}
}
}
} private MethodParameter getMethodParameter() {
Class<?> targetClass;
if (rocketMQListener != null) {
targetClass = AopProxyUtils.ultimateTargetClass(rocketMQListener);
} else {
targetClass = AopProxyUtils.ultimateTargetClass(rocketMQReplyListener);
}
Type messageType = this.getMessageType();
Class clazz = null;
if (messageType instanceof ParameterizedType && messageConverter instanceof SmartMessageConverter) {
clazz = (Class) ((ParameterizedType) messageType).getRawType();
} else if (messageType instanceof Class) {
clazz = (Class) messageType;
} else {
throw new RuntimeException("parameterType:" + messageType + " of onMessage method is not supported");
}
try {
final Method method = targetClass.getMethod("onMessage", clazz);
return new MethodParameter(method, 0);
} catch (NoSuchMethodException e) {
e.printStackTrace();
throw new RuntimeException("parameterType:" + messageType + " of onMessage method is not supported");
}
} private Type getMessageType() {
Class<?> targetClass;
if (rocketMQListener != null) {
targetClass = AopProxyUtils.ultimateTargetClass(rocketMQListener);
} else {
targetClass = AopProxyUtils.ultimateTargetClass(rocketMQReplyListener);
}
Type matchedGenericInterface = null;
while (Objects.nonNull(targetClass)) {
Type[] interfaces = targetClass.getGenericInterfaces();
if (Objects.nonNull(interfaces)) {
for (Type type : interfaces) {
if (type instanceof ParameterizedType &&
(Objects.equals(((ParameterizedType) type).getRawType(), RocketMQListener.class) || Objects.equals(((ParameterizedType) type).getRawType(), RocketMQReplyListener.class))) {
matchedGenericInterface = type;
break;
}
}
}
targetClass = targetClass.getSuperclass();
}
if (Objects.isNull(matchedGenericInterface)) {
return Object.class;
} Type[] actualTypeArguments = ((ParameterizedType) matchedGenericInterface).getActualTypeArguments();
if (Objects.nonNull(actualTypeArguments) && actualTypeArguments.length > 0) {
return actualTypeArguments[0];
}
return Object.class;
} private void initRocketMQPushConsumer() throws MQClientException {
if (rocketMQListener == null && rocketMQReplyListener == null) {
throw new IllegalArgumentException("Property 'rocketMQListener' or 'rocketMQReplyListener' is required");
}
Assert.notNull(consumerGroup, "Property 'consumerGroup' is required");
Assert.notNull(nameServer, "Property 'nameServer' is required");
Assert.notNull(topic, "Property 'topic' is required"); RPCHook rpcHook = RocketMQUtil.getRPCHookByAkSk(applicationContext.getEnvironment(),
this.rocketMQMessageListener.accessKey(), this.rocketMQMessageListener.secretKey());
boolean enableMsgTrace = rocketMQMessageListener.enableMsgTrace();
if (Objects.nonNull(rpcHook)) {
consumer = new DefaultMQPushConsumer(consumerGroup, rpcHook, new AllocateMessageQueueAveragely(),
enableMsgTrace, this.applicationContext.getEnvironment().
resolveRequiredPlaceholders(this.rocketMQMessageListener.customizedTraceTopic()));
consumer.setVipChannelEnabled(false);
consumer.setInstanceName(RocketMQUtil.getInstanceName(rpcHook, consumerGroup));
} else {
log.debug("Access-key or secret-key not configure in " + this + ".");
consumer = new DefaultMQPushConsumer(consumerGroup, enableMsgTrace,
this.applicationContext.getEnvironment().
resolveRequiredPlaceholders(this.rocketMQMessageListener.customizedTraceTopic()));
} String customizedNameServer = this.applicationContext.getEnvironment().resolveRequiredPlaceholders(this.rocketMQMessageListener.nameServer());
if (customizedNameServer != null) {
consumer.setNamesrvAddr(customizedNameServer);
} else {
consumer.setNamesrvAddr(nameServer);
}
if (accessChannel != null) {
consumer.setAccessChannel(accessChannel);
}
consumer.setConsumeThreadMax(consumeThreadMax);
if (consumeThreadMax < consumer.getConsumeThreadMin()) {
consumer.setConsumeThreadMin(consumeThreadMax);
}
consumer.setConsumeTimeout(consumeTimeout); switch (messageModel) {
case BROADCASTING:
consumer.setMessageModel(org.apache.rocketmq.common.protocol.heartbeat.MessageModel.BROADCASTING);
break;
case CLUSTERING:
consumer.setMessageModel(org.apache.rocketmq.common.protocol.heartbeat.MessageModel.CLUSTERING);
break;
default:
throw new IllegalArgumentException("Property 'messageModel' was wrong.");
} switch (selectorType) {
case TAG:
consumer.subscribe(topic, selectorExpression);
break;
case SQL92:
consumer.subscribe(topic, MessageSelector.bySql(selectorExpression));
break;
default:
throw new IllegalArgumentException("Property 'selectorType' was wrong.");
} switch (consumeMode) {
case ORDERLY:
consumer.setMessageListener(new DefaultMessageListenerOrderly());
break;
case CONCURRENTLY:
consumer.setMessageListener(new DefaultMessageListenerConcurrently());
break;
default:
throw new IllegalArgumentException("Property 'consumeMode' was wrong.");
} if (rocketMQListener instanceof RocketMQPushConsumerLifecycleListener) {
((RocketMQPushConsumerLifecycleListener) rocketMQListener).prepareStart(consumer);
} else if (rocketMQReplyListener instanceof RocketMQPushConsumerLifecycleListener) {
((RocketMQPushConsumerLifecycleListener) rocketMQReplyListener).prepareStart(consumer);
} } }

步骤四,配置文件添加参数,与原rocketmq参数区分开

multiplerocketmq:
xdt:
name-server: xdtIP:xdtPort
producer:
group: xdt_groups
road:
name-server: roadIP:roadPort
producer:
group: road_groups

步骤五,通过配置文件加载多数据源

package com.heit.road.web.config;

import cn.hutool.core.date.DateUtil;
import org.apache.rocketmq.client.AccessChannel;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.spring.annotation.ConsumeMode;
import org.apache.rocketmq.spring.annotation.MessageModel;
import org.apache.rocketmq.spring.autoconfigure.RocketMQProperties;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.apache.rocketmq.spring.core.RocketMQReplyListener;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.apache.rocketmq.spring.support.RocketMQMessageConverter;
import org.apache.rocketmq.spring.support.RocketMQUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.scope.ScopedProxyUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.support.BeanDefinitionValidationException;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils; import java.util.Collections;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors; @Configuration
public class MultipleRocketMQConfig implements ApplicationContextAware, SmartInitializingSingleton {
private final static Logger log = LoggerFactory.getLogger(MultipleRocketMQConfig.class);
private ConfigurableApplicationContext applicationContext;
private AtomicLong counter = new AtomicLong(0);
private StandardEnvironment environment;
private RocketMQMessageConverter rocketMQMessageConverter; public MultipleRocketMQConfig(RocketMQMessageConverter rocketMQMessageConverter,
StandardEnvironment environment) {
this.rocketMQMessageConverter = rocketMQMessageConverter;
this.environment = environment;
} @Bean("road")
@ConditionalOnProperty(prefix = "multiplerocketmq.road", value = {"name-server"})
@ConfigurationProperties(prefix = "multiplerocketmq.road")
public MultipleRocketMQProperties road() {
return new MultipleRocketMQProperties();
} @Bean(value = "roadmq", destroyMethod = "destroy")
@ConditionalOnMissingBean(name = "roadmq")
@ConditionalOnProperty(prefix = "multiplerocketmq.road", value = {"name-server", "producer.group"})
public RocketMQTemplate roadMQProducer(@Qualifier("road") MultipleRocketMQProperties rocketMQProperties, RocketMQMessageConverter rocketMQMessageConverter) {
return createRocketMQTemplate(rocketMQProperties, rocketMQMessageConverter);
} @Bean("xdt")
@ConditionalOnProperty(prefix = "multiplerocketmq.xdt", value = {"name-server"})
@ConfigurationProperties(prefix = "multiplerocketmq.xdt")
public MultipleRocketMQProperties xdt() {
return new MultipleRocketMQProperties();
} @Bean(value = "xdtmq", destroyMethod = "destroy")
@ConditionalOnMissingBean(name = "xdtmq")
@ConditionalOnProperty(prefix = "multiplerocketmq.xdt", value = {"name-server", "producer.group"})
public RocketMQTemplate xdtMQProducer(@Qualifier("xdt") MultipleRocketMQProperties rocketMQProperties, RocketMQMessageConverter rocketMQMessageConverter) {
return createRocketMQTemplate(rocketMQProperties, rocketMQMessageConverter);
} private RocketMQTemplate createRocketMQTemplate(MultipleRocketMQProperties rocketMQProperties, RocketMQMessageConverter rocketMQMessageConverter) {
RocketMQProperties.Producer producerConfig = rocketMQProperties.getProducer();
String nameServer = rocketMQProperties.getNameServer();
String groupName = producerConfig.getGroup();
Assert.hasText(nameServer, "[rocketmq.name-server] must not be null");
Assert.hasText(groupName, "[rocketmq.producer.group] must not be null"); String accessChannel = rocketMQProperties.getAccessChannel(); String ak = rocketMQProperties.getProducer().getAccessKey();
String sk = rocketMQProperties.getProducer().getSecretKey();
boolean isEnableMsgTrace = rocketMQProperties.getProducer().isEnableMsgTrace();
String customizedTraceTopic = rocketMQProperties.getProducer().getCustomizedTraceTopic();
DefaultMQProducer producer = RocketMQUtil.createDefaultMQProducer(groupName, ak, sk, isEnableMsgTrace, customizedTraceTopic); producer.setNamesrvAddr(nameServer);
if (!StringUtils.isEmpty(accessChannel)) {
producer.setAccessChannel(AccessChannel.valueOf(accessChannel));
}
producer.setSendMsgTimeout(producerConfig.getSendMessageTimeout());
producer.setRetryTimesWhenSendFailed(producerConfig.getRetryTimesWhenSendFailed());
producer.setRetryTimesWhenSendAsyncFailed(producerConfig.getRetryTimesWhenSendAsyncFailed());
producer.setMaxMessageSize(producerConfig.getMaxMessageSize());
producer.setCompressMsgBodyOverHowmuch(producerConfig.getCompressMessageBodyThreshold());
producer.setRetryAnotherBrokerWhenNotStoreOK(producerConfig.isRetryNextServer());
producer.setInstanceName(producer.getProducerGroup() + DateUtil.now());
RocketMQTemplate rocketMQTemplate = new RocketMQTemplate();
rocketMQTemplate.setProducer(producer);
rocketMQTemplate.setMessageConverter(rocketMQMessageConverter.getMessageConverter());
return rocketMQTemplate;
} @Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = (ConfigurableApplicationContext) applicationContext;
} @Override
public void afterSingletonsInstantiated() {
Map<String, Object> beans = this.applicationContext.getBeansWithAnnotation(MultipleRocketMQMessageListener.class)
.entrySet().stream().filter(entry -> !ScopedProxyUtils.isScopedTarget(entry.getKey()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); beans.forEach(this::registerContainer);
} private void registerContainer(String beanName, Object bean) {
Class<?> clazz = AopProxyUtils.ultimateTargetClass(bean); if (RocketMQListener.class.isAssignableFrom(bean.getClass()) && RocketMQReplyListener.class.isAssignableFrom(bean.getClass())) {
throw new IllegalStateException(clazz + " cannot be both instance of " + RocketMQListener.class.getName() + " and " + RocketMQReplyListener.class.getName());
} if (!RocketMQListener.class.isAssignableFrom(bean.getClass()) && !RocketMQReplyListener.class.isAssignableFrom(bean.getClass())) {
throw new IllegalStateException(clazz + " is not instance of " + RocketMQListener.class.getName() + " or " + RocketMQReplyListener.class.getName());
} MultipleRocketMQMessageListener annotation = clazz.getAnnotation(MultipleRocketMQMessageListener.class);
String topic = annotation.topic();
String consumerGroup = annotation.consumerGroup();
String soruce = annotation.soruce();
MultipleRocketMQProperties rocketMQProperties = this.applicationContext.getBean(soruce, MultipleRocketMQProperties.class);
boolean listenerEnabled =
(boolean) rocketMQProperties.getConsumer().getListeners().getOrDefault(consumerGroup, Collections.EMPTY_MAP)
.getOrDefault(topic, true); if (!listenerEnabled) {
log.debug(
"Consumer Listener (group:{},topic:{}) is not enabled by configuration, will ignore initialization.",
consumerGroup, topic);
return;
}
validate(annotation); String containerBeanName = String.format("%s_%s_%s", topic, consumerGroup,
counter.incrementAndGet());
GenericApplicationContext genericApplicationContext = (GenericApplicationContext) applicationContext; genericApplicationContext.registerBean(containerBeanName, MultipleRocketMQListenerContainer.class,
() -> createRocketMQListenerContainer(containerBeanName, bean, annotation));
MultipleRocketMQListenerContainer container = genericApplicationContext.getBean(containerBeanName,
MultipleRocketMQListenerContainer.class);
if (!container.isRunning()) {
try {
container.getConsumer().setInstanceName(containerBeanName);
container.getConsumer().setNamesrvAddr(rocketMQProperties.getNameServer());
container.start();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
} private MultipleRocketMQListenerContainer createRocketMQListenerContainer(String name, Object bean,
MultipleRocketMQMessageListener annotation) {
MultipleRocketMQListenerContainer container = new MultipleRocketMQListenerContainer(); container.setRocketMQMessageListener(annotation);
String soruce = annotation.soruce();
if (StringUtils.isEmpty(soruce)) {
throw new RuntimeException(name + " 未指定数据源");
}
MultipleRocketMQProperties rocketMQProperties = this.applicationContext.getBean(soruce, MultipleRocketMQProperties.class);
container.setAccessChannel(AccessChannel.CLOUD);
container.setTopic(annotation.topic());
container.setNameServer(rocketMQProperties.getNameServer());
String tags = annotation.selectorExpression();
if (!StringUtils.isEmpty(tags)) {
container.setSelectorExpression(tags);
}
container.setConsumerGroup(annotation.consumerGroup());
if (RocketMQListener.class.isAssignableFrom(bean.getClass())) {
container.setRocketMQListener((RocketMQListener) bean);
} else if (RocketMQReplyListener.class.isAssignableFrom(bean.getClass())) {
container.setRocketMQReplyListener((RocketMQReplyListener) bean);
}
container.setMessageConverter(rocketMQMessageConverter.getMessageConverter());
return container;
} private void validate(MultipleRocketMQMessageListener annotation) {
if (annotation.consumeMode() == ConsumeMode.ORDERLY &&
annotation.messageModel() == MessageModel.BROADCASTING) {
throw new BeanDefinitionValidationException(
"Bad annotation definition in @RocketMQMessageListener, messageModel BROADCASTING does not support ORDERLY message!");
}
} }

步骤六,准备多数据源listener

package com.heit.road.web.listener;

import com.heit.road.web.config.MultipleRocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component; @Component
@ConditionalOnProperty(prefix = "multiplerocketmq.road", value = {"name-server"})
@MultipleRocketMQMessageListener(soruce = "road", consumerGroup = "RoadRocketMqlistener6", topic = "road_test")
public class RoadRocketMqlistener implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
System.out.println("RoadRocketMqlistener 收到信息==》 " + message);
}
}
package com.heit.road.web.listener;

import com.heit.road.web.config.MultipleRocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component; @Component
@ConditionalOnProperty(prefix = "multiplerocketmq.xdt", value = {"name-server"})
@MultipleRocketMQMessageListener(soruce = "xdt", consumerGroup = "XdtRocketMqlistener6", topic = "road_test")
public class XdtRocketMqlistener implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
System.out.println("XdtRocketMqlistener 收到信息==》 " + message);
}
}

步骤七,编写测试方法,多数据源发送数据

//    @Qualifier("xdtmq")
@Lazy
@Resource
private RocketMQTemplate xdtmq; // @Qualifier("roadmq")
@Lazy
@Resource
private RocketMQTemplate roadmq; @ApiOperation("测试用表-MQ信息测试")
@PostMapping("sendTest")
public BaseBack<?> sendTest() {
xdtmq.asyncSend("road_test", "xdtmq123", new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
System.out.println("尝试xdt================================================");
System.out.println("信息发送成功");
System.out.println("sendResult = " + sendResult);
} @Override
public void onException(Throwable e) {
System.out.println("尝试xdt================================================");
System.out.println("信息发送失败");
e.printStackTrace();
}
}); roadmq.asyncSend("road_test", "roadmq123", new SendCallback() { @Override
public void onSuccess(SendResult sendResult) {
System.out.println("尝试road================================================");
System.out.println("信息发送成功");
System.out.println("sendResult = " + sendResult);
} @Override
public void onException(Throwable e) {
System.out.println("尝试road================================================");
System.out.println("信息发送失败");
e.printStackTrace();
}
});
return null;
}

结果如下:

springboot环境下的rokectMQ多数据源实现的更多相关文章

  1. 项目总结10:通过反射解决springboot环境下从redis取缓存进行转换时出现ClassCastException异常问题

    通过反射解决springboot环境下从redis取缓存进行转换时出现ClassCastException异常问题 关键字 springboot热部署  ClassCastException异常 反射 ...

  2. springboot环境下配置过滤器和拦截器

    以前我们在配置过滤器和拦截器的时候,都是一个类继承一个接口,然后在xml中配置一下就ok 但是,但是,这是springboot的环境,没有xml的配置.所以我们还要继续学习啊啊啊啊啊~~~~~ 先简单 ...

  3. 在springboot环境下tk-mybatis的使用记录

    1. 新建springboot工程 访问https://start.spring.io/,新建一个springboot工程. 自动生成的工程主要的注意点如下: 1)pom.xml <parent ...

  4. SpringBoot环境下使用测试类注入Mapper接口报错解决

    当我们在进行开发中难免会要用到测试类,而且测试类要注入Mapper接口,如果测试运行的时候包空指针异常,看看测试类上面的注解是否用对! 正常测试我们需要用到的注解有这些: @SpringBootTes ...

  5. SpringBoot环境下java实现文件的下载

    思路:文件下载,就是给服务器上的文件创建输入流,客户端创建输出流,将文件读出,读入到客户端的输出流中,(流与流的转换) package com.cst.icode.controller; import ...

  6. 优雅解决 SpringBoot 工程中多环境下 application.properties 的维护问题

    微信号:geekoftaste, 期待与大家一起探讨! 背景 我们知道 SpringBoot 有一个全局的配置文件 application.properties, 可以把工程里用到的占位符,第三方库的 ...

  7. springboot多环境下maven打包

    前言: 最近在项目中使用springboot时发现,采用在pom中定义不同的profile,并且maven打包时 采用-P参数并不能替换我application.properties文件中指定占位符的 ...

  8. linux环境下,springboot jar启动方式

    linux环境下,springboot jar启动方式 一.前台启动(ctrl+c会关掉进程) java -jar application.jar 二.后台启动(ctrl+c不会关闭) java -j ...

  9. SpringBoot利用spring.profiles.active=@spring.active@不同环境下灵活切换配置文件

    一.创建配置文件 配置文件结构:这里建三个配置文件,application.yml作为主配置文件配置所有共同的配置:-dev和-local分别配置两种环境下的不同配置内容,如数据库地址等. appli ...

  10. Mybatis在非spring环境下配置文件中使用外部数据源(druidDatasource)

    Spring环境下, MyBatis可以通过其本身的增强mybatis-spring提供的org.mybatis.spring.SqlSessionFactoryBean来注入第三方DataSourc ...

随机推荐

  1. Excel两张表查重,返回True

    =VLOOKUP(P2,Sheet2!A:A,1,0)=P2 VLOOKUP(A1,Sheet2!A:D,1,0) VLOOKUP--首列查找 A1--查找条件 Sheet2--同一工作簿中的第二工作 ...

  2. .NET 9使用Scalar替代Swagger

    背景 .NET 9刚刚正式发布了,如果你创建一个空的Asp.Net Core 9.0的Web API项目,启动之后,你会惊讶地发现陪伴你多年的Swagger没有了!--这是因为ASP.NET Core ...

  3. 剖析Air724UG的硬件设计,有大发现?03篇

    ​ 今天我们分享第三部分. 四.射频接口 天线接口管脚定义如下: 表格 19:RF_ANT 管脚定义 管脚名 序号 描述 LTE_ANT 46 LTE 天线接口 BT/WiFi_ANT 34 蓝牙/W ...

  4. 查壳工具之Exeinfo PE

    简介 Exeinfo PE是一款免费.专业的程序查壳软件,可以查看exe.dll程序的编译信息,开发语言,是否加壳,壳的种类以及入口地址等信息. Exeinfo PE下载地址:https://gith ...

  5. vue 使用 application/x-www-form-urlencoded格式提交数据

    const params = new URLSearchParams();//前端在传参时需要先新建一个URLSearchParams对象,然后将参数append到这个对象中 params.appen ...

  6. Excel使用IF{1,0}虚拟数组+VLOOKUP实现联合查询

    以此案例举例: 使用IF({1,0})建立虚拟数据的方法,整体输入的公式是: =VLOOKUP(E2&F2,IF({1,0},A:A&B:B,C:C),2,0) 输入完公式之后,需要按 ...

  7. GraphQL Part V: 字段,参数和变量

    字段 我们对字段已经有了好的起点,我们在 HelloWorldQuery 中有两个字段:hello 和 world.他们都是单值字段. 现在我们可以扩展应用来支持复杂类型.例如,我们想象一下,我们在创 ...

  8. Mac 配置多版本JDK

    @ 目录 前言 一.下载并安装多个JDK版本 二.配置环境变量 三.切换JDK版本 四.下篇预告!!! 总结 前言 请各大网友尊重本人原创知识分享,谨记本人博客:南国以南i. 提示:以下是本篇文章正文 ...

  9. Qt/C++地图轨迹回放/自定义图标/动态平滑移动/导入轨迹数据/支持各种地图包括天地图

    一.前言说明 这个轨迹回放的功能迭代过很多个版本,最初的版本是轨迹点的坐标每次都是删除折线再重新生成折线,后面发现有内存泄漏,地图js中并不会及时的释放没有用的对象,哪怕是用地图提供的clearove ...

  10. Qt通用方法及类库3

    函数名 //设置全局样式 static void setStyle(QUIWidget::Style style); static void setStyle(const QString &q ...