RabbitMQ消息队列
消息队列MQ
- MQ全称为Message Queue,即消息队列。
- 消息队列的应用场景:
- 任务异步处理。将不需要同步处理的并且耗时长的操作由消息队列通知消息接收方进行异步处理。提高了应用程序的响应时间。
- 应用程序解耦合。MQ相当于一个中介,生产方通过MQ与消费方交互,它将应用程序进行解耦合。
- 消息队列种类:ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ、Redis。
RabbitMQ
介绍
- RabbitMQ是由erlang语言开发,基于AMQP(Advanced Message Queue 高级消息队列协议)协议实现的消息队列,它是一种应用程序之间的通信方法,消息队列在分布式系统开发中应用非常广泛。
- AMQP是一套公开的消息队列协议,最早在2003年被提出,它旨在从协议层定义消息通信数据的标准格式,解决MQ市场上协议不统一的问题。RabbitMQ就是遵循AMQP标准协议开发的MQ服务。
- JMS是java提供的一套消息服务API标准,其目的是为所有的java应用程序提供统一的消息通信的标准,类似java的jdbc,只要遵循jms标准的应用程序之间都可以进行消息通信。它和AMQP有什么 不同,
jms是java语言专属的消息服务标准,它是在api层定义标准,并且只能用于java应用
;而AMQP是在协议层定义的标准,是跨语言的
。
优点
- 使得简单,功能强大。
- 基于AMQP协议。
- 社区活跃,文档完善。
- 高并发性能好,这主要得益于Erlang语言。
- Spring Boot默认已集成RabbitMQ。
RabbitMQ工作原理
-
基本结构
- Producer:消息生产者,即生产方客户端,生产方客户端将消息发送到MQ。
- Broker:消息队列服务进程,此进程包括两个部分:Exchange和Queue。
- Exchange:消息队列交换机,按一定的规则将消息路由转发到某个队列,对消息进行过滤。
- Queue:消息队列,存储消息的队列,消息到达队列并转发给指定的消费方。
- Consumer:消息消费者,即消费方客户端,接收MQ转发的消息。
-
消息发布接收流程
- 1、生产者和Broker建立TCP连接。
- 2、生产者和Broker建立通道。
- 3、生产者通过通道消息发送给Broker,由Exchange将消息进行转发。
- 4、Exchange将消息转发到指定的Queue(队列)
-
接收消息
- 1、消费者和Broker建立TCP连接。
- 2、消费者和Broker建立通道。
- 3、消费者监听指定的Queue(队列)。
- 4、当有消息到达Queue时Broker默认将消息推送给消费者。
- 5、消费者接收到消息。
-
工作模式
-
Work queues(工作模式)
- work queues两个消费端共同消费同一个队列中的消息。
- 应用场景:对于任务过重或任务较多情况使用工作队列可以提高任务处理的速度。
-
Publish/Subscribe(发布订阅模式)
- 每个消费者监听自己的队列。
- 生产者将消息发给broker,由交换机将消息转发到绑定此交换机的每个队列,每个绑定交换机的队列都将接收到消息。
- 应用场景:短信或邮件通知等。
- 代码:
- 生产者:声明Exchange_fanout_inform交换机、声明两个队列并且绑定到交换机,绑定时不需要指定routingkey、发送消息时不需要指定routingkey。
- 发布订阅publish/subscribe和工作模式work queues的区别
- work queues不用定义交换机,而publish/subscribe需要定义交换机。
- publish/subscribe的生产方是面向交换机发送消息,work queues的生产方是面向队列发送消息(底层使用默认交换机)
- publish/subscribe需要设置队列和交换机的绑定,work queues不需要设置,实质上work queues会将队列绑定到默认的交换机 。
-
Routing(路由模式)
- 每个消费者监听自己的队列,并且设置routingkey。
- 生产者将消息发给交换机,由交换机根据routingkey来转发消息到指定的队列。
- 代码
- 声明exchange_routing_inform交换机。
- 声明两个队列并且绑定到此交换机,绑定时需要指定routingkey
- 发送消息时需要指定routingkey。
- 区别
- Routing模式要求队列在绑定交换机时要指定routingkey,消息会转发到符合routingkey的队列。
-
Topics
- 每个消费者监听自己的队列,并且设置带统配符的routingkey。
- 生产者将消息发给broker,由交换机根据routingkey来转发消息到指定的队列。
- 案例:根据用户的通知设置去通知用户,设置接收Email的用户只接收Email,设置接收sms的用户只接收sms,设置两种通知类型都接收的则两种通知都有效。
- 代码
- 生产者:声明交换机,指定topic类型
- 消费者:队列绑定交换机指定通配符:统配符规则:中间以“.”分隔;符号#可以匹配多个词,符号*可以匹配一个词语。
- 区别
- Topic模式更多加强大,它可以实现Routing、publish/subscirbe模式的功能。
-
Header
- header模式与routing不同的地方在于,header模式取消routingkey,使用header中的 key/value(键值对)匹配队列。
-
RPC
- RPC即客户端远程调用服务端的方法 ,使用MQ可以实现RPC的异步调用,基于Direct交换机实现。
- 流程
- 客户端即是生产者就是消费者,向RPC请求队列发送RPC调用消息,同时监听RPC响应队列。
- 服务端监听RPC请求队列的消息,收到消息后执行服务端的方法,得到方法返回的结果。
- 服务端将RPC方法 的结果发送到RPC响应队列。
- 客户端(RPC调用方)监听RPC响应队列,接收到RPC调用结果。
-
Spring整合RibbitMQ
- 搭建SpringBoot环境
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐starter‐amqp</artifactId>
</dependency>
- 配置application.yml
server:
port: 44000
spring:
application:
name: test‐rabbitmq‐producer
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
virtualHost: /
- 配置连接rabbitMq参数
- 定义RabbitConfig类,配置Exchange、Queue、绑定交换机
@Configuration
public class RabbitmqConfig {
public static final String QUEUE_INFORM_EMAIL = "queue_inform_email";
public static final String QUEUE_INFORM_SMS = "queue_inform_sms";
public static final String EXCHANGE_TOPICS_INFORM="exchange_topics_inform";
/**
* 交换机配置
*/
@Bean(EXCHANGE_TOPICS_INFORM)
public Exchange EXCHANGE_TOPICS_INFORM() {
//durable(true)持久化,消息队列重启后交换机仍然存在
return ExchangeBuilder.topicExchange(EXCHANGE_TOPICS_INFORM).durable(true).build();
}
//声明队列
@Bean(QUEUE_INFORM_SMS)
public Queue QUEUE_INFORM_SMS() {
Queue queue = new Queue(QUEUE_INFORM_SMS);
return queue;
}
//声明队列
@Bean(QUEUE_INFORM_EMAIL)
public Queue QUEUE_INFORM_EMAIL() {
Queue queue = new Queue(QUEUE_INFORM_EMAIL);
return queue;
}
/**
* 绑定队列到交换机 .
*/
@Bean
public Binding BINDING_QUEUE_INFORM_SMS(@Qualifier(QUEUE_INFORM_SMS) Queue queue,
@Qualifier(EXCHANGE_TOPICS_INFORM) Exchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with("inform.#.sms.#").noargs();
}
@Bean
public Binding BINDING_QUEUE_INFORM_EMAIL(@Qualifier(QUEUE_INFORM_EMAIL) Queue queue,
@Qualifier(EXCHANGE_TOPICS_INFORM) Exchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with("inform.#.email.#").noargs();
}
- 生产端
- 使用RabbitTemplate发送消息
@SpringBootTest
@RunWith(SpringRunner.class)
public class Producer05_topics_springboot {
@Autowired
RabbitTemplate rabbitTemplate;
@Test
public void testSendByTopics(){
for (int i=0;i<5;i++){
String message = "sms email inform to user"+i;
rabbitTemplate.convertAndSend(RabbitmqConfig.EXCHANGE_TOPICS_INFORM,"inform.sms.email",message);
System.out.println("Send Message is:'" + message + "'");
}
}
}
- 消费端
- 使用@RabbitListener注解监听队列。
@Component
public class ReceiveHandler {
//监听email队列
@RabbitListener(queues = {RabbitmqConfig.QUEUE_INFORM_EMAIL})
public void receive_email(String msg,Message message,Channel channel){
System.out.println(msg);
}
//监听sms队列
@RabbitListener(queues = {RabbitmqConfig.QUEUE_INFORM_SMS})
public void receive_sms(String msg,Message message,Channel channel){
System.out.println(msg);
}
}