消息队列(MQ):
是一种应用程序间通讯方法,不需要程序之间直接建立联系。MQ是用于接收、存储、分发消息的独立应用程序。
RabbitMQ:
是用erlang代码写的,遵循amqp协议;amqp协议:高级消息队列协议
RabbitMQ特点
- 可靠性:RabbitMQ使用一些机制保证可靠性,如:持久化、发布确认、消费确认
- 灵活的路由:在消息进入队列之前,通过 Exchange 来路由消息的。对于典型的路由功能,RabbitMQ 已经提供了一些内置的 Exchange 来实现。针对更复杂的路由功能,可以将多个 Exchange 绑定在一起,也通过插件机制实现自己的 Exchange 。
- 消息集群:多个 RabbitMQ 服务器可以组成一个集群,形成一个逻辑 Broker
- 高可用:队列可以在集群中的机器上进行镜像,使得在部分节点出问题的情况下队列仍然可用
- 多种协议:RabbitMQ 支持多种消息队列协议,比如 STOMP、MQTT 等等
- 跟踪机制:如果消息异常,RabbitMQ 提供了消息跟踪机制,使用者可以找出发生了什么
- 插件机制:RabbitMQ 提供了许多插件,来从多方面进行扩展,也可以编写自己的插件
基本概念:
- Broker:表示消息队列服务器实体
- vhost:虚拟主机,每个 vhost 本质上就是一个 mini 版的 RabbitMQ 服务器,拥有自己的队列、交换器、绑定和权限机制。可搭建多环境的RabbitMQ服务器,比如开发、测试、正式
- Connection:表示一个网络连接
- Channel:信道,建立在Connection之上的虚拟连接,发布、接收消息都是通过信道,避免频繁创建连接,所以引入信道复用连接
- Exchange:交换器,用来接收生产者发送的消息并将这些消息路由给服务器中的队列。可以把交换器理解成一个路由表,路由表中有很多路由规则,这些规则将交换器和队列连接起来
- Binding:绑定,它的作用是把exchange和queue按照路由规则绑定起来。一个绑定就是基于路由键将交换器和队列连接起来的路由规则,所以可以将交换器理解成一个由绑定构成的路由表。
- Queue:队列,消息的载体
- RoutingKey:路由关键字,生产者在将消息发送给Exchange的时候,一般会指定一个routing key,来指定这个路由规则,而这个routing key需要与 Exchange Type 与binding key 联合使用才能最终生效。
使用场景:
不需要实时处理的业务,不需要实时反馈结果给用户,不知道谁依赖我的处理结果
- 异步处理:比如生成订单需要发送邮件、用户积分、优惠券、客服统计、写日志等操作,这样可以提升主流程响应速度;
- 系统解耦:各系统之间交互,统一交互规则,不需要管其他系统是否在线
- 数据同步:比如把mysql数据同步到redis或mongodb?
- 流量削峰:系统的瓶颈一般在数据库上,比如扣减库存、下单;此时可以考虑使用队列将变更请求暂存入队列
- 延迟队列:比如下单24小时未支付,自动取消。以前可能采用定时查询,现在可以改成使用延迟队列,避免无效数据库查询,减轻数据库压力,设置队列24小时后让消费者消费
- 分布式事务:
Exchange类型:
不同的Exchange类型不同发布策略也不同,Fanout性能最高,其次是Direct,最后是Topic和Headers
Direct:
消息中的路由键(routing key)如果和 Binding 中的 binding key 一致, 交换器就将消息发到对应的队列中。
Fanout:
每个发到 fanout 类型交换器的消息都会分到所有绑定的队列上去。fanout 交换器不处理路由键,只是简单的将队列绑定到交换器上,每个发送到交换器的消息都会被转发到与该交换器绑定的所有队列上。很像子网广播
Topic(主题)
direct类型的Exchange路由规则是完全匹配binding key与routing key,但这种严格的匹配方式在很多情况下不能满足实际业务需求。topic类型的Exchange在匹配规则上进行了扩展,它与direct类型的Exchage相似,也是将消息路由到binding key与routing key相匹配的Queue中,它约定:
routing key为一个句点号“. ”分隔的字符串(每一段字符串称为一个单词),如“stock.usd.nyse”、“nyse.vmw”、“quick.orange.rabbit”
binding key与routing key一样也是句点号“. ”分隔的字符串
binding key中可以存在两种特殊字符“”与“#”,用于做模糊匹配,其中“”用于匹配一个单词,“#”用于匹配多个单词(可以是零个)】
headers
headers类型的Exchange不依赖于routing key与binding key的匹配规则来路由消息,而是根据发送的消息内容中的headers属性进行匹配。
在绑定Queue与Exchange时指定一组键值对;当消息发送到Exchange时,RabbitMQ会取到该消息的headers(也是一个键值对的形式),对比其中的键值对是否匹配Queue与Exchange绑定时指定的键值对;如果匹配则消息会路由到该Queue,否则不会路由到该Queue。
杂七杂八:
消息的状态:
Ready:等待投递的消息
Unacked:已投递给消费者,但是还没收到消费者确认信号的消息。如果RabbitMQ一直没有收到消费者的确认信号,并且消费此消息的消费者已经断开连接,则RabbitMQ会安排该消息重新进入队列,等待投递给下一个消费者
Default Exchange
Default Exchange 其实是AMQP中预先声明的,并且它属于 Direct Exchange,通常来说每个Exchange都会有自己的名,Default Exchange 的名是 "".
他有一个特殊的属性,当你手动创建一个队列时,MQ会自动将这个队列绑定到Default Exchange 上,绑定时 RoutingKey 与队列名称相同
// 声明一个 Queue,这个时候我们不主动Binding的话,test_queue 会和Default Exchange 绑定,此时 routingKey = test_queue
Queue queue = new Queue("test_queue");
// 向默认交换机发布消息
rabbitTemplate.convertAndSend("", "test_queue", message);