参考地址:https://blog.youkuaiyun.com/qq_35387940/article/details/100514134
生产者中的确认主要是通过两个方法来确认的
ConfirmCallback 与 ReturnsCallback 具体什么场景会触发这两个方法,通过代码来测试下
流程:编写配置类
@SpringBootConfiguration
@Slf4j
public class RabbitConfig {
@Bean
public RabbitTemplate createRabbitTemplate(ConnectionFactory connectionFactory){
RabbitTemplate rabbitTemplate = new RabbitTemplate();
//rabbitmq注入一个连接工厂
rabbitTemplate.setConnectionFactory(connectionFactory);
//设置为强制性的,不关是否发送成功都需要确认
rabbitTemplate.setMandatory(true);
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean b, String s) {
//这里主要是对confirmCallback方法的调用
log.info("confirmCallback: 中的相关数据为:{}", correlationData);
log.info("调用方法之后的确认情况为:{}", b);
log.info("调用方法失败时的原因为:{}", s);
}
});
rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
@Override
public void returnedMessage(ReturnedMessage returnedMessage) {
log.info("returnCallBack中的消息为:{}", returnedMessage);
}
});
return rabbitTemplate;
}
/**
* 这两个方法其实就是对于rabbitmq服务的响应
* 1:消息推送到server 但在server中找不到交换机 这样就只会触发confirm方法
* 2:消息推送到server 但在server中找不到队列
* 3:消息推送到server,但在server中找不到交换机与队列
* 4:消息推送成功
*/
/**
* 测试一个没有队列的交换机
* @return
*/
@Bean
DirectExchange create(){
return new DirectExchange("noqueue");
}
/**
* 直连的队列名称
* @return
*/
@Bean
public Queue testDirectQueue(){
/**
* dureable:是否持久化,默认是false, 持久化队列: 会被存储在磁盘上,当消息代理重启时仍然存在
* exclusive: 默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列会被关闭
* autoDelete:是否自动删除,当没有生产者或者消费者使用队列,该队列会自动删除
* return new Queue("testDirectQueue", true, true, fasle,)
*/
return new Queue("TestDirectQueue", true);
}
/**
* 直连交换机
* @return
*/
@Bean
DirectExchange testDirectExchange(){
return new DirectExchange("testDirectExchange", true, false);
}
/**
* 指定直连的队列与交换机,并且指定routyKey为:testDirectRouting
* @return
*/
@Bean
Binding bindingDirect(){
return BindingBuilder.bind(testDirectQueue()).to(testDirectExchange()).with("testDirectRouting");
}
访问controller层代码编写来测试:
@RestController
@Api
public class RabbitAckControlelr {
@Autowired
RabbitTemplate rabbitTemplate;
/**
* 凭空造一个交换机,但是路由键是可以存在的是就
* 只会触发一个ConfirmCallBack这个方法
* @return
*/
@RequestMapping(value = "noexchange", method = RequestMethod.GET)
public String noexchange(){
//现在发送一个map的消息过去
String messageId = UUID.randomUUID().toString();
String messageData = "test topic message2, hello";
String format = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
Map<String, Object> map = new HashMap<>();
map.put("messageId", messageId);
map.put("messageData", messageData);
map.put("createTime", format);
rabbitTemplate.convertAndSend("noExchange", "topic.women", map);
return "ok";
}
/**
* 这是对有交换机但是没有绑定的队列的测试
* 测试结果显示它都会触发一个ConfirmCallBack与ReturnCallBack方法
* 注意也只有在这种情况下,才会返回全部的数据,其余的情况都是不会返回数据的
* @return
*/
@RequestMapping(value = "noQueue", method = RequestMethod.GET)
public String noQueue(){
//现在发送一个map的消息过去
String messageId = UUID.randomUUID().toString();
String messageData = "test topic message2, hello";
String format = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
Map<String, Object> map = new HashMap<>();
map.put("messageId", messageId);
map.put("messageData", messageData);
map.put("createTime", format);
rabbitTemplate.convertAndSend("noqueue", "noqueue", map);
return "ok";
}
/**
* 这是测试往交换机与队列中发送消息时,没有交换机与队列绑定的情况下
* 测试结果显示,这种情况也只是会调用confirmCallBack方法
* @return
*/
@RequestMapping(value = "noThing", method = RequestMethod.GET)
public String noThing(){
//现在发送一个map的消息过去
String messageId = UUID.randomUUID().toString();
String messageData = "test topic message2, hello";
String format = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
Map<String, Object> map = new HashMap<>();
map.put("messageId", messageId);
map.put("messageData", messageData);
map.put("createTime", format);
rabbitTemplate.convertAndSend("no", "noqueue", map);
return "ok";
}
@RequestMapping(value = "success", method = RequestMethod.GET)
public String success(){
//现在发送一个map的消息过去
String messageId = UUID.randomUUID().toString();
String messageData = "test topic message2, hello";
String format = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
Map<String, Object> map = new HashMap<>();
map.put("messageId", messageId);
map.put("messageData", messageData);
map.put("createTime", format);
rabbitTemplate.convertAndSend("topicExchange", "topic.women", map);
return "success";
}
下面就是消费者消息确认的代码展示,rabbitmq中的消息就是自动确认的,也就是说当消息调用之后,不管消费成功都会默认已经成功消费,将消息直接丢弃了
下面是手动确认的代码展示:
@SpringBootConfiguration
public class MessageListenerConfig {
@Autowired
private CachingConnectionFactory cachingConnectionFactory;
//消息处理类
@Autowired
private MessageListener messageListener;
/**
* 这是设置消息监听类
* @return
*/
@Bean
public SimpleMessageListenerContainer simpleMessageListenerContainer(){
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(cachingConnectionFactory);
container.setConcurrentConsumers(1); //设置当前消费者数量
container.setMaxConcurrentConsumers(1);
container.setAcknowledgeMode(AcknowledgeMode.MANUAL); //设置当前的消费者确认为手动的,rabbit默认的是自动的
container.setQueueNames("topic.women"); //将队列注入到listenerContainer中去
container.setMessageListener(messageListener); //将消息监听类注入到容器中去
return container;
}
消费者中的消息处理类的代码展示如下:
@Component
@Slf4j
public class MessageListener implements ChannelAwareMessageListener {
@Override
public void onMessage(Message message, Channel channel) throws Exception {
//从消息中获取到分发标记数
long deliverTag = message.getMessageProperties().getDeliveryTag();
try{
String msg = message.toString();
String[] msgArray = msg.split("'");
Map<String, String> stringStringMap = mapStringToMap(msgArray[1].trim(), 3);
String messageId=stringStringMap.get("messageId");
String messageData=stringStringMap.get("messageData");
String createTime=stringStringMap.get("createTime");
log.info("接收到的消息为:{}, 数据为:{}, 数据创建时间为:{}", messageId, messageData, createTime);
/**
* channel.basicAck用于肯定消费
* basic.nack用于否定确认(注意:这是AMQP 0-9-1的RabbitMQ扩展)
* basic.reject用于否定确认,但与basic.nack相比有一个限制:一次只能拒绝单条消息
* 记住basic.nack是允许消费多条消息的,具体的就是它消费的是大于当前信道中的tagid,小于的都不消费
*/
channel.basicAck(deliverTag, true);
}catch (Exception e){
/**
* 在消费时候出现异常时,常做出的处理就是
* 消费端知道这个消息是错误的,直接丢弃
* 不会重新将它发送到队列中去
*/
channel.basicReject(deliverTag, false);
e.printStackTrace();
}
}
/**
* 将一个Map String类型转换成Map
* @param str
* @param entryNum
* @return
*/
private Map<String, String> mapStringToMap(String str, int entryNum){
String substring = str.substring(1, str.length() - 1);
String[] split = substring.split(",", entryNum);
Map<String, String> map = new HashMap<>();
for(String s : split){
String trim = s.split("=")[0].trim();
String value = s.split("=")[1];
map.put(trim, value);
}
return map;
}
}