无交换机只有路由(routerKey)
解释下:该消费模型(Hello World模型)只有路由(routerKey)属于点对点消费不属于广播模式,即生产的消息每次只有一个消费者消费,并且是按顺序进行消费
下面开始创建SpringBoot工程引入RabbitMq POM文件
public static void main(String[] args) {
SpringApplication.run(RabbitmqSpringbootApplication.class, args);
}
}
创建RabbitMq的配置文件加入到IOC,此配置文件主要是获得RabbitMq
package org.cly.rabbitmq.config;
import org.cly.rabbitmq.hello.UserConsumer;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitmqConfiguration {
@Value("${spring.rabbitmq.host}")
private String addresses;
@Value("${spring.rabbitmq.port}")
private String port;
@Value("${spring.rabbitmq.username}")
private String username;
@Value("${spring.rabbitmq.password}")
private String password;
@Value("${spring.rabbitmq.virtual-host}")
private String virtualHost;
@Value("${spring.rabbitmq.publisher-confirms}")
private boolean publisherConfirms;
/**
* 连接工厂
* @return
*/
@Bean
public ConnectionFactory connectionFactory(){
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses(addresses);
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost(virtualHost);
//todo 消息发送确认
connectionFactory.setPublisherConfirms(publisherConfirms);
return connectionFactory;
}
@Bean
public RabbitTemplate newRabbitTemplate(){
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory());
//失败通知
rabbitTemplate.setMandatory(true);
//发送方确认
rabbitTemplate.setConfirmCallback(confirmCallback());
//失败回调
rabbitTemplate.setReturnCallback(returnCallback());
return rabbitTemplate;
}
//=====消费者手动应答
@Bean
public SimpleMessageListenerContainer messageContainer() {
SimpleMessageListenerContainer container
= new SimpleMessageListenerContainer(connectionFactory());
container.setQueues(userQueue());
container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
container.setMessageListener(userConsumer);
return container;
}
//=============生产者 发送方确认回调==========
@Bean
public RabbitTemplate.ConfirmCallback confirmCallback(){
return new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if(ack){
System.out.println("发送者确认发送给mq成功");
}else {
//处理失败的消息
System.out.println("发送者发送给mq失败,考虑重发:"+cause);
}
}
};
}
//=============生产者 失败通知回调==========
@Bean
public RabbitTemplate.ReturnCallback returnCallback(){
return new RabbitTemplate.ReturnCallback() {
@Override
public void returnedMessage(Message message,
int replyCode,
String replyText,
String exchange,
String routingKey) {
System.out.println("无法路由的消息,需要考虑另外处理。");
System.out.println("Returned replyText:"+replyText);
System.out.println("Returned exchange:"+exchange);
System.out.println("Returned routingKey:"+routingKey);
String msgJson = new String(message.getBody());
System.out.println("Returned Message:"+msgJson);
}
};
}
}
下面开始创建没有交换机的生产者和消费者,只有队列,先创建自动消费的例子,说明下该模式下的消息默认是轮训消费的,即一个生产者两个消费者,生产者生产一条消息会被其中一个消费者消费,再次生产一条会被另一个消费者消费
1.新建生产者
@RestController
@RequestMapping(“rabbit-mq/test”)
public class RenkeSpringBootRabbitMq {
@Autowired
RabbitTemplate rabbitTemplate;
@GetMapping(value = "routerHello")
public void HelloWorld(){
//第一个参数是key即路由,第二个参数是发送的内容,
//这种属于点对点消费,不是广播模式
//因此当有多个消费者时会有顺序的消费该消息,
// 但每次只能有一个消费者消费
rabbitTemplate.convertAndSend("renke.hello","这是renke.hello");
}
}
2.新建消费者
package org.cly.rabbitmq.controller;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
//Queue该注解是重点可以点进去查看源码:第一个参数:routingKey(路由)
//第二个参数无消费时是否持久化磁盘,也就是当消息还在队列时,如果重启RabbitMqdurable
// 为true是会序列化到磁盘,重启加载到RabbitMq,为false则重启消失
//第三个参数,消费后是否自动删除autoDelete为true
@RabbitListener(queuesToDeclare = @Queue(value = “renke.hello”, durable = “false”, autoDelete = “false”))
public class RouterConsumer {
@RabbitHandler
public void getMessage(String message) {
System.out.println(“RouterConsume开始消费:” + message);
}
}
3.第二个消费者
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@RabbitListener(queuesToDeclare =@Queue(value = “renke.hello”))
public class RouterConsumer2 {
@RabbitHandler
public void getMessage(String message){
System.out.println(“RouterConsume2开始消费:”+message);
}
}
下面开始新建另一种模式即需要手动确认的ACK,消费者接收到消息后不手动确认ACKRabbitMQ的消息会一直存在
代码搞起来之前在说下流程线:
在配置文件注入Queu以及在配置文件将该Queue设置为收到没回确认即可
配置文件指定配置queue的名称
public final static String QUEUE_USER = “sb.user”;
配置文件创建Queue
@Bean
public Queue userQueue() {
return new Queue(QUEUE_USER);
}
将userQueue设置为ACK为手动确认
//=====消费者手动应答
@Bean
public SimpleMessageListenerContainer messageContainer() {
SimpleMessageListenerContainer container
= new SimpleMessageListenerContainer(connectionFactory());
container.setQueues(userQueue());
container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
container.setMessageListener(userConsumer);
return container;
}
生产者
@Component
public class DefaultProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendDefaultMessage(){
String helloMessage = "default hello";
String userMessage = "default user";
//该队列是普通消费者消费(自动确认)
// rabbitTemplate.convertAndSend(RabbitmqConfiguration.QUEUE_HELLO,helloMessage);
//该队列是消费者手动确认
rabbitTemplate.convertAndSend(RabbitmqConfiguration.QUEUE_USER,userMessage);
}
}
消费者
import com.rabbitmq.client.Channel;
import org.cly.rabbitmq.config.RabbitmqConfiguration;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.stereotype.Component;
@Component
@RabbitListener(queues = RabbitmqConfiguration.QUEUE_USER)
public class UserConsumer implements ChannelAwareMessageListener {
/**
* 这些注解没有了,在配置文件里配置
* @param message
*/
// @RabbitHandler
@Override
public void onMessage(Message message, Channel channel) throws Exception {
try {
String msg = new String(message.getBody());
System.out.println("UserReceiver>>>>>>>接收到消息:"+msg);
try {
//手动确认消费
channel.basicAck(message.getMessageProperties().getDeliveryTag(),
true);
System.out.println("UserReceiver>>>>>>消息已应答");
} catch (Exception e) {
System.out.println(e.getMessage());
channel.basicNack(message.getMessageProperties().getDeliveryTag(),
false,true);
System.out.println("UserReceiver>>>>>>拒绝消息,要求Mq重新派发");
throw e;
}
}catch (Exception e){
System.out.println(e.getMessage());
}
}
}