基本概念请看另一篇:rabbitMQ入门
一、前期工作
1、引入依赖
<!-- rabbitmq -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2、application.yml文件添加相关配置
spring:
rabbitmq:
host: 192.168.1.60
port: 5672
username: guest
password: guest
3、新增 RabbitMqConfig 配置类,先创建 ConnectionFactory 和 RabbitTemplate 的Bean
@Configuration
public class RabbitMqConfig {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Value("${spring.rabbitmq.host}")
private String host;
@Value("${spring.rabbitmq.port}")
private int port;
@Value("${spring.rabbitmq.username}")
private String username;
@Value("${spring.rabbitmq.password}")
private String password;
@Bean
public ConnectionFactory connectionFactory(){
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(host,port);
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost("/");
connectionFactory.setPublisherConfirms(true);
connectionFactory.setPublisherReturns(true);
return connectionFactory;
}
@Bean
public RabbitTemplate rabbitTemplate(){
RabbitTemplate template = new RabbitTemplate(connectionFactory());
template.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
logger.info("发送成功");
}
});
template.setReturnCallback(new RabbitTemplate.ReturnCallback() {
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
logger.info("发送失败");
}
});
return template;
}
}
二、简单的用户下单模型范例
1、Queue、Exchange、Routing-key等信息配置到 application.yml文件
order:
queue_name: order_queue_name
exchange_name: order_exchange_name
routing_key_name: order_routing_key_name
2、RabbitMqConfig 配置类增加订单消息队列相关配置
@Value("${order.queue_name}")
private String ORDER_QUEUE_NAME;
@Value("${order.exchange_name}")
private String ORDER_EXCHANGE_NAME;
@Value("${order.routing_key_name}")
private String ORDER_ROUTING_KEY_NAME;
@Bean
public DirectExchange orderExchange(){
//创建持久化、非自动删除的交换器
return new DirectExchange(ORDER_EXCHANGE_NAME,true,false);
}
@Bean
public Queue orderQueue(){
//创建持久化的队列
return new Queue(ORDER_QUEUE_NAME,true);
}
@Bean
public Binding orderBinging(){
//通过路由将队列和交换器进行绑定
return BindingBuilder.bind(orderQueue()).to(orderExchange()).with(ORDER_ROUTING_KEY_NAME);
}
3、生产者
@Service
public class OrderServiceImpl implements OrderService{
@Value("${order.queue_name}")
private String ORDER_QUEUE_NAME;
@Value("${order.exchange_name}")
private String ORDER_EXCHANGE_NAME;
@Value("${order.routing_key_name}")
private String ORDER_ROUTING_KEY_NAME;
@Autowired
RabbitTemplate template;
@Override
public Order createOrder() {
/**用户下单逻辑。。。。。。**/
/**用户下单逻辑。。。。。。**/
String content = "用户下单";
CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
template.convertAndSend(ORDER_EXCHANGE_NAME,ORDER_ROUTING_KEY_NAME,content,correlationData);//消息通知
return null;
}
}
4、消费者
@Component
@RabbitListener(queues = "${order.queue_name}")
public class MqMsgListener {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@RabbitHandler
public void orderQueue(String msg){
logger.info("接收到的消息:" + msg);
}
}
5、消费者的写法也可以换另一种
-
在配置中增加并发量属性配置
spring:
rabbitmq:
listener:
simple:
# 消费者初始并发值
concurrency: 5
# 消费者最大并发值
max-concurrency: 10
# 某消费者一次监听可拉取的消息数
prefetch: 3
-
在 RabbitMqConfig 配置类中增加消息监听容器
@Value("${spring.rabbitmq.listener.simple.concurrency}")
private int concurrency;
@Value("${spring.rabbitmq.listener.simple.max-concurrency}")
private int max_concurrency;
@Value("${spring.rabbitmq.listener.simple.prefetch}")
private int prefetch;
@Bean
public SimpleMessageListenerContainer simpleMessageListenerContainer(){
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory());
container.setQueues(orderQueue());//这里的参数可以支持多个队列,比如orderQueue(),productQueue()
container.setExposeListenerChannel(true);
container.setConcurrentConsumers(concurrency);//消费者初始并发值
container.setMaxConcurrentConsumers(max_concurrency);//消费者最大并发值
container.setPrefetchCount(prefetch);//某消费者一次监听可拉取的消息数
container.setAcknowledgeMode(AcknowledgeMode.AUTO);//自动确认消息
container.setMessageListener(new ChannelAwareMessageListener() {
@Override
public void onMessage(Message message, Channel channel) throws Exception {
logger.info("第二种方式接收消息:" + new String(message.getBody()));
}
});
return container;
}
6、消费者的写法还有另一种
-
在配置中增加并发量属性配置同上
spring:
rabbitmq:
listener:
simple:
# 消费者初始并发值
concurrency: 5
# 消费者最大并发值
max-concurrency: 10
# 某消费者一次监听可拉取的消息数
prefetch: 3
-
在 RabbitMqConfig 配置类中增加
@Bean(name = "simpleRabbitListenerContainerFactory")
public SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory(){
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
factory.setConcurrentConsumers(concurrency);
factory.setMaxConcurrentConsumers(max_concurrency);
factory.setPrefetchCount(prefetch);
factory.setAcknowledgeMode(AcknowledgeMode.AUTO);
return factory;
}
-
消费者类
@Component
public class RabbitMqListener {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@RabbitListener(queues = "${order.queue_name}",containerFactory = "simpleRabbitListenerContainerFactory")
public void orderQueue(@Payload byte[] message){
logger.info("第三种方式接收到的消息:" + new String(message));
}
}
三、基于上面我们尝试一下 Fanout 广播模式(请忽视名称定义的不规范)
1、在 RabbitMqConfig 配置类中增加队列配置,这里交换器改用 fanout 模式
@Bean
public FanoutExchange fanoutExchange(){
return new FanoutExchange(ORDER_EXCHANGE_NAME,true,false);
}
@Bean
public Queue orderQueue(){
return new Queue(ORDER_QUEUE_NAME,true);
}
@Bean
public Queue orderQueue2(){
//第二个队列名称自行定义,这里只是做一个示范
return new Queue(ORDER_QUEUE_NAME2,true);
}
@Bean
public Binding orderBinging(){
//将队列和交换器进行绑定,不需要路由,对个队列绑到同一个交换器
return BindingBuilder.bind(orderQueue()).to(fanoutExchange());
}
@Bean
public Binding orderBinging2(){
return BindingBuilder.bind(orderQueue2()).to(fanoutExchange());
}
2、生产者修改如下
@Override
public Order createOrder() {
/**用户下单逻辑。。。。。。**/
/**用户下单逻辑。。。。。。**/
String content = "用户下单";
template.convertAndSend(ORDER_EXCHANGE_NAME,"",content);//消息通知,这里不需要设置路由
return null;
}
3、建立两个消费者查看结果
//第一个消费者
@Component
@RabbitListener(queues = "${order.queue_name}")
public class MqMsgListener {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@RabbitHandler
public void orderQueue(String msg){
logger.info("第一个队列接收到的消息:" + msg);
}
}
//第二个消费者
@Component
@RabbitListener(queues = "${order.queue_name2}")
public class MqMsgListener2 {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@RabbitHandler
public void orderQueue(String msg){
logger.info("第二个队列接收到的消息:" + msg);
}
}