MQ是什么? MQ的好处是什么? MQ有什么缺点?
MQ全称为Message Queue,消息队列是应用程序和应用程序之间的通信方法。
优点:应用解耦 异步提速 削峰填谷
缺点:可用降低(集群) 复杂度提高 数据的一致
AMQP 和 JMS
MQ是消息通信的模型;实现MQ的大致有两种主流方式:AMQP、JMS。
AMQP:
AMQP是一种协议,更准确的说是一种binary wire-level protocol(链接协议)。这是其和JMS的本质差别,AMQP不从API层进行限定,而是直接定义网络交换的数据格式。
JMS:
JMS即Java消息服务(JavaMessage Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信
AMQP 与 JMS 区别
JMS是定义了统一的接口,来对消息操作进行统一;AMQP是通过规定协议来统一数据交互的格式
JMS限定了必须使用Java语言;AMQP只是协议,不规定实现方式,因此是跨语言的。
JMS规定了两种消息模式;而AMQP的消息模式更加丰富
RabbitMQ和MQ的关系是什么?
RabbitMQ是MQ的一种实现
RabbitMQ概念(重要)
RabbitMQ是由erlang语言开发,基于AMQP(Advanced Message Queue 高级消息队列协议)协议实现的消息队列,它是一种应用程序之间的通信方法,消息队列在分布式系统开发中应用非常广泛。
RabbitMQ的安装
RabbitMQ的使用:五种工作模式
简单模式、工作队列模式、发布订阅模式、路由模式、通配符模式
MQ的学习目的
MQ: message queue 消息队列
消息队列如何实现分布式的通信
生产者、消费者、中间件
MQ的优势
应用解耦
异步提速
削峰填谷
MQ的缺点
可用降低(集群)
复杂度提高
数据的一致
RabbitMQ和MQ的关系
ActiveMQ:基于JMS
ZeroMQ:基于C语言开发
RabbitMQ:基于AMQP协议,erlang语言开发,稳定性好
RocketMQ:基于JMS,阿里巴巴产品
Kafka:类似MQ的产品;分布式消息系统,高吞吐量
RabbitMQ概念
虚拟机连接网络三种方式:
(1)创建一个用户
(2)创建一个虚拟主机(VirtualHost)
(3)VirtualHost绑定用户
(4)host: 127.0.0.1
port: 5672
username:guest
password: guest
virtualHost: /
1.生产者
//1.创建连接工厂
//2.设置连接参数(host/port/username/password/virtualhost)
//3.创建连接
//4.通过连接创建通道(channel)
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.200.131");
connectionFactory.setPort(5672);
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
connectionFactory.setVirtualHost("/");
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
---------------------------------------
//5.创建一个交换机(Exchange)
//6.创建一个队列(Queue)
//7.绑定交换机和队列之间的关系(Binding)
---------------------------------------
//8.发送消息
----------------------------------------
//9.关闭资源
channel.close();
connection.close();
2.消费者
//1.创建连接工厂
//2.设置连接参数(host/port/username/password/virtualhost)
//3.创建连接
//4.通过连接创建通道(channel)
--------------------------------------------
//5.创建一个交换机(Exchange)
//6.创建一个队列(Queue)
//7.绑定交换机和队列之间的关系(Binding)
Exchange,Queue,Binding:声明的时候,如果已经存在,则不再创建,如果不存在,才进行创建
要: 启动消费者的时候,启动顺序没有要求
不要:将消费者必须要在生产者运行之后才能启动
--------------------------------------------
//8.监听队列,从队列中获取消息
Consumer consumer = new DefaultConsumer(channel){
handleDelivery()....
};
channel.basicConsume("queueName",true,consumer);
------------------------------------------
//9.关闭资源
不要去关闭资源
简单模式
/**
* 参数一:队列名称
* 参数二:是否持久化,队列在RabbitMQ重启后是否还存在
* 参数三:消费者是否独占该队列
* 参数四:是否自动删除,当没有consumer自动删除
* 参数五:额外参数
*/
channel.queueDeclare("hello_world",true,false,false,null);
/**
* 参数一:交换机名称,若使用默认,则给值为""
* 参数二:routingKey 路由key
* 参数三:配置信息
* 参数四:消息体
*/
channel.basicPublish("","hello_world",null,"hello rabbitmq~~~~".getBytes());
生产者发送消息
生产者创建连接(Connection),开启一个信道(Channel),连接到RabbitMQ Broker;
声明队列并设置属性;如是否排它,是否持久化,是否自动删除;
将路由键(空字符串)与队列绑定起来;
发送消息至RabbitMQ Broker;
关闭信道;
关闭连接;
消费者接收消息
消费者创建连接(Connection),开启一个信道(Channel),连接到RabbitMQ Broker
向Broker 请求消费相应队列中的消息,设置相应的回调函数;
等待Broker回应闭关投递响应队列中的消息,消费者接收消息;
确认(ack,自动确认)接收到的消息;
RabbitMQ从队列中删除相应已经被确认的消息;
关闭信道;
关闭连接;
应用场景
工作队列模式
多个消费者会以轮询的方式从消息队列中获取消息进行消费。订阅模式类型:
P:生产者,也就是要发送消息的程序,但是不再发送到队列中,而是发给X(交换机)
C:消费者,消息的接受者,会一直等待消息到来。
Queue:消息队列,接收消息、缓存消息。
Exchange:交换机,图中的X。一方面,接收生产者发送的消息。另一方面,知道如何处理消息,例如递交给某个特别队列、递交给所有队列、或是将消息丢弃。到底如何操作,取决于Exchange的类型。Exchange有常见以下3种类型:
Fanout:广播,将消息交给所有绑定到交换机的队列
Direct:定向,把消息交给符合指定routing key 的队列
Topic:通配符,把消息交给符合routing pattern(路由模式) 的队列
Exchange(交换机)只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与Exchange绑定,或者没有符合路由规则的队列,那么消息会丢失!
发布订阅模式
发布订阅模式:
1、每个消费者监听自己的队列。
2、生产者将消息发给broker,由交换机将消息转发到绑定此交换机的每个队列,每个绑定交换机的队列都将接收到消息
(1)声明一个交换机
channel.exchangeDeclare("exchangeName",BuiltinExchangeType.FANOUT);
(2)声明了两个队列
channel.queueDeclare("queueName1",true,false,false,null);
channel.queueDeclare("queueName2",true,false,false,null);
(3)绑定交换机和队列之间的关系
channel.queueBind("queueName1","exchangeName","");
channel.queueBind("queueName2","exchangeName","");
(4)发送消息
channel.basicPublish("exchangeName","",null,body);
(5)接收消息
channel.basicConsume("queueName",true,consumer);
路由模式
路由模式特点:
队列与交换机的绑定,不能是任意绑定了,而是要指定一个RoutingKey(路由key)
消息的发送方在 向 Exchange发送消息时,也必须指定消息的 RoutingKey。
Exchange不再把消息交给每一个绑定的队列,而是根据消息的Routing Key进行判断,只有队列的Routingkey与消息的 Routing key完全一致,才会接收到消息
交换机的类型为:Direct,还有队列绑定交换机的时候需要指定routing key。
(1)声明一个交换机
channel.exchangeDeclare("exchangeName",BuiltinExchangeType.DIRECT);
(2)声明了两个队列
channel.queueDeclare("queueName1",true,false,false,null);
channel.queueDeclare("queueName2",true,false,false,null);
(3)绑定交换机和队列之间的关系
channel.queueBind("queueName1","exchangeName","a");
channel.queueBind("queueName2","exchangeName","b");
channel.queueBind("queueName1","exchangeName","c");
channel.queueBind("queueName2","exchangeName","c");
(4)发送消息
channel.basicPublish("exchangeName","routingKey",null,body);
(5)接收消息
channel.basicConsume("queueName",true,consumer);
通配符模式
可以根据RoutingKey把消息路由到不同的队列。只不过Topic类型Exchange可以让队列在绑定Routing key 的时候使用通配符!
Routingkey 一般都是有一个或多个单词组成,多个单词之间以”.”分割,例如: item.insert
通配符规则:
#:匹配一个或多个词
:匹配不多不少恰好1个词
举例:
item.#:能够匹配item.insert.abc 或者 item.insert
item.:只能匹配item.insert
(1)声明一个交换机
channel.exchangeDeclare("exchangeName",BuiltinExchangeType.TOPIC);
(2)声明了两个队列
channel.queueDeclare("queueName1",true,false,false,null);
channel.queueDeclare("queueName2",true,false,false,null);
(3)绑定交换机和队列之间的关系
channel.queueBind("queueName1","exchangeName","a.#");
channel.queueBind("queueName2","exchangeName","b.#");
channel.queueBind("queueName1","exchangeName","*.a.*");
channel.queueBind("queueName2","exchangeName","b.#.c");
(4)发送消息
channel.basicPublish("exchangeName","routingKey",null,body);
(5)接收消息
channel.basicConsume("queueName",true,consumer);
》ConnectionFactory
》Connection
》channel
》channel.exchangeDeclare
》channel.queueDeclare
》channel.queueBind()
》channel.basicPublish()
》channel.basicConsume()·
RabbitMQ工作模式:
1、简单模式 HelloWorld
一个生产者、一个消费者,不需要设置交换机(使用默认的交换机)
2、工作队列模式 Work Queue
一个生产者、多个消费者(竞争关系),不需要设置交换机(使用默认的交换机)
3、发布订阅模式 Publish/subscribe
需要设置类型为fanout的交换机,并且交换机和队列进行绑定,当发送消息到交换机后,交换机会将消息发送到绑定的队列
4、路由模式 Routing
需要设置类型为direct的交换机,交换机和队列进行绑定,并且指定routing key,当发送消息到交换机后,交换机会根据routing key将消息发送到对应的队列
5、通配符模式 Topic
需要设置类型为topic的交换机,交换机和队列进行绑定,并且指定通配符方式的routing key,当发送消息到交换机后,交换机会根据routing key将消息发送到对应的队列
**
Spring整合RabbitMQ
**
pom.xml
<dependencies>
<!--junit单元测试驱动-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--导入spring的context坐标,context依赖core、beans、expression-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!--导入spring集成junit的坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>2.1.8.RELEASE</version>
</dependency>
</dependencies>
生产者
applicationContext.xml
ConnectionFactory
<!--加载配置文件-->
<context:property-placeholder location="classpath:rabbitmq.properties"/>
<!-- 定义rabbitmq connectionFactory -->
<rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}"
port="${rabbitmq.port}"
username="${rabbitmq.username}"
password="${rabbitmq.password}"
virtual-host="${rabbitmq.virtual-host}"/>
<!--定义管理交换机、队列-->
<rabbit:admin connection-factory="connectionFactory"/>
Queue
<!--定义持久化队列,不存在则自动创建;不绑定到交换机则绑定到默认交换机
默认交换机类型为direct,名字为:"",路由键为队列的名称
-->
<rabbit:queue id="spring_queue" name="spring_queue" auto-declare="true"/>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~广播;所有队列都能收到消息~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<!--定义广播交换机中的持久化队列,不存在则自动创建-->
<rabbit:queue id="spring_fanout_queue_1" name="spring_fanout_queue_1" auto-declare="true"/>
<!--定义广播交换机中的持久化队列,不存在则自动创建-->
<rabbit:queue id="spring_fanout_queue_2" name="spring_fanout_queue_2" auto-declare="true"/>
Exchange
<!--定义广播类型交换机;并绑定上述两个队列-->
<rabbit:fanout-exchange id="spring_fanout_exchange" name="spring_fanout_exchange" auto-declare="true">
<rabbit:bindings>
<rabbit:binding queue="spring_fanout_queue_1"/>
<rabbit:binding queue="spring_fanout_queue_2"/>
</rabbit:bindings>
</rabbit:fanout-exchange>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~通配符;*匹配一个单词,#匹配多个单词 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<!--定义通配符交换机中的持久化队列,不存在则自动创建-->
<rabbit:queue id="spring_topic_queue_star" name="spring_topic_queue_star" auto-declare="true"/>
<!--定义通配符交换机中的持久化队列,不存在则自动创建-->
<rabbit:queue id="spring_topic_queue_well" name="spring_topic_queue_well" auto-declare="true"/>
<!--定义通配符交换机中的持久化队列,不存在则自动创建-->
<rabbit:queue id="spring_topic_queue_well2" name="spring_topic_queue_well2" auto-declare="true"/>
Exchange-Queue(Bingding)
<!--定义通配符类型交换机;并绑定上述队列-->
<rabbit:topic-exchange id="spring_topic_exchange" name="spring_topic_exchange" auto-declare="true">
<rabbit:bindings>
<rabbit:binding pattern="heima.*" queue="spring_topic_queue_star"/>
<rabbit:binding pattern="heima.#" queue="spring_topic_queue_well"/>
<rabbit:binding pattern="itcast.#" queue="spring_topic_queue_well2"/>
</rabbit:bindings>
</rabbit:topic-exchange>
rabbitTemplate
<!--定义rabbitTemplate对象操作可以在代码中方便发送消息-->
<rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>
消费者
applicationContext.xml
ConnectionFactory
<!--创建监听类-->
<bean id="springQueueListener" class="live.longmarch.rabbitmq.listener.SpringQueueListener"/>
<bean id="fanoutListener1" class="live.longmarch.rabbitmq.listener.FanoutListener1"/>
<bean id="fanoutListener2" class="live.longmarch.rabbitmq.listener.FanoutListener2"/>
<bean id="topicListenerStar" class="live.longmarch.rabbitmq.listener.TopicListenerStar"/>
<bean id="topicListenerWell" class="live.longmarch.rabbitmq.listener.TopicListenerWell"/>
<bean id="topicListenerWell2" class="live.longmarch.rabbitmq.listener.TopicListenerWell2"/>
<!--配置监听类所监听的队列-->
<rabbit:listener-container connection-factory="connectionFactory" auto-declare="true">
<rabbit:listener ref="springQueueListener" queue-names="spring_queue"/>
<rabbit:listener ref="fanoutListener1" queue-names="spring_fanout_queue_1"/>
<rabbit:listener ref="fanoutListener2" queue-names="spring_fanout_queue_2"/>
<rabbit:listener ref="topicListenerStar" queue-names="spring_topic_queue_star"/>
<rabbit:listener ref="topicListenerWell" queue-names="spring_topic_queue_well"/>
<rabbit:listener ref="topicListenerWell2" queue-names="spring_topic_queue_well2"/>
</rabbit:listener-container>
listener监听类 implements MessageListener
onMessage…
业务操作
public class SpringQueueListener implements MessageListener {
public void onMessage(Message message) {
try {
String msg = new String(message.getBody(), "utf-8");
System.out.printf("接收路由名称为:%s,路由键为:%s,队列名为:%s的消息:%s \n",
message.getMessageProperties().getReceivedExchange(),
message.getMessageProperties().getReceivedRoutingKey(),
message.getMessageProperties().getConsumerQueue(),
msg);
} catch (Exception e) {
e.printStackTrace();
}
}
}
读取applicationContext.xml配置文件,启动监听
总结:
MQ
Message Queue : 存储消息的一个容器
生产者、broker、消费者
MQ优点: 解耦、提速、削峰 [秒杀…]
MQ缺点: 维护高,可用性降低、复杂度提供、数据处理的一致性
如何选择MQ: 允许异步、投入和产出
技术---->做什么----->适合项目中的那个模块用----->项目
MQ的具体实现:
RabbitMQ(erlang AMQP协议)
ActiveMQ(java JMS接口)
RocketMQ(阿里)
Kafka
ZeroMQ
RabbitMQ的概念
Producer–>Connection(channel)–>virtualHost[Exchange–>binding(routingKey)–>queue]<–Connection(channel)<–consumer
RabbitMQ的使用
简单模式、工作队里模式、发布订阅模式、路由模式、通配符模式
RabbitMQ代码
ConnectionFactory(5要素)
Connection
Channel
Channel.queueDeclare(queueName,true,false,false,null)
Channel.exchangeDeclare(exchangeName,type) type: fanout direct topic
Channel.queueBind(queueName,exchange,routingKey);
Channel.basicPublish(exchange,routingKey,null,body.getBytes());
Consumer consumer = new DefaultConsumer(channel){
handleDelivery…
业务操作
}
Channel.basicConsume(queueName,true,consumer);
RabbitMQ整合Spring开发
pom.xml
生产者
applicationContext.xml
ConnectionFactory
Queue
Exchange
Exchange-Queue(Bingding)
rabbitTemplate
消费者
applicationContext.xml
ConnectionFactory
listener监听类 implements MessageListener
onMessage…
业务操作