消息队列基础知识
什么是消息队列
本处提到的消息队列是指各个服务以及系统组件/模块之间的通信,属于一种中间件。参与消息传递的双方称为生产者和消费者,生产者负责发送消息,消费者负责处理消息。
消息队列作用
- 通过异步处理,提高系统性能(减少响应所需时间)
- 削峰/限流
- 降低系统耦合性
消息队列如何降低耦合性
消息队列使用发布-订阅模式工作,消息发送者(生产者)发布消息,一个或多个消息接受者(消费者)订阅消息。 从上图可以看到消息发送者(生产者)和消息接受者(消费者)之间没有直接耦合,消息发送者将消息发送至分布式消息队列即结束对消息的处理,消息接受者从分布式消息队列获取该消息后进行后续处理,并不需要知道该消息从何而来。对新增业务,只要对该类消息感兴趣,即可订阅该消息,对原有系统和业务没有任何影响,从而实现网站业务的可扩展性设计。
消息队列带来的问题
- 系统可用性下降
- 系统复杂度提高
- 一致性问题
JMS Java消息服务
JMS(JAVA Message Service)API是一个消息服务的标准或者说是规范,允许应用程序组件基于JavaEE平台创建、发送、接收和读取消息。
JMS的两种消息模型
-
点到点(P2P)模型
使用队列(Queue)作为消息通信载体;满足生产者与消费者模式,一条消息只能被一个消费者使用,未被消费的消息在队列中保留直到被消费或超时。 -
发布/订阅(Pub/Sub)模型
使用主题(Topic)作为消息通信载体,类似于广播模式;发布者发布一条消息,该消息通过主题传递给所有的订阅者。
RPC与消息队列区别
- 用途:RPC是用于解决两个服务之间的远程通信问题,用于调用远程计算机上某个服务的方法;消息队列主要用来降低系统耦合性、实现任务异步、进行流量削峰。
- 通信方式:RPC是双向直接网络通讯,消息队列是单向引入中间载体的网络通讯。
- 架构:消息队列是需要把消息存储起来,RPC则没有相关需求。
- 请求处理的时效性:通过RPC发出的调配用一般会被处理,放在消息队列中的消息不一定会被处理。
RabbitMQ
RabbitMQ 如何保证信息不丢失
- 开启生产者确认机制,确保生产者的消息能到达队列
- 开启持久化功能,确保消息未消费前在队列中不会丢失
- 开启消费者确认机制为auto,由spring确认消息处理成功后完成ack
- 开启消费者失败重试机制,多次失败后将消息投递到异常交换机,交由人工处理
RabbitMQ消息重复消费问题怎么解决
- 设置一个唯一的标识符,处理消息时,先到数据库查询一下,如果不存在就正常处理,如果存在就不用再消费
- 幂等方案:利用redis分布式锁、数据库的锁等
RabbitMQ中的死信交换机?(RabbitMQ延迟队列了解过吗?)
当一个消息在一个队列中变为死信后,可以被重新发送到另一个交换机中,这个交换机就是死信交换机。导致死信的几种原因有:
- 消息被拒(Basic.Reject / Basic.Nack)且requeue = false
- 消息TTL过期
- 队列满了,无法再添加。
什么是延迟队列?RabbitMQ如何实现延迟队列?
延迟队列指的是存储对应的延迟消息,消息被发送以后,并不想让消费者立刻拿到消息,而是等待特定时间后,消费者才能拿到这个消息进行消费。
实现方式:
- 利用RabbitMQ的死信交换机和消息存活时间TTL来实现
- 安装死信插件(具体没用过)
RabbitMQ如果有100万消息堆积在MQ中,如何解决(消息堆积怎么解决)?
当生产者发送消息的速度超过了消费者处理消息的速度,就会导致队列中的消息堆积,直到存储消息达到上限。之后发送的消息就会变为死信,肯会被抛弃,这就是消息堆积。
解决消息堆积有三种思路
- 增加更多消费者
- 消费者使用线程池,多线程处理消息
- 使用惰性队列,扩大堆积上线
惰性队列
- 在声明队列的时候设置属性x-queue-mode为lazy,即惰性队列
- 基于磁盘存储,消息上限高
- 性能比较稳定,但基于磁盘存储,受限于磁盘IO,时效性会降低。
RabbitMQ的高可用机制
在生产环境中,使用集群来保证高可用性
普通集群
又称标准集群,具备以下特征:
- 会在集群的各个节点减共享部分数据,包括:交换机、队列元信息。不包含队列中的信息。
- 当访问集群某个节点时,如果队列不在该节点,会从数据所在节点传递到当前节点在返回
- 队列所在节点宕机,队列中的消息就会消失
镜像集群
本质是主从模式,具备下面的特征
- 交换机、队列、队列中的消息 会在各个mq的镜像节点之间同步备份
- 创建队列的节点被称为该队列的主节点,备份到的其他节点叫做该队列的镜像节点
- 一个队列的主节点可能是另一个队列的镜像节点
- 所有操作都是主节点完成,然后同步给镜像节点
- 主节点宕机后,镜像节点会替代成为新的主节点
仲裁队列
- 与镜像队列一样,都是主从模式,支持主从数据同步
- 使用简单,没有复杂配置,只需在声明队列的时候指定使用仲裁队列
- 主从同步基于Raft协议,强一致性
Kafka
Kafka如何保证消息不丢失
生产者发送消息到Brocker丢失
- 设置异步发送
消息在Brocker丢失
Kafka如何保证消费顺序性
kafka默认存储和消费信息,是不能保证顺序性的。因为一个topic数据可能存储在不同的分区中,每个分区都有一个按照顺序存储的偏移量。如果消费者关联了多个分区,不能保证顺序性。
但是通过把消息存储到一个分区下可以解决。方法有两种:1)发送消息时指定分区号。2)发送消息时,按照相同的业务设置相同的key。因为默认情况下,分区也是通过key的hashcode值来选择分区的。hash值如果一样的话,分区一定一样。