前提:在可运行的SpringBoot的项目内引用以下JAR包
整个工具的代码都在Gitee或者Github地址内
gitee:solomon-parent: 这个项目主要是总结了工作上遇到的问题以及学习一些框架用于整合例如:rabbitMq、reids、Mqtt、S3协议的文件服务器、mongodb
需要引入的JAR包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
1.新增两个注解,一个是rabbitmq注解,一个是rabbitmq重试注解
/**
* rabbitmq标注注解
* @author huangweihua
*/
@Target(value = { ElementType.FIELD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface RabbitMq {
@AliasFor(annotation = Component.class)
String value() default "";
/**
* 队列
*/
String[] queues();
/**
* 交换器
*/
String exchange() default "";
/**
* 路由规则
*/
String routingKey() default "";
/**
* 是否持久化
*/
boolean isPersistence() default true;
/**
* 确认模式(只支持手动提交,自动提交代码暂时不支持)
*/
AcknowledgeMode mode() default AcknowledgeMode.MANUAL;
/**
* 每个队列消费者数量
*/
int consumersPerQueue() default 1;
/**
* 每次的接收的消息数量最大数值(0:公平分发 1:不公平分发)
*/
int prefetchCount() default AbstractMessageListenerContainer.DEFAULT_PREFETCH_COUNT;
/**
* 交换类型(暂时不支持system,只支持DIRECT、TOPIC、FANOUT、HEADERS)
*/
String exchangeTypes() default ExchangeTypes.DIRECT;
/**
* 消息最大存活时间
*/
long delay() default 0L;
/**
* 死信队列Class
*/
Class dlxClazz() default void.class;
/**
* 是否启用插件内的ttl队列
*/
boolean isDelayExchange() default false;
/**
* Headers交换器下需要配置 是否匹配全部头部属性 默认非全部
*/
boolean matchAll() default false;
/**
* Headers交换器下需要配置 是否匹配值,true就是匹配值,false就是不匹配值,只判断是否存在
*/
boolean matchValue() default false;
/**
* 需要匹配的头部消息,如matchAll为True清空则需要匹配全部headers存在,才可通过,false为只要匹配中其中一个即可通过
* 如果matchValue为true,headers结果应为 0:key,1:value,2:key,3:value.........如此下去,false的话则全部为key
* @return
*/
String[] headers() default {};
}
/**
* rabbitMQ重试次数注解
* @author
*/
@Target(value = { ElementType.FIELD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface RabbitMqRetry {
/**
* 重试次数
*/
int retryNumber() default 2;
/**
* 重试间隔 默认20秒
*/
long initialInterval() default 20000L;
/**
* 最大重试间隔为100秒
*/
long maxInterval() default 100000L;
/**
* 重试间隔乘法策略
*/
double multiplier() default 5;
}
2.新增Rabbitmq配置
public abstract class AbstractRabbitCallBack {
private Logger logger = LoggerUtils.logger(AbstractRabbitCallBack.class);
/**
* 保存mq消费成功或失败后方法
*/
public abstract void saveRabbitCallBack(CorrelationData correlationData, boolean ack, String cause);
/**
* 保存mq消息丢失方法
*/
public abstract void saveReturnedMessage(ReturnedMessage returned);
}
public class RabbitCallBack implements ReturnsCallback, ConfirmCallback {
private Logger logger = LoggerUtils.logger(RabbitCallBack.class);
private static Collection<AbstractRabbitCallBack> rabbitCallBackList;
public RabbitCallBack(Collection<AbstractRabbitCallBack> rabbitCallBackList){
RabbitCallBack.rabbitCallBackList = rabbitCallBackList;
}
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if (!ack) {
logger.error("RabbitMQConfig:消息发送失败:correlationData({}),ack(false),cause({})", correlationData,cause);
}
if(ValidateUtils.isNotEmpty(RabbitCallBack.rabbitCallBackList)){
for(AbstractRabbitCallBack abstractRabbitCallBack : RabbitCallBack.rabbitCallBackList){
abstractRabbitCallBack.saveRabbitCallBack(correlationData,ack,cause);
}
}
}
@Override
public void returnedMessage(ReturnedMessage returned) {
logger.error("RabbitMQConfig:消息丢失:exchange({}),route({}),replyCode({}),replyText({}),message:{}",
returned.getExchange(), returned.getRoutingKey(), returned.getReplyCode(), returned.getReplyText(), returned.getMessage());
if(ValidateUtils.isNotEmpty(RabbitCallBack.rabbitCallBackList)){
for(AbstractRabbitCallBack abstractRabbitCallBack : RabbitCallBack.rabbitCallBackList){
abstractRabbitCallBack.saveReturnedMessage(returned);
}
}
}
}
@Configuration
@EnableConfigurationProperties(value={RabbitProperties.class})
@Import(value = {RabbitUtils.class, DelayedMQService.class, DirectMQService.class, FanoutMQService.class, TopicMQService.class, HeadersMQService.class})
public class RabbitMQConfig {
private Logger logger = LoggerUtils.logger(RabbitMQConfig.class);
/**
* 接受数据自动的转换为Json
*/
@Bean("messageConverter")
@ConditionalOnMissingBean(MessageConverter.class)
public MessageConverter messageConverter() {
return new Jackson2JsonMessageConverter();
}
@Bean("rabbitTemplate")
@ConditionalOnMissingBean(RabbitTemplate.class)
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory,MessageConverter messageConverter,
RabbitProperties properties) {
final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(messageConverter);
rabbitTemplate.setMandatory(ValidateUtils.getOrDefault(properties.getTemplate().getMandatory(),true));
if(ValidateUtils.isNotEmpty(properties.getTemplate().getReceiveTimeout())){
rabbitTemplate.setReceiveTimeout(properties.getTemplate().getReceiveTimeout().toMillis());
}
if(ValidateUtils.isNotEmpty(properties.getTemplate().getReplyTimeout())){
rabbitTemplate.setReplyTimeout(properties.getTemplate().getReplyTimeout().toMillis());
}
Map<String,AbstractRabbitCallBack> callBackMap = SpringUtil.getBeansOfType(AbstractRabbitCallBack.class);
if(ValidateUtils.isNotEmpty(callBackMap)){
RabbitCallBack rabbitCallBack = new RabbitCallBack(callBackMap.values());
rabbitTemplate.setConfirmCallback(rabbitCallBack);
rabbitTemplate.setReturnsCallback(rabbitCallBack);
}
return rabbitTemplate;
}
@Bean("rabbitAdmin")
@ConditionalOnMissingBean(RabbitAdmin.class)
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
logger.info("RabbitAdmin启动了。。。");
// 设置启动spring容器时自动加载这个类(这个参数现在默认已经是true,可以不用设置)
rabbitAdmin.setAutoStartup(true);
return rabbitAdmin;
}
}
3.Rabbitmq通用消费器
/**
* RabbitMq消费器
*/
public abstract class AbstractConsumer<T,R> extends MessageListenerAdapter {
protected final Logger logger = LoggerUtils.logger(getClass());
private final int retryNumber = 1;
@Override
public void onMessage(Message message, Channel channel) throws Exception {
MessageProperties messageProperties = message.getMessageProperties();
long deliveryTag = messageProperties.getDeliveryTag();
String correlationId = messageProperties.getHeader("spring_returned_message_correlation");
boolean isAutoAck = getIsAutoAck();
try {
if(checkMessageKey(messageProperties)){
throw new BaseException(MqErrorCode.MESSAGE_REPEAT_CONSUMPTION);
}
// 消费者内容
String json= new String(message.getBody(), StandardCharsets.UTF_8);
logger.debug("线程名:{},AbstractConsumer:消费者消息: {}",Thread.currentThread().getName(), json);
RabbitMqModel rabbitMqModel = JackJsonUtils.conversionClass(json, RabbitMqModel.class);
// 消费者消费消息
R result = this.handleMessage((T) rabbitMqModel.getBody());
if(!isAutoAck){
// 手动确认消息
channel.basicAck(deliveryTag, false);
}
//保存消费成功消息
saveLog(result,message,rabbitMqModel);
} catch (Throwable e) {
// 消费失败次数不等于空并且失败次数大于某个次数,不处理直接return,并记录到数据库
logger.error("AbstractConsumer:消费报错 异常为:", e);
//将消费失败的记录保存到数据库或者不处理也可以
this.saveFailMessage(message, e);
//保存重试失败次数达到retryNumber上线后拒绝此消息入队列并删除redis
saveFailNumber(messageProperties, channel, deliveryTag,correlationId);
throw e;
} finally {
//iCacheService.del(BaseICacheCode.RABBIT_LOCK,correlationId);
}
}
/**
* 记录失败次数并决定是否拒绝此消息
*/
public void saveFailNumber(MessageProperties messageProperties, Channel channel, long deliveryTag,String correlationId) throws Exception {
Integer lock = messageProperties.getHeader("retryNumber");
Integer actualLock = ValidateUtils.isEmpty(lock) ? 1 : lock + 1;
logger.error("rabbitMQ 失败记录:消费者correlationId为:{},deliveryTag为:{},失败次数为:{}", correlationId, deliveryTag,actualLock);
int retryNumber = getRetryNumber();
if(retryNumber <= this.retryNumber || actualLock >= retryNumber){
if(retryNumber <= this.retryNumber){
logger.error("rabbitMQ 失败记录:因记录不需要重试因此直接拒绝此消息,消费者correlationId为:{},消费者设置重试次数为:{}", correlationId, retryNumber);
} else {
logger.error("rabbitMQ 失败记录:已满足重试次数,删除redis消息并且拒绝此消息,消费者correlationId为:{},重试次数为:{}", correlationId, actualLock);
}
channel.basicNack(messageProperties.getDeliveryTag(), false, false);
} else {
logger.error("rabbitMQ 失败记录:因记录重试次数还未达到重试上限,还将继续进行重试,消费者correlationId为:{},消费者设置重试次数为:{},现重试次数为:{}", correlationId, retryNumber,actualLock);
messageProperties.setHeader("retryNumber",actualLock);
}
}
/**
* 获取重试次数,默认为2
*
* @return
*/
public int getRetryNumber() {
RabbitMqRetry rabbitMqRetry = getClass().getAnnotation(RabbitMqRetry.class);
return ValidateUtils.isEmpty(rabbitMqRetry) ? retryNumber : rabbitMqRetry.retryNumber();
}
/**
* 获取是否是自动确认
*
* @return
*/
public boolean getIsAutoAck() {
RabbitMq rabbitMq = getClass().getAnnotation(RabbitMq.class);
return ValidateUtils.isEmpty(rabbitMq) ? false : ValidateUtils.equalsIgnoreCase(AcknowledgeMode.AUTO.toString(),rabbitMq.mode().toString()) ? true : false;
}
/**
* 消费方法
* @param body 请求数据
*/
public abstract R handleMessage(T body) throws Exception;
/**
* 保存消费失败的消息
*
* @param message mq所包含的信息
* @param e 异常
*/
public void saveFailMessage(Message message, Throwable e){
}
/**
* 判断是否重复消费
* @return true 重复消费 false 不重复消费
*/
public boolean checkMessageKey(MessageProperties messageProperties){
return false;
}
/**
* 保存消费成功消息
* @param result
* @param message
* @param rabbitMqModel
*/
public abstract void saveLog(R result,Message message,RabbitMqModel rabbitMqModel);
}
4.Rabbitmq使用的实体类
public class InitRabbitBinding implements Serializable {
String queue;
String exchange;
String routingKey;
Map<String, Object> args;
public InitRabbitBinding(RabbitMq rabbitMq,String queueName, boolean isInitDlxMap, boolean isAddDlxPrefix) {
// 队列名
this.queue = this.getName(queueName, isAddDlxPrefix);
// 交换机名
this.exchange = this.getName(rabbitMq.exchange(), isAddDlxPrefix);
// 路由
this.routingKey = this.getName(rabbitMq.routingKey(), isAddDlxPrefix);
this.args = this.initArgs(rabbitMq, isInitDlxMap);
}
public String getQueue() {
return queue;
}
public void setQueue(String queue) {
this.queue = queue;
}
public String getExchange() {
return exchange;
}
public void setExchange(String exchange) {
this.exchange = exchange;
}
public String getRoutingKey() {
return routingKey;
}
public void setRoutingKey(String routingKey) {
this.routingKey = routingKey;
}
public Map<String, Object> getArgs() {
return args;
}
public void setArgs(Map<String, Object> args) {
this.args = args;
}
private String getName(String name, boolean isAddDlxPrefix) {
if (ValidateUtils.isEmpty(name)) {
return name;
}
return isAddDlxPrefix ? BaseRabbitMqCode.DLX_PREFIX + name : name;
}
/**
* 绑定死信队列参数
*
* @param rabbitMq MQ注解
* @param isInitDlxMap 是否初始化死信队列参数
* @return 死信队列参数
*/
private Map<String, Object> initArgs(RabbitMq rabbitMq,boolean isInitDlxMap) {
boolean dlx = !void.class.equals(rabbitMq.dlxClazz()) || rabbitMq.delay() != 0L;
if (!dlx || !isInitDlxMap) {
return null;
}
Map<String, Object> args = new HashMap<>(3);
args.put(BaseRabbitMqCode.DLX_EXCHANGE_KEY, BaseRabbitMqCode.DLX_PREFIX + rabbitMq.exchange());
if (ValidateUtils.isNotEmpty(rabbitMq.routingKey())) {
args.put(BaseRabbitMqCode.DLX_ROUTING_KEY, BaseRabbitMqCode.DLX_PREFIX + rabbitMq.routingKey());
}
/**
* x-message-ttl 在创建队列时设置的消息TTL,表示消息在队列中最多能存活多久(ms);
* Expiration 发布消息时设置的消息TTL,消息自产生后的存活时间(ms);
* x-delay 由rabbitmq_delayed_message_exchange插件提供TTL,从交换机延迟投递到队列的时间(ms);
*/
if (rabbitMq.delay() != 0L && !rabbitMq.isDelayExchange()) {
args.put(BaseRabbitMqCode.DLX_TTL, rabbitMq.delay());
}
return args;
}
}
public class MessageQueueDatail implements Serializable {
private static final long serialVersionUID = 7292656135434186436L;
/**
* 队列名称
*/
private String queueName;
/**
* 监听容器标识
*/
private String containerIdentity;
/**
* 监听是否有效
*/
private boolean activeContainer;
/**
* 是否正在监听
*/
private boolean running;
/**
* 活动消费者数量
*/
// private int activeConsumerCount;
public MessageQueueDatail(String queueName, AbstractMessageListenerContainer container) {
this.queueName = queueName;
this.running = container.isRunning();
this.activeContainer = container.isActive();
// this.activeConsumerCount = container.;
this.containerIdentity = "Container@" + ObjectUtils.getIdentityHexString(container);
}
public String getQueueName() {
return queueName;
}
public void setQueueName(String queueName) {
this.queueName = queueName;
}
public String getContainerIdentity() {
return containerIdentity;
}
public void setContainerIdentity(String containerIdentity) {
this.containerIdentity = containerIdentity;
}
public boolean isActiveContainer() {
return activeContainer;
}
public void setActiveContainer(boolean activeContainer) {
this.activeContainer = activeContainer;
}
public boolean isRunning() {
return running;
}
public void setRunning(boolean running) {
this.running = running;
}
}
/**
* 基础发送MQ基类
*/
public class RabbitMqModel<T> extends BaseMq<T> {
private static final long serialVersionUID = -5799719724717056583L;
/**
* 交换机
*/
private String exchange;
/**
* 路由规则
*/
private String routingKey;
/**
* 设置头部属性
*/
private Map<String,Object> headers;
public Map<String, Object> getHeaders() {
return headers;
}
public void setHeaders(Map<String, Object> headers) {
this.headers = headers;
}
public RabbitMqModel() {
super();
}
public RabbitMqModel(T body) {
super(body);
}
public RabbitMqModel(String exchange, T body) {
super(body);
this.exchange = exchange;
}
public RabbitMqModel(String exchange, String routingKey, T body) {
super(body);
this.exchange = exchange;
this.routingKey = routingKey;
}
public String getExchange() {
return exchange;
}
public void setExchange(String exchange) {
this.exchange = exchange;
}
public String getRoutingKey() {
return routingKey;
}
public void setRoutingKey(String routingKey) {
this.routingKey = routingKey;
}
}
5.Rabbitmq初始化配置
@Configuration
@EnableConfigurationProperties(value = {RabbitProperties.class})
public class RabbitMQInitConfig implements CommandLineRunner {
private final Logger logger = LoggerUtils.logger(getClass());
private RabbitMq rabbitMq;
private final RabbitAdmin admin;
private final CachingConnectionFactory connectionFactory;
/**
* 所有的队列监听容器MAP
*/
public final static Map<String, AbstractMessageListenerContainer> allQueueContainerMap = new ConcurrentHashMap<>();
public RabbitMQInitConfig(RabbitAdmin admin, CachingConnectionFactory connectionFactory) {
this.admin = admin;
this.connectionFactory = connectionFactory;
}
@Override
public void run(String... args) throws Exception {
//根据RabbitMq注解找出使用这个注解的类并初始化消费队列
this.init(new ArrayList<>(SpringUtil.getBeansWithAnnotation(RabbitMq.class).values()));
}
/**
* 初始化MQ
*
* @param clazzList 消费者集合数组
*/
private void init(List<Object> clazzList) {
// 判断消费者队列是否存在
if (ValidateUtils.isEmpty(clazzList)) {
logger.debug("MessageListenerConfig:没有rabbitMq消费者");
return;
}
Map<String, AbstractMQService> abstractMQMap = SpringUtil.getBeansOfType(AbstractMQService.class);
// 遍历消费者队列进行初始化绑定以及监听
for (Object abstractConsumer : clazzList) {
// 根据反射获取rabbitMQ注解信息
rabbitMq = AnnotationUtils.findAnnotation(abstractConsumer.getClass(), RabbitMq.class);
if (ValidateUtils.isNotEmpty(rabbitMq)) {
String[] queues = rabbitMq.queues();
for (String queueName : queues) {
// 初始化队列绑定
Queue queue = initBinding(abstractMQMap, queueName, true, false);
// 启动监听器并保存已启动的MQ
RabbitMQInitConfig.allQueueContainerMap
.put(queue.getName(), this.startContainer((AbstractConsumer) abstractConsumer, queue));
// 初始化死信队列
this.initDlx(queue,abstractMQMap);
}
}
}
}
/**
* 初始化死信队列MQ
*/
private void initDlx(Queue queue,Map<String, AbstractMQService> abstractMQMap) {
// 判断消费队列是否需要死信队列 只要死信队列或者延时队列为true即可判断为开启死信队列
Class<?> clazz = rabbitMq.dlxClazz();
String queueName = queue.getName();
if (void.class.equals(clazz)) {
logger.debug("MessageListenerConfig:initDlx 队列:{}不需要死信队列", queueName);
return;
}
// 判断设置死信队列的类必须是为AbstractDlxConsumer下的子类
if (!AbstractConsumer.class.isAssignableFrom(clazz) || AbstractConsumer.class.equals(clazz)) {
logger.debug("MessageListenerConfig:队列:{}死信队列设置错误,死信队列类名为:{}", queueName, clazz.getName());
return;
}
// 获取死信队列类
AbstractConsumer abstractConsumer = (AbstractConsumer) SpringUtil.getBean(clazz);
// 初始化队列绑定
Queue queues = initBinding(abstractMQMap, queueName, false, true);
// 启动监听器
this.startContainer(abstractConsumer, queues);
logger.debug("MessageListenerConfig队列:{}绑定{}死信队列", queue.getName(), queues.getName());
}
/**
* 启动监听器
*
* @param abstractConsumer 抽象的消费者
*/
private DirectMessageListenerContainer startContainer(AbstractConsumer abstractConsumer, Queue queue) {
// 新建监听器
DirectMessageListenerContainer container = new DirectMessageListenerContainer(connectionFactory);
// 新建消息侦听器适配器
MessageListenerAdapter adapter = new MessageListenerAdapter(abstractConsumer);
// 设置编码格式
adapter.setEncoding(BaseCode.UTF8);
// 监听器配置队列
container.setQueues(queue);
// 消费者的监听
container.setMessageListener(adapter);
// 是否自动声明 队列、交换机、绑定
container.setAutoDeclare(true);
// 设置消费者提交方式
container.setAcknowledgeMode(rabbitMq.mode());
//为每个队列添加多个消费者 增加并行度
container.setConsumersPerQueue(rabbitMq.consumersPerQueue());
container.setPrefetchCount(rabbitMq.prefetchCount());
container.setAmqpAdmin(admin);
RabbitMqRetry rabbitMqRetry = AnnotationUtils.findAnnotation(abstractConsumer.getClass(), RabbitMqRetry.class);
if (ValidateUtils.isNotEmpty(rabbitMqRetry) && AbstractConsumer.class
.isAssignableFrom(abstractConsumer.getClass())) {
//设置重试机制
container.setAdviceChain(setRabbitRetry(rabbitMqRetry, admin));
}
// 启动对应的适配器
container.start();
return container;
}
public RetryOperationsInterceptor setRabbitRetry(RabbitMqRetry rabbitMqRetry, RabbitAdmin admin) {
RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setBackOffPolicy(backOffPolicyByProperties(rabbitMqRetry));
retryTemplate.setRetryPolicy(retryPolicyByProperties(rabbitMqRetry));
return RetryInterceptorBuilder
.stateless()
.retryOperations(retryTemplate)
.recoverer(new RepublishMessageRecoverer(admin.getRabbitTemplate()))
.build();
}
public ExponentialBackOffPolicy backOffPolicyByProperties(RabbitMqRetry rabbitMqRetry) {
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
// 重试间隔
backOffPolicy.setInitialInterval(rabbitMqRetry.initialInterval());
// 重试最大间隔
backOffPolicy.setMaxInterval(rabbitMqRetry.maxInterval());
// 重试间隔乘法策略
backOffPolicy.setMultiplier(rabbitMqRetry.multiplier());
return backOffPolicy;
}
public SimpleRetryPolicy retryPolicyByProperties(RabbitMqRetry rabbitMqRetry) {
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(rabbitMqRetry.retryNumber());
return retryPolicy;
}
private Queue initBinding(Map<String, AbstractMQService> abstractMQMap, String queue, boolean isInitDlxMap,
boolean isAddDlxPrefix) {
AbstractMQService abstractMQService = (ValidateUtils.isNotEmpty(rabbitMq) && rabbitMq.isDelayExchange())
? abstractMQMap.get("delayedMQService") : abstractMQMap
.get(rabbitMq.exchangeTypes() + AbstractMQService.SERVICE_NAME);
return abstractMQService.initBinding(rabbitMq, queue, admin, isInitDlxMap, isAddDlxPrefix);
}
}
6.队列注册接口实现
public abstract class AbstractMQService {
public static String SERVICE_NAME = "MQService";
public Queue initBinding(RabbitMq rabbitMq,String queueName, RabbitAdmin admin, boolean isInitDlxMap, boolean isAddDlxPrefix) {
InitRabbitBinding initRabbitBinding = new InitRabbitBinding(rabbitMq,queueName, isInitDlxMap, isAddDlxPrefix);
// 初始化队列
Queue queue = new Queue(initRabbitBinding.getQueue(), rabbitMq.isPersistence(), false, false,initRabbitBinding.getArgs());
// 绑定队列
admin.declareQueue(queue);
// 绑定交换机
AbstractExchange exchange = initExchange(initRabbitBinding.getExchange(),rabbitMq);
admin.declareExchange(exchange);
// 绑定
admin.declareBinding(this.initBinding(queue,exchange,initRabbitBinding.getRoutingKey(),rabbitMq));
return queue;
}
/**
* 初始化交换机
*/
protected abstract AbstractExchange initExchange(String exchange,RabbitMq rabbitMq);
/**
* 初始化绑定
* @return
*/
protected abstract Binding initBinding(Queue queue,AbstractExchange exchange,String routingKey,RabbitMq rabbitMq);
}
/**
* RabbitMq延迟队列注册
*/
@Service("delayedMQService")
public class DelayedMQService extends AbstractMQService {
@Override
protected AbstractExchange initExchange(String exchangeName, RabbitMq rabbitMq) {
Map<String, Object> props = new HashMap<>(1);
//延迟交换器类型
props.put("x-delayed-type", rabbitMq.exchangeTypes());
return new CustomExchange(exchangeName, "x-delayed-message", true, false, props);
}
@Override
protected Binding initBinding(Queue queue, AbstractExchange exchange, String routingKey,RabbitMq rabbitMq) {
return BindingBuilder.bind(queue).to(exchange).with(routingKey).noargs();
}
}
/**
* RabbitMq直连队列注册
*/
@Service("directMQService")
public class DirectMQService extends AbstractMQService {
@Override
protected AbstractExchange initExchange(String exchangeName, RabbitMq rabbitMq) {
return new DirectExchange(exchangeName);
}
@Override
protected Binding initBinding(Queue queue, AbstractExchange exchange, String routingKey,RabbitMq rabbitMq) {
return BindingBuilder.bind(queue).to((DirectExchange) exchange).with(routingKey);
}
}
/**
* RabbitMq广播队列注册
*/
@Service("fanoutMQService")
public class FanoutMQService extends AbstractMQService {
@Override
protected AbstractExchange initExchange(String exchangeName, RabbitMq rabbitMq) {
return new FanoutExchange(exchangeName);
}
@Override
protected Binding initBinding(Queue queue, AbstractExchange exchange, String routingKey,RabbitMq rabbitMq) {
return BindingBuilder.bind(queue).to((FanoutExchange) exchange);
}
}
@Service("headersMQService")
public class HeadersMQService extends AbstractMQService{
@Override
protected AbstractExchange initExchange(String exchange, RabbitMq rabbitMq) {
return new HeadersExchange(exchange);
}
@Override
protected Binding initBinding(Queue queue, AbstractExchange exchange, String routingKey,RabbitMq rabbitMq) {
HeadersExchangeMapConfigurer headersExchangeMapConfigurer = BindingBuilder.bind(queue).to((HeadersExchange)exchange);
String[] headers = rabbitMq.headers();
Map<String,Object> headerMap = new HashMap<>();
if (rabbitMq.matchValue()) {
for (int i = 0; i < headers.length; i+=2) {
String key = headers[i];
Object value = i+1> headers.length ? null : headers[i + 1];
headerMap.put(key, value);
}
}
if(rabbitMq.matchAll()){
return rabbitMq.matchValue() ? headersExchangeMapConfigurer.whereAll(headerMap).match() : headersExchangeMapConfigurer.whereAll(headers).exist();
} else {
return rabbitMq.matchValue() ? headersExchangeMapConfigurer.whereAny(headerMap).match() : headersExchangeMapConfigurer.whereAny(headers).exist();
}
}
}
/**
* RabbitMq主题队列注册
*/
@Service("topicMQService")
public class TopicMQService extends AbstractMQService {
@Override
protected AbstractExchange initExchange(String exchangeName, RabbitMq rabbitMq) {
return new TopicExchange(exchangeName);
}
@Override
protected Binding initBinding(Queue queue, AbstractExchange exchange, String routingKey,RabbitMq rabbitMq) {
return BindingBuilder.bind(queue).to((TopicExchange) exchange).with(routingKey);
}
}
7.Rabbitmq发送工具类
@Configuration
public class RabbitUtils implements SendService<RabbitMqModel> {
private Logger logger = LoggerUtils.logger(RabbitUtils.class);
private final RabbitTemplate rabbitTemplate;
public RabbitUtils(RabbitTemplate rabbitTemplate) {this.rabbitTemplate = rabbitTemplate;}
/**
* 发送消息
*/
@Override
public void send(RabbitMqModel mq) throws Exception {
if (!convertAndSend(mq,0,false)) {
throw new BaseException(BaseExceptionCode.BASE_EXCEPTION_CODE);
}
}
/**
* 发送延缓信息
*/
@Override
public void sendDelay(RabbitMqModel mq, long delay) throws Exception {
if (!convertAndSend(mq,delay,true)) {
throw new BaseException(BaseExceptionCode.BASE_EXCEPTION_CODE);
}
}
/**
* 发送消息,并设置消息过期时间
* @param mq
* @param expiration
* @throws Exception
*/
@Override
public void sendExpiration(RabbitMqModel mq, long expiration) throws Exception {
if (!convertAndSend(mq,expiration,false)) {
throw new BaseException(BaseExceptionCode.BASE_EXCEPTION_CODE);
}
}
/**
* 重置队列并发使用者
*/
public boolean resetQueueConcurrentConsumers(String queueName, int concurrentConsumers) {
Assert.state(concurrentConsumers > 0, "参数 'concurrentConsumers' 必须大于0.");
DirectMessageListenerContainer container = (DirectMessageListenerContainer)findContainerByQueueName(queueName);
if (ValidateUtils.isNotEmpty(container) && container.isActive() && container.isRunning()) {
container.setConsumersPerQueue(concurrentConsumers);
return true;
}
return false;
}
/**
* 重启消息监听者
*/
public boolean restartMessageListener(String queueName) {
if(ValidateUtils.isEmpty(queueName)){
logger.debug("restartMessageListener 重启队列失败,传入队列名为空!");
return false;
}
DirectMessageListenerContainer container = (DirectMessageListenerContainer)findContainerByQueueName(queueName);
if(ValidateUtils.isEmpty(container)){
logger.debug("restartMessageListener 停止队列失败,没有这个监听器");
return false;
}
Assert.state(!container.isRunning(), String.format("消息队列%s对应的监听容器正在运行!", queueName));
container.start();
return true;
}
/**
* 停止消息监听者
*/
public boolean stopMessageListener(String queueName) {
if(ValidateUtils.isEmpty(queueName)){
logger.debug("stopMessageListener 停止队列失败,传入队列名为空!");
return false;
}
DirectMessageListenerContainer container = (DirectMessageListenerContainer)findContainerByQueueName(queueName);
if(ValidateUtils.isEmpty(container)){
logger.debug("stopMessageListener 停止队列失败,没有这个监听器");
return false;
}
Assert.state(container.isRunning(), String.format("消息队列%s对应的监听容器未运行!", queueName));
container.stop();
return true;
}
// private Map<String, AbstractMessageListenerContainer> getQueue2ContainerAllMap() {
// if (!hasInit) {
// synchronized (allQueueContainerMap) {
// if (!hasInit) {
// Collection<MessageListenerContainer> listenerContainers = SpringUtil.getBean(RabbitListenerEndpointRegistry.class).getListenerContainers();
// listenerContainers.forEach(container -> {
// DirectMessageListenerContainer simpleContainer = (DirectMessageListenerContainer) container;
// Arrays.stream(simpleContainer.getQueueNames()).forEach(queueName ->
// allQueueContainerMap.putIfAbsent(StringUtils.trim(queueName), simpleContainer));
// });
// hasInit = true;
// }
// }
// }
// return allQueueContainerMap;
// }
private boolean convertAndSend(BaseMq baseMq, long expiration, boolean isDelayed) {
RabbitMqModel rabbitMQModel = (RabbitMqModel) baseMq;
if (ValidateUtils.isEmpty(rabbitMQModel) || ValidateUtils.isEmpty(rabbitMQModel.getExchange())) {
return false;
}
Map<String,Object> headers = rabbitMQModel.getHeaders();
rabbitTemplate.convertAndSend(rabbitMQModel.getExchange(), rabbitMQModel.getRoutingKey(), rabbitMQModel,msg->{
if(ValidateUtils.equals(0,expiration)){
return msg;
}
if(ValidateUtils.isNotEmpty(headers)){
for(Entry<String,Object> entry : headers.entrySet()){
msg.getMessageProperties().setHeader(entry.getKey(),entry.getValue());
}
}
if(isDelayed){
msg.getMessageProperties().setHeader("x-delay",expiration);
} else {
msg.getMessageProperties().setExpiration(String.valueOf(expiration));
}
return msg;
},new CorrelationData(UUID.randomUUID().toString()));
return true;
}
public Collection<AbstractMessageListenerContainer> getAllQueueContainerList(){
return RabbitMQInitConfig.allQueueContainerMap.values();
}
public List<MessageQueueDatail> statAllMessageQueueDetail() {
List<MessageQueueDatail> queueDetailList = new ArrayList<>();
RabbitMQInitConfig.allQueueContainerMap.entrySet().forEach(entry -> {
String queueName = entry.getKey();
AbstractMessageListenerContainer container = entry.getValue();
MessageQueueDatail deatil = new MessageQueueDatail(queueName, container);
queueDetailList.add(deatil);
});
return queueDetailList;
}
public AbstractMessageListenerContainer findContainerByQueueName(String queueName) {
String key = StringUtils.trim(queueName);
AbstractMessageListenerContainer container = RabbitMQInitConfig.allQueueContainerMap.get(key);
return container;
}
/**
* 手动拉去消息消费,根据队列名找到对应消费器消费
* @param transactional 是否开启事务
* @param queueName 队列名
* @throws Exception
*/
public void handleQueueMessageManually(boolean transactional,String queueName) throws Exception {
Channel channel = rabbitTemplate.getConnectionFactory().createConnection().createChannel(transactional);
GetResponse response = channel.basicGet(queueName, false);
if (ValidateUtils.isEmpty(response)) {
logger.debug("没有从{}队列中获取到消息",queueName);
return;
}
Map<String,Object> annotationMap = SpringUtil.getBeansWithAnnotation(RabbitMq.class);
for(Object obj : annotationMap.values()){
RabbitMq rabbitMq = AnnotationUtils.findAnnotation(obj.getClass(), RabbitMq.class);
List<String> queues = Arrays.asList(rabbitMq.queues());
if(!queues.contains(queueName)){
continue;
}
logger.debug("从{}队列找到消费者类:{}",queueName,obj.getClass().getSimpleName());
AbstractConsumer abstractConsumer = (AbstractConsumer) obj;
abstractConsumer.onMessage(new Message(response.getBody(), new MessageProperties()),channel);
}
}
}
最后用户只需要继承AbstractConsumer实现handleMessage方法即可,并且在继承方法上加上@RabbitMq注解如果需要重试就加上@RabbitMqRetry