MQ消息的可靠投递
在使用 RabbitMQ 的时候,作为消息发送方希望杜绝任何消息丢失或者投递失败场景。RabbitMQ 为我们提供了两种方式用来控制消息的投递可靠性模式。官网描述几种模式地址:https://www.rabbitmq.com/getstarted.html
confirm 确认模式
return 退回模式
1、rabbitmq 整个消息投递的路径为:
producer—>rabbitmq broker—>exchange—>queue—>consumer
2、消息从 producer 到 exchange 则会返回一个 confirmCallback 。
3、消息从 exchange–>queue 投递失败则会返回一个 returnCallback 。
生产者的实现
public class Producer{
@Autowired
RabbitTemplate rabbitTemplate;
@Autowired
ConfirmCallback confirmCallback;
@Autowired
ReturnCallback returnCallback;
@Autowired
private CommonFeignClient commonFeignClient;
/**
* 推送MQ消息(第一种)不需要创建队列的MQ,可以队列创建在bean工厂里
*/
public void sendMail(String ex, String message) {
rabbitTemplate.setMandatory(true);
rabbitTemplate.setConfirmCallback(confirmCallback);
rabbitTemplate.setReturnCallback(returnCallback);
//第一个是参数交换机(exchange)的名字,第二个是路由键(routing-key)**的名字,第三个则为消息的内容
rabbitTemplate.convertAndSend(ex, SysCode.BUSINESS.GROUP_KEY, message);
}
/**
* 推送MQ消息(第二种)
*
* @param message
* @return
*/
public String sendMessage(MessageData message) {
Connection connection = null;
//创建信道
Channel channel = null;
try {
/**
* Mandatory为true时,消息通过交换器无法匹配到队列会返回给生产者
* 为false时,匹配不到会直接被丢弃
*/
rabbitTemplate.setMandatory(true);
/**
* 消费者确认收到消息后,手动ack回执回调处理
*/
rabbitTemplate.setConfirmCallback(confirmCallback);
/**
* 消息投递到队列失败回调处理
*/
rabbitTemplate.setReturnCallback(returnCallback);
ConnectionFactory connectionFactory = rabbitTemplate.getConnectionFactory();
//日志对象
CommonInterfaceLog commonInterfaceLog = new CommonInterfaceLog();
//创建连接
connection = connectionFactory.createConnection();
// 建立信道 构造参数 true代表该信道开启 Transactional 事务模式
// 此处传入true,就不需要再显示编码 channel.txSelect()了
// 内部已经调用了channle.txSelect();
//创建信道
channel = connection.createChannel(false);
//在信道中设置交换器
channel.exchangeDeclare(message.getEx(), ExchangeTypes.TOPIC, true);
commonInterfaceLog.setCreateTime(new Date());
commonInterfaceLog.setMqKey(message.getKey());
commonInterfaceLog.setMqExchange(message .getEx());
commonInterfaceLog.setMqQueue(message.getQueue());
commonInterfaceLog.setBusinessCode(message.getBusinessNumber());
commonInterfaceLog.setSource(message.getSource());
commonInterfaceLog.setMqReqType(ExchangeTypes.TOPIC);
commonInterfaceLog.setRequestMessage(JSONObject.toJSONString(message.getMessageJsonStr()));
commonInterfaceLog.setInterfaceType("MQ");
//进行日志的写入
commonFeignClient.commonInterfaceLogService(commonInterfaceLog);
//发送消息
log.info("------------pushMessageInfo[" + JSONObject.toJSONString(message.getMessageJsonStr()).getBytes().toString() + "]");
channel.basicPublish(message.getEx(), message.getKey(),null, JSONObject.toJSONString(message.getMessageJsonStr()).getBytes());
channel.close();
connection.close();
if(channel!=null||connection!=null){
channel.clearConfirmListeners();
channel.clearReturnListeners();
connection.close();
}
return "------------pushMessageInfo[" + JSONObject.toJSONString(message.getMessageJsonStr()).getBytes().toString() + "]";
} catch (Exception e) {
log.info("MQ消息队列异常:" + message.getQueue() + ":" + message.getKey() + ":" + e.getMessage());
e.printStackTrace();
throw new RuntimeException("MQ消息队列异常:" + message.getQueue() + ":" + message.getKey() + ":" + e.getMessage());
}
}
}
ConfirmCallback 类的实现
//ConfirmCallback确认模式
@Slf4j
@Component
public class ConfirmCallback implements RabbitTemplate.ConfirmCallback {
@Autowired
RabbitTemplate rabbitTemplate;
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if (ack && null != correlationData) {
try{
Message returnedMessage = correlationData.getReturnedMessage();
// returnedMessage.getMessageProperties().getMessageId();
log.info("消息发送成功:correlationData({}),ack({}),cause({})", new String(returnedMessage.getBody()), ack, cause);
} catch (Exception e) {
e.printStackTrace();
}
} else {
String uuid = rabbitTemplate.getUUID();
log.info("消息发送失败:correlationData({}),ack({}),cause({}),uuid({})",correlationData.getReturnedMessage().getBody(),ack, cause,uuid);
}
}
}
ReturnCallback 类的实现
//ReturnCallback 退回模式
@Slf4j
@Component
public class ReturnCallback implements RabbitTemplate.ReturnCallback {
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
log.info("消息退回 ===> replyCode={} ,replyText={} ,exchange={} ,routingKey={}", replyCode, replyText, exchange, routingKey);
}
}
CommonfaceLog 日志表,根据自己的需求进行编写
@Data
@TableName("common_log")
public class CommonfaceLog implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId(value = "id", type = IdType.INPUT)
private Integer id;
/**
* 业务编码
*/
@TableField(value = "business_code")
private String businessCode;
/**
* 接口名称
*/
@TableField(value = "interface_name")
private String interfaceName;
/**
* 接口类型
*/
@TableField(value = "interface_type")
private String interfaceType;
/**
* mq交换机
*/
@TableField(value = "mq_exchange")
private String mqExchange;
/**
* mq队列
*/
@TableField(value = "mq_queue")
private String mqQueue;
/**
* mq方式
*/
@TableField(value = "mq_req_type")
private String mqReqType;
/**
* topic_KEY
*/
@TableField(value = "mq_key")
private String mqKey;
/**
* 接口地址
*/
@TableField(value = "interface_address")
private String interfaceAddress;
/**
* 上次调用时间
*/
@TableField(value = "last_call_time")
private Date lastCallTime;
/**
* 请求报文
*/
@TableField(value = "request_message")
private String requestMessage;
/**
* 响应报文
*/
@TableField(value = "response_message")
private String responseMessage;
/**
* 失败次数
*/
@TableField(value = "failures_number")
private Integer failuresNumber;
/**
* 本次失败原因
*/
@TableField(value = "this_failure_reasons")
private String thisFailureReasons;
/**
* 创建时间
*/
@TableField(value = "create_time")
private Date createTime;
/**
* 请求状态
*/
@TableField(value = "request_status")
private Integer requestStatus;
/**
* 业务条线
*/
@TableField(value = "source")
private String source;
}
消费者的实现
@Log4j2
@Component("CarPolicysDirectListener")
public class CarPolicysDirectListener {
@Autowired
CoreSalesPolicysService coreSalesPolicysService;
@Autowired
CommonFeignClient commonFeignClient;
@RabbitHandler
@Qualifier("carPolicyListenerContainer") //指定消费的bean来控制消费者
@RabbitListener( containerFactory = "carPolicysFactory", //bean名称,根据业务起
bindings = {@QueueBinding(
value = @Queue(name = SysCode.CAR_POLICY.QUEUES, durable = "true"),//队列
exchange = @Exchange(name = SysCode.CAR_POLICY.EX,//交换机
type = ExchangeTypes.DIRECT,//模式
ignoreDeclarationExceptions = "true"),
key = SysCode.CAR_POLICY.KEY,//路由规则key
ignoreDeclarationExceptions = "true")})
public void getCarPolicysTopic(@Payload String message, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag, Channel channel){
CommonInterfaceLog commonInterfaceLog =new CommonInterfaceLog();
int length=255;
try {
//那个消息开始消费
log.info("["+SysCode.SOURCE.CAR+"]getCarPolicysTopic监听到消息:"+message);
//判断是否为空
if(StringUtils.isNotBlank(message)){
//把消息json串转换成你所需要的对象
CoreSalesPolicysModel coreSalesPolicysModel = JSONObject.parseObject(message, CoreSalesPolicysModel.class);
//执行相关的业务
if (SysCode.CERTITYPE.POLICY.equals(coreSalesPolicysModel.getCertiType())){
commonInterfaceLog.setBusinessCode(coreSalesPolicysModel.getPolicyNo());
}else if (SysCode.CERTITYPE.ENDORSE.equals(coreSalesPolicysModel.getCertiType())){
commonInterfaceLog.setBusinessCode(coreSalesPolicysModel.getCertiNo());
}
R r=coreSalesPolicysService.savePolicys(coreSalesPolicysModel);
//异常处理
if(500==r.getCode()){
log.info(""+r.getMsg());
commonInterfaceLog.setThisFailureReasons(r.getMsg());
//出现异常,告诉mq抛弃该消息
channel.basicNack(deliveryTag,false,false);
}else {
//消费消息
channel.basicAck(deliveryTag, false);
}
commonInterfaceLog.setResponseMessage(r.toString());
log.info("["+SysCode.SOURCE.CAR+"]getCarPolicysTopic执行完毕**************");
}else {
channel.basicReject(deliveryTag,false);
//出现异常,告诉mq抛弃该消息
channel.basicNack(deliveryTag,false,false);
}
} catch (Exception e) {
commonInterfaceLog.setThisFailureReasons(null != e.getMessage() ? e.getMessage().substring(0, e.getMessage().length() > length ? length : e.getMessage().length()) : null);
try {
//出现异常,告诉mq抛弃该消息
channel.basicNack(deliveryTag,false,false);
e.printStackTrace();
}catch (IOException e1) {
e1.printStackTrace();
}
}finally {
//进行日志表的写入,记录MQ日志,建议存储到非关系数据库
commonInterfaceLog.setInterfaceName("savePolicys");
commonInterfaceLog.setInterfaceType("MQ");
commonInterfaceLog.setMqExchange(SysCode.CAR_POLICY.EX);
commonInterfaceLog.setMqQueue(SysCode.CAR_POLICY.QUEUES);
commonInterfaceLog.setMqKey(SysCode.CAR_POLICY.KEY);
commonInterfaceLog.setMqReqType(ExchangeTypes.DIRECT);
commonInterfaceLog.setRequestMessage(message);
commonInterfaceLog.setCreateTime(new Date());
commonInterfaceLog.setSource(SysCode.SOURCE.CAR);
commonFeignClient.saveInterfaceLog(commonInterfaceLog);
}
}
}
1、创建bean来控制MQ的消费者
@Bean(name = "carPolicyListenerContainer")
public SimpleRabbitListenerContainerFactory baseMultiListenerContainer(@Qualifier("carPolicyConnectionFactory") ConnectionFactory secondConnectionFactory){
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(secondConnectionFactory);
//手动确认消息,默认自动确认消息
factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
//初始化消费者数量
factory.setConcurrentConsumers(5);
//最大消费者数量
factory.setMaxConcurrentConsumers(5);
//一次处理的消息数量
factory.setPrefetchCount(2);
factory.setDefaultRequeueRejected(true);
return factory;
}
2、定义MQ的连接
其中Integer.parseInt(SysCode.RABBITMQPORT)是MQ的端口号,可以写活
@Bean(name = "carPolicyConnectionFactory")
public ConnectionFactory groupPolicyConnectionFactory(
@Value("${spring.rabbitmq.group.addresses}") String addresses,//地址
@Value("${spring.rabbitmq.group.virtualhost}") String virtualHost,//端口和指定位置组合,例如 /car,5672/car等
@Value("${spring.rabbitmq.group.username}") String username,//用户名
@Value("${spring.rabbitmq.group.password}") String password//密码
) {
return connectionFactory1(addresses, Integer.parseInt(SysCode.RABBITMQPORT), username, password,virtualHost);
}
public CachingConnectionFactory connectionFactory1(String host, int port, String username, String password,String virtualHost) {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses(host);
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost(virtualHost);
log.info("MQ配置:{}",connectionFactory);
return connectionFactory;
}
版权声明:转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Boy_Martin/article/details/126104042