前言:
MQ 是什么?队列是什么,MQ 我们可以理解为消息队列,队列我们可以理解为管道。以管道的方式做消息传递。
场景:
1.优化用户体验,当我们在网上购物结算时,就会发现,界面会提醒我们,让我们稍等,以及一些友好的图片文字提醒
2.在我们平时的超市中购物也,遇到大批人一窝蜂一样涌入收银台,怎么办?所以超时,会设置一个走廊,让人们排队一个一个慢慢结算。
在网络购物中也会遇到同样的情况,MQ就会帮我们解决这个问题。
RabbitMQ模式
1.简单模式:非常简单的模式,一发一收
2.工作模式 :与简单模式一样只是有多个消费者,采用轮询模式,但是资源分配不公平
3.公平模式:资源分配公平的工作模式
4.订阅模式:消息产生者将消息通过交换机放入队列,对应消息队列的消费者拿到消息进行消费
5.路由模式:与订阅模式一样,只是绑定队列时,加入了路由键
6.主题模式:与路由模式一样,只是路由键可以进行模糊匹配。像SQL语句中的 like% 和like%
SpringBoot整合RabbitMQ
- 直接进入正题,这里先写平时用的最多的路由键模式,其他模式以后补上来
POM依赖
<!--rabbitmq-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<version>1.5.2.RELEASE</version>
</dependency>
</dependencies>
application配置
#rabbitmq配置
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=miro
spring.rabbitmq.password=xxxxx
spring.rabbitmq.virtual-host=mirovh
绑定交换机设置config类
@Configuration
public class RouteConfig {
//声明交换机
@Bean
public DirectExchange directExchange(){
return new DirectExchange("direct-ex"); //交换机的名字direct-ex
}
//创建队列 2个队列就要2个消费者
@Bean
public Queue directQueue1(){
return new Queue("direct-queue1"); //队列1的名字direct-queue1
}
@Bean
public Queue directQueue2(){
return new Queue("direct-queue2"); //队列2的名字direct-queue2
}
//绑定队列到交换机的路由键上面
@Bean
public Binding bindingDirectExchange1(){
return BindingBuilder.bind(directQueue1()).to(directExchange()).with("dq1");//路由键名字dq1
}
@Bean
public Binding bindingDirectExchange2(){
return BindingBuilder.bind(directQueue2()).to(directExchange()).with("dq2");//路由键名字dq2
}
}
Controller层
@RestController
public class rabbitController {
@Resource
private AmqpTemplate amqpTemplate; //使用AmqpTemplate ,这提供了接收/发送等等方法
@RequestMapping("direct1")
public String direct1(String data){
amqpTemplate.convertAndSend("direct-ex","dq1",data); //三个参数为 交换机的名字direct-ex,路由键的名字dq1,数据
return "success";
}
@RequestMapping("direct2")
public String direct2(String data){
amqpTemplate.convertAndSend("direct-ex","dq2",data);
return "success";
}
}
监听队列
@Configuration
public class RouteConsumer {
@Resource
private ConnectionFactory connectionFactory;
//手动定义消费者进行消息确认
@Bean
public SimpleMessageListenerContainer DirectContainer1(){
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
//当前监听器容器监听的队列
//放入的是上面的工作队列
container.setQueueNames("direct-queue1"); //这里写入要接受消息的队列
//监听通道
container.setExposeListenerChannel(true);
//设置最大并发消费者数量--最终上线
container.setMaxConcurrentConsumers(10);
//设置当前并发消费者数量--代码可以动态调整数量
container.setConcurrentConsumers(1);
//修改为手动确认模式
container.setAcknowledgeMode(AcknowledgeMode.MANUAL);//消息确认后才能删除
//一个消费者每次处理一条数据
container.setPrefetchCount(1);
//定义一个监听
container.setMessageListener(new ChannelAwareMessageListener() {
//从队列中读取消息的方法
@Override
public void onMessage(Message message, Channel channel) throws Exception {
byte[] bytes = message.getBody();
String data = new String(bytes);
System.out.println("路由键模式消费者1处理消息"+data);
//这里进行数据库的操作
//确认消息已被处理
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
}
});
return container;
}
@Bean
public SimpleMessageListenerContainer DirectContainer2(){
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
//当前监听器容器监听的队列
//放入的是上面的工作队列
container.setQueueNames("direct-queue2");
//监听通道
container.setExposeListenerChannel(true);
//设置最大并发消费者数量--最终上线
container.setMaxConcurrentConsumers(10);
//设置当前并发消费者数量--代码可以动态调整数量
container.setConcurrentConsumers(1);
//修改为手动确认模式
container.setAcknowledgeMode(AcknowledgeMode.MANUAL);//消息确认后才能删除
//一个消费者每次处理一条数据
container.setPrefetchCount(1);
//定义一个监听
container.setMessageListener(new ChannelAwareMessageListener() {
//从队列中读取消息的方法
@Override
public void onMessage(Message message, Channel channel) throws Exception {
byte[] bytes = message.getBody();
String data = new String(bytes);
System.out.println("路由键模式消费者2处理消息"+data);
//这里进行数据库的操作
//确认消息已被处理
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
}
});
return container;
}
}
测试
死信队列机制实现延时任务
解决问题
@Configuration
public class DelayConfig {
//配置死信队列
@Bean
public Queue delayQueue(){
Map<String,Object>params = new HashMap<>();
//定义死信的时间,单位毫秒
params.put("x-message-ttl",5 * 1000);
//x-dead-letter-exchange 是属性 后面跟的值是死信要发送到的交换机名字
params.put("x-dead-letter-exchange","delay-ex");
//-dead-letter-routing-key 是属性 后面跟的值是这些死信在转发时携带的路由键名称
params.put("x-dead-letter-routing-key","order");
return new Queue("delay-queue",true,false,false,params);
}
//交换机
@Bean
public DirectExchange delayExchange(){
return new DirectExchange("delay-ex");
}
//创建保存死信信息的队列
@Bean
public Queue orderQueue1(){
return new Queue("order-queue");
}
//将order队列绑定到delay-ex 交换机的order路由键下面
@Bean
public Binding bindingToDelayExchange1(){
return BindingBuilder.bind(orderQueue1()).to(delayExchange()).with("order");//路由键名字dq1
}
@Resource
private ConnectionFactory connectionFactory;
//手动定义消费者进行消息确认
@Bean
public SimpleMessageListenerContainer oderContainer1(){
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
//当前监听器容器监听的队列
//放入的是上面的工作队列
container.setQueues(orderQueue1());
//监听通道
container.setExposeListenerChannel(true);
//设置最大并发消费者数量--最终上线
container.setMaxConcurrentConsumers(10);
//设置当前并发消费者数量--代码可以动态调整数量
container.setConcurrentConsumers(1);
//修改为手动确认模式
container.setAcknowledgeMode(AcknowledgeMode.MANUAL);//消息确认后才能删除
//一个消费者每次处理一条数据
container.setPrefetchCount(1);
//定义一个监听
container.setMessageListener(new ChannelAwareMessageListener() {
//从队列中读取消息的方法
@Override
public void onMessage(Message message, Channel channel) throws Exception {
byte[] bytes = message.getBody();
String data = new String(bytes);
System.out.println("延迟模式消费者处理消息"+data);
//确认消息已被处理
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
}
});
return container;
}
}
Controller层
@RequestMapping("delay")
public String delay(String data){
amqpTemplate.convertAndSend("delay-queue",data);
return "success";
}