1. 定义mq发送和接收接口
/**
* MQ 消息发送器 接口定义
* @author tostyle
* 2022/3/7 10:26
*/
public interface MqSender {
/** 推送MQ消息, 实时 **/
void send(AbstractMq mqModel);
/** 推送MQ消息, 延迟接收,单位:s **/
void send(AbstractMq mqModel, int delay);
}
/**
* MQ 消息接收器 接口定义
* @author tostyle
* 2022/3/7 10:27
*/
public interface MqMsgReceiver {
/** 接收消息 **/
void receiveMsg(String msg);
}
2. 编写activemq和rabbitmq的实现
2.1 activemq配置
package com.tostyle.mq.vender.activemq;
import com.tostyle.mq.constant.MqSendTypeEnum;
import com.tostyle.mq.constant.MqVenderConstant;
import com.tostyle.mq.model.AbstractMq;
import org.apache.activemq.command.ActiveMQQueue;
import org.apache.activemq.command.ActiveMQTopic;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.config.JmsListenerContainerFactory;
import org.springframework.stereotype.Component;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* activeMQ的配置项
* @author tostyle
* 2022/3/7 10:55
*/
@Component
@ConditionalOnProperty(name = MqVenderConstant.YML_VENDER_KEY, havingValue = MqVenderConstant.ACTIVE_MQ)
public class ActiveMqConfig {
Map<String, Destination> map = new ConcurrentHashMap<>();
public Destination getDestination(AbstractMq mqModel){
if(map.get(mqModel.getMqName()) == null){
this.init(mqModel.getMqName(), mqModel.getMqType());
}
return map.get(mqModel.getMqName());
}
private synchronized void init(String mqName, MqSendTypeEnum mqSendTypeEnum){
if(mqSendTypeEnum == MqSendTypeEnum.QUEUE){
map.put(mqName, new ActiveMQQueue(mqName) );
}else{
map.put(mqName, new ActiveMQTopic(mqName) );
}
}
public static final String TOPIC_LISTENER_CONTAINER = "jmsTopicListenerContainer";
/** 新增jmsListenerContainer, 用于接收topic类型的消息 **/
@Bean
public JmsListenerContainerFactory<?> jmsTopicListenerContainer(ConnectionFactory factory){
DefaultJmsListenerContainerFactory bean = new DefaultJmsListenerContainerFactory();
bean.setPubSubDomain(true);
bean.setConnectionFactory(factory);
return bean;
}
}
2.2 activemq发送代码
package com.tostyle.mq.vender.activemq;
import com.tostyle.mq.constant.MqVenderConstant;
import com.tostyle.mq.model.AbstractMq;
import com.tostyle.mq.vender.MqSender;
import org.apache.activemq.ScheduledMessage;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.jms.TextMessage;
/**
* activeMQ 消息发送器的实现
* @author tostyle
* 2022/3/7 10:54
*/
@Component
@ConditionalOnProperty(name = MqVenderConstant.YML_VENDER_KEY, havingValue = MqVenderConstant.ACTIVE_MQ)
public class ActiveMqSender implements MqSender {
@Resource
private ActiveMqConfig activeMqConfig;
@Resource
private JmsTemplate jmsTemplate;
@Override
public void send(AbstractMq mqModel) {
jmsTemplate.convertAndSend(activeMqConfig.getDestination(mqModel), mqModel.toMsg());
}
@Override
public void send(AbstractMq mqModel, int delay) {
jmsTemplate.send(activeMqConfig.getDestination(mqModel), session -> {
TextMessage tm = session.createTextMessage(mqModel.toMsg());
tm.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_DELAY, delay * 1000);
tm.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_PERIOD, 1000);
tm.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_REPEAT, 1);
return tm;
});
}
}
3. rabbitmq实现
3.1 rabbitmq配置
package com.tostyle.mq.vender.rabbitmq;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import com.tostyle.mq.constant.MqSendTypeEnum;
import com.tostyle.mq.constant.MqVenderConstant;
import com.tostyle.mq.model.AbstractMq;
import com.tostyle.mq.utils.SpringBeansUtil;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Set;
/**
* RabbitMQ的配置项
* 1. 注册全部定义好的Queue Bean
* 2. 动态注册fanout交换机
* 3. 将Queue模式绑定到延时消息的交换机
*
*
* @author tostyle
* 2022/3/7 10:30
*/
@Component
@ConditionalOnProperty(name = MqVenderConstant.YML_VENDER_KEY, havingValue = MqVenderConstant.RABBIT_MQ)
public class RabbitMqConfig {
/** 全局定义延迟交换机名称 **/
public static final String DELAYED_EXCHANGE_NAME = "delayedExchange";
/** 扇形交换机前缀(activeMQ中的topic模式), 需根据queue动态拼接 **/
public static final String FANOUT_EXCHANGE_NAME_PREFIX = "fanout_exchange_";
/** 注入延迟交换机Bean **/
@Resource
@Qualifier(DELAYED_EXCHANGE_NAME)
private CustomExchange delayedExchange;
/** 注入rabbitMQBeanProcessor **/
@Resource
private RabbitMqBeanProcessor rabbitMqBeanProcessor;
/** 在全部bean注册完成后再执行 **/
@PostConstruct
public void init(){
// 获取到所有的MQ定义
Set<Class<?>> set = ClassUtil.scanPackageBySuper(ClassUtil.getPackage(AbstractMq.class), AbstractMq.class);
for (Class<?> aClass : set) {
// 实例化
AbstractMq amq = (AbstractMq) ReflectUtil.newInstance(aClass);
// 注册Queue === new Queue(name), queue名称/bean名称 = mqName
rabbitMqBeanProcessor.beanDefinitionRegistry.registerBeanDefinition(amq.getMqName(),
BeanDefinitionBuilder.rootBeanDefinition(Queue.class).addConstructorArgValue(amq.getMqName()).getBeanDefinition());
// 广播模式
if(amq.getMqType() == MqSendTypeEnum.BROADCAST){
// 动态注册交换机, 交换机名称/bean名称 = FANOUT_EXCHANGE_NAME_PREFIX + amq.getMQName()
rabbitMqBeanProcessor.beanDefinitionRegistry.registerBeanDefinition(FANOUT_EXCHANGE_NAME_PREFIX +amq.getMqName(),
BeanDefinitionBuilder.genericBeanDefinition(FanoutExchange.class, () ->{
// 普通FanoutExchange 交换机
return new FanoutExchange(FANOUT_EXCHANGE_NAME_PREFIX +amq.getMqName(),true,false);
}
).getBeanDefinition()
);
}else{
// 延迟交换机与Queue进行绑定, 绑定Bean名称 = mqName_DelayedBind
rabbitMqBeanProcessor.beanDefinitionRegistry.registerBeanDefinition(amq.getMqName() + "_DelayedBind",
BeanDefinitionBuilder.genericBeanDefinition(Binding.class, () ->
BindingBuilder.bind(SpringBeansUtil.getBean(amq.getMqName(), Queue.class)).to(delayedExchange).with(amq.getMqName()).noargs()
).getBeanDefinition()
);
}
}
}
}
package com.tostyle.mq.vender.rabbitmq;
import com.tostyle.mq.constant.MqVenderConstant;
import org.springframework.amqp.core.CustomExchange;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* 将spring容器的 [bean注册器]放置到属性中,为 RabbitConfig提供访问。
* 顺序:
* 1. postProcessBeanDefinitionRegistry (存放注册器)
* 2. postProcessBeanFactory (没有使用)
* 3. 注册延迟消息交换机的bean: delayedExchange
* 4. 动态配置RabbitMQ所需的bean。
* @author tostyle
* 2022/3/7 10:28
*/
@Configuration
@ConditionalOnProperty(name = MqVenderConstant.YML_VENDER_KEY, havingValue = MqVenderConstant.RABBIT_MQ)
public class RabbitMqBeanProcessor implements BeanDefinitionRegistryPostProcessor {
/** bean注册器 **/
protected BeanDefinitionRegistry beanDefinitionRegistry;
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
this.beanDefinitionRegistry = beanDefinitionRegistry;
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
/** 自定义交换机: 用于延迟消息 **/
@Bean(name = RabbitMqConfig.DELAYED_EXCHANGE_NAME)
CustomExchange delayedExchange() {
Map<String, Object> args = new HashMap<>();
args.put("x-delayed-type", "direct");
return new CustomExchange(RabbitMqConfig.DELAYED_EXCHANGE_NAME, "x-delayed-message", true, false, args);
}
}
3.2 rabbitmq发送类
package com.tostyle.mq.vender.rabbitmq;
import com.tostyle.mq.constant.MqSendTypeEnum;
import com.tostyle.mq.constant.MqVenderConstant;
import com.tostyle.mq.model.AbstractMq;
import com.tostyle.mq.vender.MqSender;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @author tostyle
* 2022/3/7 10:40
*/
@Component
@ConditionalOnProperty(name = MqVenderConstant.YML_VENDER_KEY, havingValue = MqVenderConstant.RABBIT_MQ)
public class RabbitMqSender implements MqSender {
@Resource
private RabbitTemplate rabbitTemplate;
@Override
public void send(AbstractMq mqModel) {
if(mqModel.getMqType() == MqSendTypeEnum.QUEUE){
rabbitTemplate.convertAndSend(mqModel.getMqName(), mqModel.toMsg());
}else{
// fanout模式 的 routeKEY 没意义。
this.rabbitTemplate.convertAndSend(RabbitMqConfig.FANOUT_EXCHANGE_NAME_PREFIX + mqModel.getMqName(), null, mqModel.toMsg());
}
}
@Override
public void send(AbstractMq mqModel, int delay) {
if(mqModel.getMqType() == MqSendTypeEnum.QUEUE){
rabbitTemplate.convertAndSend(RabbitMqConfig.DELAYED_EXCHANGE_NAME, mqModel.getMqName(), mqModel.toMsg(), messagePostProcessor ->{
messagePostProcessor.getMessageProperties().setDelay(Math.toIntExact(delay * 1000));
return messagePostProcessor;
});
}else{
// fanout模式 的 routeKEY 没意义。 没有延迟属性
this.rabbitTemplate.convertAndSend(RabbitMqConfig.FANOUT_EXCHANGE_NAME_PREFIX + mqModel.getMqName(), null, mqModel.toMsg());
}
}
}
以上就是核心的代码,
完整实例
https://gitee.com/todostyle/springcloud.git