目录
1消息队列的作用与使用场景
- 异步:批量数据异步处理。例:批量上传文件,比如代发、代扣文件。
- 削峰:高负载任务负载均衡。例:电商秒杀抢购。
- 解耦:串行任务并行化。例:退货流程解耦。
- 广播:基于Pub/Sub实现一对多通信。
2多个消费者监听一个队列时,消息如何分发
- Round-Robin(轮询)
- 默认的策略,消费者轮流、平均地收到消息。
- Fair dispatch(公平分发)
- 如果要实现根据消费者地处理能力来分发消息,给空闲地消费者发送更多消息,可以用basicQos(int prefetch_count)来设置。prefetch_count地含义:当消费者有多条消息没有响应ACK时,不再给这个消费者发送消息。
3无法被路由的消息,去了哪里
如果没有任何设置,无法路由的消息会被直接丢弃。
无法路由的情况:Routing key不正确。
解决方案:
- 使用mandatory=true配合ReturnListener,实现消息回发。
- 声明交换机时,指定备份交换机。
4消息在什么时候会编程Dead Letter(死信)
- 消息被拒绝并且没有设置重新入队:(NACK || Reject ) && requeue == false
- 消息过期(消息或者队列的TTL设置)
- 消息堆积,并且队列达到最大长度,先入队的消息编程DL。
解决方案:可以在声明队列时,指定一个Dead Letter Exchange,来实现Dead Letter的转发,保证消息不会丢失。
5RabbitMQ如何实现延时队列
- 利用TTL(队列的消息存活时间或消息存活时间),加上死信交换机。
- 先保存消息到数据库,用调度器扫描发送(时间不够精准)。
6消息幂等性
- Broker本身没有消息重复过滤的机制
- 生产者方面,可以对每条消息生成一个msgId,以此控制消息重复投递。
// 消息属性
AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
.messageId(String.valueOf(UUID.randomUUID()))
.build();
// 发送消息
channel.basicPublish("", QUEUE_NAME, properties, msg.getBytes());
- 消费者方面,消息体(比如json报文)中必须携带一个业务ID,比如交易流水号,消费者也可以根据业务ID去重,避免重复消费。
7如何在服务端和消费端做限流
- 网关/接入层:其他限流方式。
- 服务端(Broker):配置文件中内存和磁盘的控制:队列长度无法实现限流。
- 消费端:prefetch_count。
8如何保证消息的顺序性
对于一个对消息顺序要求严格的场景。
一个队列只有一个消费者的情况下才能保证顺序,否则只能通过全局ID来实现。
- 每条消息有一个msgId,关联的消息拥有同一个parentMsgId。
- 可以在消费端实现前一条消息未消费,不处理下一条消息
- 也可以在生产端实现前一条消息未处理完毕,不发布下一条消息。
9RabbitMQ的集群模式和集群节点类型
集群模式:
- 普通模式:缺省模式,以两个节点(rabbit01、rabbit02)为例来进行说明。
- 对于Queue来说,消息实体只存在于其中一个节点rabbit01(或者rabbit02),rabbit01和rabbit02两个节点仅有相同的元数据,即队列的结构。
- 当消息进入rabbit01节点的Queue后,consumer从rabbit02节点消费时,RabbitMQ会临时在rabbit01、rabbit02之间进行消息传输,把A中的消息实体取出并经过B发送给consumer。所以consumer应尽量连接每一个节点,从中取消息。
- 即对于同一个逻辑队列,要在多个节点建立物理Queue,否则无论consumer连接rabbit01或者rabbit02,出口总在rabbit01,会产生瓶颈。当rabbit01节点故障后,rabbit02节点无法渠道rabbit01中还未消费的消息实体。
- 如果做了消息持久化,那么需要等到rabbit01节点恢复,然后才可以被消费;如果没有持久化的话,就会产生消息丢失的现象。
- 镜像模式:把需要的队列做成镜像队列,存在与多个节点属于RabbitMQ的HA方案。
- 该模式解决了普通模式中的问题,其实质和普通模式不同之处在于,消息实体会主动在镜像节点间同步,而不是在客户端取数据时临时拉取。
- 该模式带来的副作用也很明显,除了降低系统性能外,如果镜像队列数量过多,加之大量的消息进入,集群内部的网络带宽将会被这种同步通讯大大消耗。所以在对可靠性要求高的场合中适用。
节点:
- 内存节点(RAM):保存状态到内存(但持久化的队列和消息还是会保存到磁盘)。
- 磁盘节点(DISC):保存状态到内存和磁盘。
注:一个集群中至少需要一个磁盘节点。