消息服务概述:
大多数应用中,可通过消息服务中间件来提升系统异步通信,扩展解耦能力
两个重要概念:消息代理(message broker)和目的地(destination)。当消息发送者者发出消息后,将由消息代理接管,消息代理保证将消息传递至目的地
两种形式的目的地:
队列(queue):点对点的消息通信(point-to-point)
主题(topic):发布(publish)/订阅(subscribe)的消息通信
点对点式:消息发送者发送消息,消息代理将消息放在一个队列中,消息接收者从队列中获取内容,消息读取后移除队列。消息只有唯一的发送者和接收者。但接收者并未说明只能有1个。
发布订阅式:发送者发送消息到主题,多个订阅者(接收者)监听(订阅)这个主题,会在消息到达时同时收到消息
两种消息代理规范:
JMS(Java-Message-Service)Java消息服务:基于JVM消息代理的规范,ActiveMQ、HornetMQ是JMS实现
AMQP(Advanced Message Queuing Protocol)高级消息队列协议:消息代理规范,兼容JMS,RabbitMQ是AMQP的实现
spring支持:spring提供了对JMS的支持,spring-rabbit提供可对AMQP的支持,需要ConnectionFactory的实现来连接消息代理,提供JmsTemplate、RabbitTemplate来发送消息,
@JmsListenet、@RabbitListener在方法上监听消息代理发送的消息,@EnableJms、@EnableRabbit开启支持
消息服务可解决问题
异步处理:
示例场景:用户注册,注册成功之后给用户邮箱及短信发送注册成功,注册、发送邮件、发送短信分别耗时50ms
串行模式:注册->发送邮件->发送短信 共计耗时150ms
并行模式:注册->发送邮件、发送短信 共计耗时100ms
分析优化思路:邮件和短信只是一个通知,并非客户端所必须的,因此注册成功之后即可告知客户端注册成功,客户端没必要等着收到邮件和短信后才显示注册成功
消息队列:注册->消息队列->发送短信/发送邮件 共计耗时55ms.
引入消息队列后,用户响应时间即 写入数据库的时间+写入消息队列的时间(可忽略不计)。
引入消息队列后,用户响应时间是串行模式的3倍,是并行模式的2倍
应用解耦
场景:双11购物节,用户下单后,订单系统告知库存系统
传统做法:订单系统调用库存系统的接口,但是此方法有缺点:一旦库存系统出现故障,订单就会失败;订单系统和库存系统高耦合
引入消息队列:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户下单成功;库存系统监听消息队列,收到消息之后,进行库存相关操作。就算库存系统出现故障,消息队列也会能保证消息的可靠推送,不会导致消息丢失。
流量削峰:秒杀活动中应用广泛
场景:秒杀活动,一般流量较大,导致应用过大,解决此问题可引入消息队列
作用:控制活动人数,超过一定阈值的订单直接丢弃;缓解短时间的高流量压垮应用。
流程:用户发起请求后,将消息写入消息队列,如果消息队列长度超过最大值,则直接抛弃用户请求或跳转到错误界面。秒杀业务系统监听消息队列中的数据,根据其做后续处理
rabbitmq概述
MQ(Message Queue):是一种分布式应用程序的通信方法,是消费者-生产者模型的典型代表,
生产者(producer)往消息队列中不断写入消息,consumer则读取或订阅队列的消息,这点和队列类似,具有FIFO(先入先出)的特点
消息队列:在消息队列模型中,可划分为3个角色,Producer、Queue、Consumer
Producer:生产者,往消息队列中添加数据的角色
Queue:队列,为承载消息的容器。不是栈是因为想使消息先进先出,有序。栈是先进后出
Consumer:消费者,从消息队列中获取数据的角色
理解消息队列:
例如用户下单购买商品,商家将商品订单发给快递公司,快递公司根据商品中买家的信息分发对应的买家
在上述过程中,商品作为消息,商家作为消息的生产者,快读公司作为队列,买家作为消费者。
快递公司根据包裹中买家的地址和电话锁定买家。因此消息队列中队列和消费者也需要一个映射规则,实现Message和Consumer之间的路由
RabbitMQ基本概念
Mseeage:消息,包含消息头(附属配置信息)和消息体(消息实体内容),消息体是不透明的,
消息体是由一些可选属性组成,包括路由键(routing-key)、优先级(priority)、持久化存储(delivery-mode)等
Publisher:消息生产者,向交换机发布信息的程序
Exchange:交换机,接收生产者发送的消息并将这些消息路由给服务器中的消息队列
常见3种交换器:direct、fanout、topic
direct:发布与订阅,完全匹配
fanout:广播,所有订阅该广播的队列都可以收到该消息
topic:主题,规则匹配
Queue:消息队列,保存消息直到发送给消费者。消息容器,也是消息的终点,一个消息可投入一个或多个消息队列,消息一直在队列中,直到消费者将消息取走。
Binding:绑定,用于给Exchange和Queue建立关系,把交换机和消息队列通过路由规则绑定起来,可以是多对多关系
Connection:网络连接
Channel:信道,消息队列和外部联系都是通过信道。消息通过Channel塞进队列或流出队列。客户端每个连接中,可有多个Channel。
因为系统中建立和销毁TCP连接都是非常昂贵的开销,因此引入信道,来复用一条TCP连接。
Consumer:消费者,从消息队列中获取消息的客户端程序
Virtual Host:虚拟主机,表示一批交换器、消息队列和相关对象。一个broker中可以有多个host,用作不同用户的权限分离
Broker:消息队列服务器实体,维护一条生产者到消费者的路线,保证数据从按照指定方式传输。
通信过程:
生产者(Producer)生产消息,消息通过Channel(信息)发送给Broker(服务器主体),Broker再分配给一个Exchange(交换机)
Exchange收到消息后,根据消息中的routingkey(路由键),将消息转发给匹配的QUEUE(消息队列)
QUEUE收到消息后,将消息通过Channel(信道)发送给消费者(Consumer)
Consumer收到消息后,发送ACK(确认标志)给消息队列确认收到消息
Queue收到ACK后,删除消息队列中缓存的此条消息
rabbitmq运行机制
AMQP中消息的路由:
过程和JMS存在一些差别,AMQP中引入了Exchange和Binging的概念
生产者将消息发布道Exchange上,消息最终达到队列并被消费者接收,Binding决定了交换器的消息应该发送道那个队列。
Exchange类型:
分发消息时,根据类型的不同分发策略有区别,目前共计4中:direct、fanout、topic、headers。headers和direct完全一致,但性能差很多,几乎用不到。
Direct Exchange:消息中的路由键和Binding中的binding key一致(完全匹配),交换器就将消息发到对应的队列中。完全匹配,单播的模式
Fanout Exchange:每个发到Fanout Exchange上的消息都会分发到所有绑定的队列上,不处理路由键,转发消息最快
Topic Exchange:通过模式匹配分配消息的路由键属性,将路由键和某个模式匹配,此时队列需要绑定再一个模式上。
RabbitMQ支持的消息类型
直连模式:生产者-消息队列-消费者
work queue工作模式:也成为Task Queues 任务模型。
当处理消息较多时,可能生产消息速度远大于消费速度,消息队列中消息会越积越多,无法及时处理,此时可使用work模型,让多个消费者绑定道一个队列,共同消费队列中的主体。
队列中的消息一旦消费,就会消失,不会重复消费
生产者-消息队列-多个消费者
Publish/Subscribe 发布/订阅模式 (广播模式)
fanout 扇出,也称广播
广播模式中,消费发送流程是:可有多个消费者;每个消费者有自己的队列;每个队列都要绑定同一个交换机;
生产者发送消息,只能发到交换机,交换机决定发到那个队列,生产者无法决定
交换机将消息发送给绑定的所有队列
队列的消费者都能拿到消息,实现1个消息被多个消费者消费
总结:一个生产者绑定一个交换机,1个交换机绑定多个队列,每个队列都有对应的消费者,有几个消息队列就有几个消费者。
从而实现交换机将消息发给所有消息队列,1个消息被多个消费者接收
Routing 路由模式
Exchange类型为Direct,可实现不同消息被不同的队列消费
在direct 模型下,
消息发送方向exchange发送消息时,必须指定消息的routingkey
exchange和队列的绑定,不是任意绑定,而是指定一个routingkey(路由key),
exchange不再将消息平等地给每一个队列,而是根据消息的routingkey进行判断,只有队列的routingkey和消息的routingkey完全一致,才会将消息给到指定队列
总结:生产者向exchange发送消息指定routingkey,exchange收到消息后,将消息递交给与routingkey完全匹配的队列;每个队列都有各自的消费者;每个消费者消费各自绑定队列的消息
Topics模式
Exchange的类型为topic,可根据消息中的routingkey匹配道不同的队列,区别在于routingkey绑定队列时,可使用通配符进行模糊匹配。
此模式routingkey一般由1个或多个单词组成,单词之间由于"."分隔。例如item.insert
通配符:*(匹配1个词),#(匹配1个或多个词)
RPC模式
客户端远程调用服务方法,使用MQ可以实现RPC的异步调用,基于Direct交换机实现,流程如下:
客户端既是生产者又是消费者,向RPC请求队列发送RPC调用消息,同时监听RPC响应队列
服务端监听RPC请求队列的消息,收到消息后执行服务端的方法,得到方法返回的结果
服务端将RPC方法返回的结果发送至RPC响应队列
客户端监听RPC响应队列,接收到RPC响应结果
安装rabbitmq
springboot整合rabbitmq
目的(核心使用方法):使用RabbitTemplate发送和接收消息
实现步骤r
创建项目,引入spring for rabbitmq的依赖;
创建RabbitAutoConfiguration 自动配置类,并配置自动连接工厂ConnectionFactory
创建RabbitProperties配置类,封装RabbitMQ的配置
RabbitTemplate给RabbitMQ发送和接收消息
使用@RabbitListener和@EnableRabbit来监听消息队列
AmqpAdmin是RabbitMQ系统管理功能组件(在代码中创建交换机、消息队列、绑定规则)
rabbitmq入门程序
发送端程序:创建连接,创建通道,声明队列,发送消息
接收端程序:创建连接,创建通道,声明队列,监听队列,接收消息
work queue工作模式
两个消费端共同消费同一个消息队列中的数据
应用场景:对于任务较重或任务较多情况下,使用工作队列可以提高任务处理速度
实现关键:多个消费者监听同一个队列,1条消息只会被1个消费者接收,rabbitmq采用轮询方式将消息平均发给消费者,消费者处理完某条消息后,才会收到下条消息
Publish/Subscribe 发布订阅模式
每个消费者监听自己的队列;生产者发送消息给broker后,由交换机将消息转发到绑定此交换机的每个队列,每个绑定交换机的队列都可以接收到数据
实现关键:交换机将消息发送给绑定交换机的所有消息队列
Routing 路由模式
每个消费者监听自己的队列,并设置routingkey;生产者发送消息给交换机,交换机根据routingkey发送消息给指定的消息队列,从而实现不同消费者消费不同的消息
实现关键:交换机和队列绑定指定路由键,发送消息时指定路由键,则发给指定的队列。从而实现指定消费者消费发给指定队列的消息
Topic模式
每个消费者监听自己的队列,并设置带有通配符的routingkey;生产者发送消息给beoker,交换机根据routingkey发送消息给指定的消息队列,从而实现不同消费者消费不同的消息
实现关键:交换机给队列绑定指定含通配符的路由键,发送消息时指定路由键,则发给指定的队列,从而实现消费者消费发给指定队列的消息
Header模式
与routingkey区别在于,取消了routingkey,使用header中的key/value匹配队列
示例:根据用户的通知设置去通知用户,设置只接收email的用户只接收email,只接收sms的用户只接收sms。但是性能较低,故使用较少
RPC
客户端远程调用方法,使用MQ可实现RPC的异步调用,基于Direct交换机实现,流程如下:
客户端既是生产者又是消费者,向RPC请求队列发送RPC调用消息,同时监听RPC响应队列
服务端监听RPC请求队列的消息,收到消息后执行服务端的方法,得到方法返回的结果
服务端将RPC方法返回的结果发送至RPC响应队列
客户端监听RPC响应队列,接收到RPC响应结果
springboot整合rabbitmq
创建rabbitmq配置类,类中定义队列常量、交换机常量、路由键常量,声明交换机、声明队列、绑定交换机和队列
启动类添加注解@EnableRabbit开启支持
消息接收方法上使用@RabbitListener(queue={队列名称})监听指定队列