一、介绍MQ
MQ全称为Message Queue,即消息队列, RabbitMQ是由erlang语言开发,基于AMQP(Advanced Message Queue 高级消息队列协议)协议实现的消息队列,它是一种应用程序之间的通信方法,消息队列在分布式系统开发中应用非常广泛。
1.AMQP
AMQP是一套公开的消息队列协议,最早在2003年被提出,它旨在从协议层定义消息通信数据的标准格式,为的就是解决MQ市场上协议不统一的问题。RabbitMQ就是遵循AMQP标准协议开发的MQ服务。
官方:http://www.amqp.org/
二、安装MQ
RabbitMQ是基于erlang的,所以,在正式安装RabbitMQ之前,需要先安装一下erlang。RabbitMQ和Erlang版本对应关系:https://www.rabbitmq.com/which-erlang.html#intro
1.下载并安装Erlang
下载地址:https://www.erlang.org/downloads
2.下载并安装Rabbitmq
下载地址:https://www.rabbitmq.com/download.html
三、启动MQ
1.启动
cmd进入sbin目录运行rabbitmq-plugins enable rabbitmq_management
2.启动出现RabbitMQ和Erlang版本不一致的错误如下##### 3.登录
浏览器输入:http://localhost:15672/
用户名和密码默认是guest
四、关闭MQ
rabbitmqctl stop
五、RabbitMQ工作原理组成部分说明如下:
- Broker:消息队列服务进程,此进程包括两个部分:Exchange和Queue。
- Exchange:消息队列交换机,按一定的规则将消息路由转发到某个队列,对消息进行过虑。
- Queue:消息队列,存储消息的队列,消息到达队列并转发给指定的消费方。
- Producer:消息生产者,即生产方客户端,生产方客户端将消息发送到MQ。
- Consumer:消息消费者,即消费方客户端,接收MQ转发的消息。
消息发布接收流程:
-----发送消息-----
1、生产者和Broker建立TCP连接。
2、生产者和Broker建立通道。
3、生产者通过通道消息发送给Broker,由Exchange将消息进行转发。
4、Exchange将消息转发到指定的Queue(队列)
----接收消息-----
1、消费者和Broker建立TCP连接
2、消费者和Broker建立通道
3、消费者监听指定的Queue(队列)
4、当有消息到达Queue时Broker默认将消息推送给消费者。
5、消费者接收到消息。
六、MQ的六种工作模式
1.simple简单模式
一个生产者,一个消费者。
2.work工作模式
多个消费端消费同一个队列中的消息,队列采用轮询的方式将消息是平均发送给消费者
3.publish/subscribe发布订阅(共享资源)
一个生产者发送的消息会被多个消费者获取。
4.routing路由模式
发送消息到交换机并且要指定路由key ,消费者将队列绑定到交换机时需要指定路由key。
5.topic主题模式
将路由键和某模式进行匹配,此时队列需要绑定在一个模式上,“#”匹配一个词或多个词,“*”只匹配一个词。
6.RPC模式
rpc模式即远程调用,客户端通过rabbitmq发送消息至服务器端,在服务器端调用各种函数对消息进行处理后将处理结果通过另一消息队列返回给客户端。客户端和服务器在此过程中既是发送方也是接收方。
七、应用场景
1.异步处理
将不需要同步处理的并且耗时长的操作由消息队列通知消息接收方进行异步处理。提高了应用程序的响应时间。
2.应用解耦
MQ相当于一个中介,生产方通过MQ与消费方交互,它将应用程序进行解耦合。
3.流量削锋
比如秒杀
4.日志处理
日志处理是指将消息队列用在日志处理中,比如Kafka的应用,解决大量日志传输的问题。
- 日志采集客户端,负责日志数据采集,定时写受写入Kafka队列;
- Kafka消息队列,负责日志数据的接收,存储和转发;
- 日志处理应用:订阅并消费kafka队列中的日志数据;
(1)Kafka:接收用户日志的消息队列。
(2)Logstash:做日志解析,统一成JSON输出给Elasticsearch。
(3)Elasticsearch:实时日志分析服务的核心技术,一个schemaless,实时的数据存储服务,通过index组织数据,兼具强大的搜索和统计功能。
(4)Kibana:基于Elasticsearch的数据可视化组件,超强的数据可视化能力是众多公司选择ELK stack的重要原因。
八、RabbitMQ四种交换机
1.Direct Exchanges
处理路由键。需要将一个队列绑定到交换机上,要求该消息与一个特定的路由键完全匹配。这是一个完整的匹配。如果一个队列绑定到该交换机上要求路由键 “adc”,则只有被标记为“abc”的消息才被转发,不会转发abc.def,也不会转发dog.ghi,只会转发abc。
2.Fount Exchanges
将路由键和某模式进行匹配。此时队列需要绑定要一个模式上。符号“#”匹配一个或多个词,符号“”匹配不多不少一个词。因此“abc.#”能够匹配到“abc.def.ghi”,但是“abc.” 只会匹配到“abc.def”。
3.Topic Exchanges
将路由键和某模式进行匹配。此时队列需要绑定要一个模式上。符号“#”匹配一个或多个词,符号“”匹配不多不少一个词。因此“abc.#”能够匹配到“abc.def.ghi”,但是“abc.” 只会匹配到“abc.def”。
4.Headers Exchanges
不处理路由键。而是根据发送的消息内容中的headers属性进行匹配。在绑定Queue与Exchange时指定一组键值对;当消息发送到RabbitMQ时会取到该消息的headers与Exchange绑定时指定的键值对进行匹配;如果完全匹配则消息会路由到该队列,否则不会路由到该队列。headers属性是一个键值对,可以是Hashtable,键值对的值可以是任何类型。而fanout,direct,topic 的路由键都需要要字符串形式的。
匹配规则x-match有下列两种类型:
x-match = all :表示所有的键值对都匹配才能接受到消息
x-match = any :表示只要有键值对匹配就能接受到消息
九、SpringBoot集成RabbitMQ
基本配置信息
pom.xml
<!-- rabbitMQ -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
application.properties
spring.rabbitmq.addresses=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/
#每次从队列中取一个,轮询分发,默认是公平分发
spring.rabbitmq.listener.simple.prefetch=1
#失败后重试
spring.rabbitmq.listener.simple.default-requeue-rejected=true
1.work模式
/** *****************消费者****************************************** */
@RabbitListener(queues = "test_work")
public void workConusmer1(String s) {
System.out.println("workConusmer1=======" + s);
}
@RabbitListener(queues = "test_work")
public void workConusmer2(String s) {
System.out.println("workConusmer2=======" + s);
}
/** *****************work****************************************** */
@Bean
public Queue queueWork() {
return new Queue("test_work");
}
/** *****************发送****************************************** */
for (int i = 1; i< 7 ; i++){
String context = "sendWork==========" + new Date()+"===="+i;
rabbitTemplate.convertAndSend("test_work",context);
}
2.发布订阅模式
/** *****************消费者****************************************** */
@RabbitListener(queues = "test_publish1")
public void publishConusmer1(String s) {
System.out.println("publishConusmer1=======" + s);
}
@RabbitListener(queues = "test_publish2")
public void publishConusmer2(String s) {
System.out.println("publishConusmer2=======" + s);
}
/** *****************publish****************************************** */
@Bean
public Queue queuePublih1() {
return new Queue("test_publish1");
}
@Bean
public Queue queuePublih2() {
return new Queue("test_publish2");
}
//定义交换机
@Bean
public FanoutExchange exchangeFanout() {
return new FanoutExchange("publish_exchange");
}
//将队列与交换机绑定
@Bean
public Binding bindingExchange1(){
return BindingBuilder.bind(queuePublih1()).to(exchangeFanout());
}
//将队列与交换机绑定
@Bean
public Binding bindingExchange2(){
return BindingBuilder.bind(queuePublih2()).to(exchangeFanout());
}
/** *****************发送****************************************** */
for (int i = 1; i< 7 ; i++){
String context = "sendPublish==========" + new Date()+"===="+i;
rabbitTemplate.convertAndSend("publish_exchange","",context);
}
3.topic模式
/** *****************消费者****************************************** */
@RabbitListener(queues = "test_topic1")
public void topicConsumer1(String s) {
System.out.println("topicConsumer1=========="+s);
}
@RabbitListener(queues = "test_topic1")
public void topicConsumer2(String s) {
System.out.println("topicConsumer2=========="+s);
}
/** *********************topic********************************** */
@Bean
public Queue queueTopic1() {
return new Queue("test.topic1");
}
@Bean
public Queue queueTopic2() {
return new Queue("test.topic1");
}
@Bean
public TopicExchange exchangeTopic() {
return new TopicExchange("topic_exchange");
}
/**** # 匹配一个或者多个,* 匹配一个 ** */
@Bean
public Binding bindingTopic1() {
return BindingBuilder.bind(queueTopic1()).to(exchangeTopic()).with("test.#");
}
@Bean
public Binding bindingTopic2() {
return BindingBuilder.bind(queueTopic2()).to(exchangeTopic()).with("test.*");
}
/** *****************发送****************************************** */
for (int i = 1; i< 7 ; i++){
String context = "sendPublish topic_exchange1==========" + new Date()+"===="+i;
rabbitTemplate.convertAndSend("topic_exchange","test.topic1",context);
}
for (int i = 1; i< 7 ; i++){
String context = "sendtopic topic_exchange2==========" + new Date()+"===="+i;
rabbitTemplate.convertAndSend("topic_exchange","test.topic2",context);
}