- 一、消息服务概述
- 二、消息服务可以解决的问题
- 三、RabbitMQ概述
-
大多应用中,可通过消息服务中间件来提升系统异步通信、扩展解耦能力。
-
消息服务中两个重要概念:
消息代理(message broker)
和目的地(destination)
当消息发送者发送消息以后,将由消息代理接管,消息代理保证消息传递到指定目的地。 -
消息队列主要有两种形式的目的地
队列(queue)
:点对点消息通信(point-to-point)主题(topic)
:发布(publish)/订阅(subscribe)消息通信 -
点对点式: – 消息发送者发送消息,消息代理将其放入一个队列中,消息接收者从队列中获取消息内容,消息读取后被移出队列 – 消息只有
唯一的发送者和接受者
,但并不是说只能有一个接收者 -
发布订阅式: – 发送者(发布者)发送消息到主题,
多个接收者(订阅者)监听(订阅)这个主题
,那么就会在消息到达时同时收到消息 -
JMS(Java Message Service)JAVA消息服务: – 基于JVM消息代理的规范。
ActiveMQ、HornetMQ是JMS实现
-
AMQP(Advanced Message Queuing Protocol) – 高级消息队列协议,也是一个消息代理的规范,兼容JMS –
RabbitMQ是AMQP的实现
-
Spring支持 – spring-jms提供了对JMS的支持 – spring-rabbit提供了对AMQP的支持 – 需要ConnectionFactory的实现来
连接消息代理
– 提供JmsTemplate、RabbitTemplate来发送消息 –@JmsListener
(JMS)、@RabbitListener
(AMQP)注解在方法上监听消息代理发布的消息 –@EnableJms
、@EnableRabbit
开启支持 -
Spring Boot自动配置 – JmsAutoConfiguration –
RabbitAutoConfiguration
跳转到目录
1、异步处理场景说明:用户注册后,需要发注册邮件和注册短信,传统的做法有两种:a)串行的方式;b)并行的方式
①串行方式:将注册信息写入数据库后,发送注册邮件,再发送注册短信,以上三个任务全部完成后才返回给客户端。 这有一个问题是:邮件,短信并不是必须的,它只是一个通知,而这种做法让客户端等待没有必要等待的东西。 ②并行方式:将注册信息写入数据库后,发送邮件的同时,发送短信,以上三个任务完成后,返回给客户端,并行的方式能提高处理的时间。
假设三个业务节点分别使用50ms,串行方式使用时间150ms,并行使用时间100ms。虽然并性已经提高的处理时间,但是,前面说过,邮件和短信对我正常的使用网站没有任何影响,客户端没有必要等着其发送完成才显示注册成功,应该是写入数据库后就返回。
③消息队列:引入消息队列后,把发送邮件,短信不是必须的业务逻辑异步处理: 由此可以看出,引入消息队列后,用户的响应时间就等于
写入数据库的时间+写入消息队列的时间
(可以忽略不计),引入消息队列后处理后,响应时间是串行的3倍,是并行的2倍。
场景:双11是购物狂节,用户下单后,订单系统需要通知库存系统,传统的做法就是订单系统调用库存系统的接口。 这种做法有一个缺点:
- 当库存系统出现故障时,订单就会失败。
- 订单系统和库存系统高耦合。
引入消息队列:
-
订单系统:用户下单后,订单系统完成持久化处理,将消息
写入消息队列
,返回用户订单下单成功。 -
库存系统:
订阅下单的消息,获取下单消息,进行库操作
。就算库存系统出现故障,消息队列也能保证消息的可靠投递,不会导致消息丢失。
流量削峰一般在秒杀活动中应用广泛。
场景:秒杀活动,一般会因为流量过大,导致应用挂掉,为了解决这个问题,一般在应用前端加入消息队列。
作用:
- 可以控制活动人数,超过此一定阀值的订单直接丢弃
- 可以缓解短时间的高流量压垮应用(应用程序按自己的最大处理能力获取订单)
用户的请求,服务器收到之后,首先写入消息队列,加入消息队列长度超过最大值,则直接抛弃用户请求或跳转到错误页面。秒杀业务根据消息队列中的
请求信息
,再做后续处理。
跳转到目录 MQ 全称为 Message Queue
,是一种分布式应用程序的的通信方法,是消费者-生产者模型
的典型的代表,producer 往消息队列中不断写入消息,而另一端 consumer 则可以读取或者订阅队列中的消息,这点可以与数据结构中队列的作用相类似,具有 FIFO 的特点。
RabbitMQ 是 MQ 产品的典型实现,是基于 AMQP
协议可复用的企业消息系统。业务上,可以实现服务提供者和消费者之间的数据解耦,提供高可用性的消息传输机制,在实际生产中应用相当广泛。
首先来一张消息队列的经典图,可以划分为三个角色: Producer, Queue, Consumer
- Queue:为承载消息的容器,为什么是队列而不是栈呢?主要是因为绝大部分的场景,我们都是希望消息是
先进先出
,有顺序的 - Producer:生产者,就是产生消息,并不断往队列塞的角色
- Consumer:消费者,也就是不断从队列中获取消息的角色
其实在生活中,这种模型用得非常多,就比如我们都会接触的网购快递,可以说是一个典型的消息队列的 例子
了:
商家不断的把商品扔给快递公司(注意不是直接将商品给买家),而快递公司则将商品根据地质分发对应的买家
对上面这个过程进行拆解,可以映射扮演的角色
- 商品:Message,传递的消息,由商家投递给快递公司时,需要进行打包(一般Producer生产消息也会将实体数据进行封装)
- 商家:Produer 生产者
- 快递公司: Queue,消息的载体
- 买家:Consumer 消费者
那么快递公司时怎么知道要把商品给对应的买家呢?根据包裹上的地址+电话
- 同样消息队列也需要一个映射规则,实现Message和Consumer之间的
路由
核心概念:
-
Message
:消息,包含消息头
(即附属的配置信息)和消息体
(即消息的实体内容)消息,消息体是不透明的,消息体是由一些可选属性组成的,包括路由键
(routing-key)、优先级(priority)、持久性存储(delivery-mode)等。- Exchange(交换机)就是通过
路由键
来将消息
路由到哪个消息队列
中;
- Exchange(交换机)就是通过
-
Publisher
:消息生产者,向交换机发布消息的客户端程序,我们可以简单理解为就是一个 Java 程序。 -
Exchange
:交换机,用来接收生产者发送的消息并将这些消息路由给服务器中的消息队列。常见的三种交换器类型:
direct
:发布与订阅,完全匹配 。我们可以简单理解为一对一的关系,一个交换器将消息发送给一个队列,是完全匹配的fanout
:广播,所有订阅该广播的队列都可以收到该消息。广播式交换器,不管消息的 ROUTING_KEY 设置为什么,Exchange 都会将消息转发给所有绑定的 Queuetopic
:主题,规则匹配 。 -
Queue
:消息队列,用来保存消息直到发送给消费者。它是消息的容器
,也是消息的终点。一个消息可投入一个或多个队列。消息一直在队列里面,等待消费者连接到这个队列将其取走。 -
Binding
:绑定,用于给 Exchange(交换机) 和 Queue(消息队列) 建立关系,把 exchange 和 queue 按照路由规则
绑定起来, 可以是多对多的关系。 -
Connection
:网络连接 -
Channel
:信道,MQ(消息队列)与外部打交道都是通过Channel来的,发布消息、订阅队列还是接收消息,这些动作都是通过Channel完成;简单来说就是消息通过Channel塞进队列或者流出队列。在客户端的每个连接里,可建立多个channel。因为对于操作系统来说建立和销毁 TCP 都是非常昂贵的开销,所以引入了信道的概念,以复用
一条 TCP 连接。 -
Consumer
:消费者,从消息队列中获取消息的客户端应用程序。 -
Virtual Host
:虚拟主机,表示一批交换器、消息队列和相关对象。 虚拟主机是共享相同的身份认证和加密环境的独立服务器域。每个 vhost 本质上就是一个 mini 版的 RabbitMQ 服务器,拥有自己的队列、交换器、绑定和权限机制。vhost 是 AMQP 概念的基础,必须在连接时指定,RabbitMQ 默认的 vhost 是/
。一个 broker 里可以有多个 vhost,用作不同用户的权限分离。 -
Broker
:消息队列服务器实体,它提供一种传输服务,它的角色就是维护一条从生产者到消费者的路线,保证数据能按照指定的方式进行传输。可以理解为在 Linux 上创建的虚拟机实体。
- 生产者(Publisher)生产消息,
消息
通过Channel(信道)发送给Broker(服务器主体), Broker再分配给一个Exchange(交换机) - Exchange收到消息后, 通过消息中的
ROUTEKEY
(路由键), 将消息转发给匹配的Queue(消息队列) - Queue收到消息后, 将消息通过Channel发送给消费者(Consumer)
- Consumer收到消息后, 发送
ACK
(确认标志)给消息队列确认收到消息 - Queue收到ACK, 删除该消息队列中缓存的此条消息.