01 RabbitMQ介绍
MQ:
消息队列;
RabbitMQ:
MQ的框架消息队列系统,实现消息的发送和接收,一种消息代理和队列的服务器;
AMQP(Advanced Message Queuing Protocol):
高级消息队列协议。提供统一消息服务的应用层标准高级消息队列,是应用层协议的一个开放标准,为面向消息的中间件设计;
AMQP的主要特征是:
面向消息、队列、路由、安全性、可靠。
02 工作模式
消息:
包括两个部分:有效载荷(你想要传输的数据)和标签(描述有效载荷,并决定了谁将获取消息);
生产者(消费者):
发送消息到消息队列;
消费者:
从消息队列接收消息;
队列:
存储着即将被消费的消息, 在创建队列时,要指定队列名称;
交换机交换机和队列:
持久化:将交换机和队列的数据保存在磁盘上,服务器重启或宕机之后仍然存在;
非持久化:将交换机和队列的数据保存在内存上,服务器重启或宕机后不存在(性能高于持久化);
根据需求来确定是否需要持久化,上图可以看出,针对每一种交换机有不同的type。
交换机的类型:
默认交换机
直连交换机(direct)
主题交换机(topic)
头交换机(headers)
扇形交换机(fanout)
消息确认:
自动确认:消息发送给应用后立即删除;
显示确认:待应用发送一个确认回执后再删除消息;
03 队列模型
点对点消息队列模型:1对1模型
特点:
消息一旦被消息,消息队列就被删除;
生产者和消费者不存在时间上的依赖性;
消费者接收成功后,需要向队列应答成功;
发布订阅消息模型:n对n模型
特点:
每个消息可以有多个消费者,发布的消息可以被多个消费者消费;
发布者和订阅者有时间上的依赖关系;
针对每个主题的订阅者,必须创建一个订阅者之后,才能消费发布者的消息;
为了消费消息,订阅者必须保持订阅状态;
04 消息队列问题
1)消息不丢失问题
消费者挂掉消息丢失--消费者可以通过AMQP 的basic.ack 命令显式地向RabbitMQ 发送一个确认;
或者在订阅到队列的时候就将auto_ack 参数设置为true 。当设置了auto_ack=true 时,一旦消费者接收消息,RabbitMQ 会自动视其确认了消息。而当auto_ack=false时,需要消费者通过确认命令告诉RabbitMQ 它
已经正确地接收了消息,同时RabbitMQ才能安全地把消息从队列中删除;
Rabbitmq自己挂掉消息丢失--RabbitMQ 确保持久性消息能从服务器重启中恢复的方式是,将它们写入磁盘上的一个持久化日志文件。当发布一条持久性消息到持久交换器上时, Rabbit 会在消息提交到日志文件后才发送响应;
2)异步处理
引用应用场景:用户注册信息后,写入数据库,需要发送注册短信和注册邮件。
传统串行方式:300ms,1s可处理3次请求;
传统并行方式:200ms,1s可处理5次请求;
消息队列:105ms,1s可处理9次请求;
3)应用解耦
在项目启动之初是很难预测未来会遇到什么困难的,消息中间件在处理过程中插入了一个隐含的,基于数据的接口层,两边都实现这个接口,这样就允许独立的修改或者扩展两边的处理过程,只要两边遵守相同的接口约束即可。
传统模式:两个系统耦合,相互依赖,一个系统出现问题,则都会失败;
消息队列:发货系统发货后,发货系统完成持久化处理,将消息写入消息队列,返回发货成功,订单系统订阅发货的消息,获取发货信息,订单系统根据信息,进行更新操作。发货系统在发货的时候不用关心后续操作了,如果订单系统不能正常使用。也不影响正常发货,实现订单系统与发货系统的应用解耦;
4)流量削峰
对于一些类似淘宝双11这种秒杀活动,访问量剧增,瞬时间流量过大,服务器可能承受不了那么大的流量压力,导致服务宕机,使用消息中间件采用队列的形式,将压力转移到消息队列上,可以减少突发访问压力,不会因为突发的超时负荷要求而崩溃,起到缓冲作用。
5)消息队列积压
ready准备状态,如果消息数比较多,认为是消费者的处理能力不足,通过增加消费者来可以解决;
unacked未确认状态是因为,消费者取走消息后没有及时做消息确认,没有收到ack的应答,对于开启手动确认机制的,不进行ack则消息会一直以unacked状态留在队列中;
或者是消费者处理能力不足。生产者投放消息的速度较快,当消费者按照prefetch_count设置的值取走相应数量的消息时,这些消息都会暂时处于unacked状态;
当积压很多的时候会导致程序无法消费数据,这时候监控系统就会提示我们了,然后运维或开发自己发现后,就要迅速解决积压问题(有一次提交代码的时候有一个值没有处理好,导致监控飙升)
解决办法:
增加消费者的处理能;
考虑使用队列最大长度限制;
给消息设置年龄,超时就丢弃;
默认情况下,rabbitmq消费者为单线程串行消费,设置并发消费两个关键属性concurrentConsumers和prefetchCountoncurrentConsumers设置的是对每个listener在初始化的时候设置的并发消费者的个数,prefetchCount是每次一次性从broker里面取的待消费的消息的个数;
建立新的queue,消费者同时订阅新旧queue,采用订阅模式;
生产者端缓存数据,在mq被消费完后再发送到mq,打破发送循环条件,设置合适的qos值,当qos值被用光,而新的ack没有被mq接收时,就可以跳出发送循环,去接收新的消息;消费者主动block接收进程,消费者感受到接收消息过快时主动block,利用block和unblock方法调节接收速率,当接收线程被block时,跳出发送循环。
新建一个topic,partition是原来的10倍;然后写一个临时的分发数据的consumer程序,这个程序部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写入临时建立好的10倍数量的queue;接着临时征用10倍的机器来部署consumer,每一批consumer消费一个临时queue的数据;等快速消费完积压数据之后,得恢复原先部署架构,重新用原先的consumer机器来消费消息(目前采用过这种方法来应对紧急消息堆积情况);
6)消息的事务支持
即是原子性的,一次处理的多个消息应该在一个事务支持范围内,一个消息失败则即回滚到消息队列中去;
7)消息的持久化
启用消息持久化后,消息队列宕机重启后,消息可以从持久化存储恢复,消息不丢失,可以继续消费处理;
8)消息队列的高可用性
遇到宕机、重启等情况,消息队列可能无法服务,所以需要消息队列的高可用的支持,可以采用Rabbitmq的镜像集群模式;