com.lx.boot.mq.RabbitUtil Maven / Gradle / Ivy
package com.lx.boot.mq;
import com.lx.boot.OS;
import com.lx.boot.email.EmailUtil;
import com.lx.constant.DefaultBaseConstant;
import com.lx.util.LX;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import static com.lx.constant.DefaultBaseConstant.FIRM_ID;
/**
* 默认开启mq mq.enable
* 关闭可以使用
* mq.enable=false
*
**/
@Slf4j
@Configuration
@ConditionalOnProperty(name = "mq.enable", havingValue = "true", matchIfMissing = false)
@ConditionalOnClass(RabbitTemplate.class)
public class RabbitUtil {
@Autowired
private RabbitTemplate rabbitTemplate;
/** 说明:发送延迟消息
* @author ylx 2023/6/19 16:22 */
public void sendDelayMessage(String className, String methodName, Object msg) {
MqInfo mq = new MqInfo("任务" + LX.md5(className + methodName).substring(0, 10), 0, null, OS.getApplicationName(), className, methodName);
mq.setMsg(msg);
sendDelayMessage(mq);
}
/** 说明:发送延迟消息
* @author ylx 2023/6/19 16:22 */
public void sendDelayMessage(String className, String methodName, Object msg,long delay,long [] retryList) {
MqInfo mq = new MqInfo("任务" + LX.md5(className + methodName).substring(0, 10), delay, retryList, OS.getApplicationName(), className, methodName);
mq.setMsg(msg);
sendDelayMessage(mq);
}
//说明: 发送延迟消息
/**{ ylx } 2021/6/2 15:33 */
public void sendDelayMessage(MqInfo mq) {
log.info("消息:"+mq);
if (mq.getDelay()>0){
this.rabbitTemplate.convertAndSend("delay_exchange", mq.getServiceName()+"_delay_key", mq,
message -> {
//注意这里时间可以使long,而且是设置header
message.getMessageProperties().setHeader("x-delay", mq.getActualDelay() * 1000L);
return message;
});
}else{
this.rabbitTemplate.convertAndSend("delay_exchange", mq.getServiceName()+"_delay_key", mq);
}
}
/**
* 默认情况下,如果没有配置手动ACK, 那么Spring Data AMQP 会在消息消费完毕后自动帮我们去ACK
* 存在问题:如果报错了,消息不会丢失,但是会无限循环消费,一直报错,如果开启了错误日志很容易就吧磁盘空间耗完
* 解决方案:手动ACK,或者try-catch 然后在 catch 里面将错误的消息转移到其它的系列中去
* spring.rabbitmq.listener.simple.acknowledge-mode = manual
* @param list 监听的内容
*/
@RabbitListener(queues = "${spring.application.name}_delay_queue")
public void cfgUserReceiveDealy(MqInfo mqInfo, Message message, Channel channel) throws IOException {
OS.setLogTraceId(mqInfo.getRequestId());
log.info("===============接收队列接收消息====================");
log.info("接收时间:{},接受内容:{}", LocalDateTime.now(), mqInfo.toString());
try {
OS.put(FIRM_ID,mqInfo.getFirmId());
boolean success = true;
Object bean = OS.getBean(mqInfo.getClassName());
LX.exObj(bean, "调用对象不存在!" + mqInfo.getClassName());
Method method;
if (mqInfo.getMsg() == null) {
method = bean.getClass().getDeclaredMethod(mqInfo.getMethodName());
} else {
method = bean.getClass().getDeclaredMethod(mqInfo.getMethodName(), mqInfo.getMsg().getClass());
}
LX.exObj(method, "调用对象方法不存在!" + mqInfo.getClassName() + "." + mqInfo.getMethodName());
// 调用方法 如果有问题报错即可 会进行重新尝试
method.invoke(bean, mqInfo.getMsg());
//通知 MQ 消息已被接收,可以ACK(从队列中删除)了
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
} catch (Exception e) {
log.error("============消费失败,尝试消息补发再次消费!==============",e);
retry(mqInfo, message, channel);
}
}
//说明:消费失败
/**{ ylx } 2022/3/8 18:02 */
private void retry(MqInfo mqInfo, Message message, Channel channel){
log.error("============消费失败,尝试消息补发再次消费!==============");
try {
if (mqInfo.addCount()){
sendDelayMessage(mqInfo);
}else{
EmailUtil.sendEmail(LX.LOCALHOST_IP+" MQ消息推送失败!"+mqInfo.getNameOfFunction() ,OS.getProperty(DefaultBaseConstant.ES_HOSTS)+" : " +OS.getLogTraceId(), OS.getProperty(DefaultBaseConstant.MQ_EMAIL_USERS_LIST,"").split(","));
this.rabbitTemplate.convertAndSend("delay_exchange", "deadletterQueue", mqInfo);
log.error("重试多次失败!"+mqInfo);
}
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}catch (Exception e1){
log.error("重试发生失败!"+mqInfo);
}
}
/**
* 延时队列交换机
* 注意这里的交换机类型:CustomExchange
* @return
*/
@Bean
public CustomExchange delayExchange(){
Map args = new HashMap<>();
args.put("x-delayed-type", "direct");
return new CustomExchange("delay_exchange","x-delayed-message",true, false,args);
}
/**
* 延时队列
* @return
*/
@Bean
public Queue delayQueue(){
return new Queue(OS.getApplicationName()+"_delay_queue",true);
}
/**
* 给延时队列绑定交换机
* @return
*/
@Bean
public Binding cfgDelayBinding(Queue delayQueue, CustomExchange delayExchange){
return BindingBuilder.bind(delayQueue).to(delayExchange).with(OS.getApplicationName()+"_delay_key").noargs();
}
/** 死信队列*/
@Bean
public Queue deadletterQueue(){
return new Queue("deadletterQueue",true);
}
/**
* 给死信队列绑定交换机
* @return
*/
@Bean
public Binding deadletterQueueBinding(Queue deadletterQueue, CustomExchange delayExchange){
return BindingBuilder.bind(deadletterQueue).to(delayExchange).with("deadletterQueue").noargs();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy