消息队列-RabbitMQ

1. 消息队列

1.1 优缺点

答:总结为:

优点:

  • 解耦。系统生产消息后,直接给MQ不用关心其他事务。
  • 异步。异步执行,提高吞吐量。发送者将消息发送给消息队列后,不需要同步等待接收者处理完毕,而是可以进行其它操作。
  • 削峰。请求在MQ中,Server根据处理能力处理消息,缓解服务器压力。

缺点:

  • 系统可用性降低。MQ挂了,整个系统通信GG。
  • 系统复杂度增加。加入MQ,引出一致性、传输可靠性、消息不被重复消费等等问题。
  • 一致性问题。A处理结束返回,BC写库成功,D失败,数据不一致。

1.2 消息中间件

答:主要是ActiveMQ、RabbitMQ、RocketMQ和KafKa。

  • ActiveMQ:老技术,现在用得不多。
  • RabbitMQ:开源、社区活跃,中小型用这个。
  • RocketMQ:阿里开发,和Dubbo RPC框架很像。
  • Kafaka:专门做大数据。

1.3 MQ常见问题和解决思路

  1. 消息顺序问题:让消息按发送顺序被消费。
  • 解决方法:保证生产者-MQ-消费者,一一对应。合理设计规避。
  • 缺陷问题:吞吐量不够;耦合度太高。从业务层面保证消息顺序。
  1. 消息重复问题:因为网络问题导致消费者收到两条一样的消息。
  • 解决方法:保证幂等性,即不管多少重复消息,最后处理结果还是一样。通过唯一编号标识消息或者日志表记录去重

2. RabbitMQ

RabbitMQ是一款开源的、erLang编写,基于AMQP(Advanced Message Queuing Protocol)的消息中间件。

2.1 基本概念

消息队列-RabbitMQ

  • Producer(生产者):生产消息的一方(邮件投递者);
  • Consumer(消费者):消费消息的一方(邮件收件人);
  • Broker:MQ服务器实体,一般认为一个Server就是一个Broker;
  • Routing Key:路由键,指定消息的路由规则;
  • Exchange:消息交换器,根据路由键把消息分发到队列中;
  • Queue:消息队列容器,保存消息直到发送给消费者,每个消息可以投入到一个或多个队列。;
  • Binding:绑定,把exchange和queue按照路由规则绑定起来;
  • VHost:理解为虚拟broker(mini-RabbitMQ),拥有独立的组件和权限系统,可以做到vhost范围的用户控制,所以一般应用于权限隔离。
  • Channel:消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务。

由Exchange、Queue、RoutingKey三个才能决定一个从Exchange到Queue的唯一的线路

2.2 工作模式

2.2.1 Simple简单收发

  1. 生产者生产消息,把消息放进队列;
  2. 消费者监听队列消息,有就消费掉。

2.2.2 Work竞争资源

  1. 生产者生产消息,把消息放进队列;
  2. 多个消费者监听同一队列,竞争消费,谁抢到谁就消费。
  3. 可能导致重复消费问题。

2.2.3 Publish/Subscribe共享资源

  1. 生产者将消息发给broker,由交换机转发到队列中;
  2. 每个消费者监听自己的队列。

2.2.4 routing路由模式

  1. 生产者将消息发给broker,指定路由规则,交换机根据路由key转发到对于的消息队列;
  2. 只有对应的消费者才能消费消息。

2.2.5 topic主题模式

  1. 基于路由模式的模糊匹配,*匹配多个单词,#匹配一个单词(类似sql的模糊查询)

2.3 消息相关问题

2.3.1 消息顺序性

答:通常可以用两种方法:

  • 拆分为多个queue,queue和consumer一对一
  • queue唯一consumer多个,consumer内部排队分发

2.3.2 消息幂等性

答:解决思路就是,对消息做唯一标识,根据标识判断是否被消费。

2.3.3 消息传输方式

答:RabbitMQ使用信道传输数据,信道基于TCP连接,但不受数量限制,一条TCP连接上信道能无上限。

2.3.4 消息确认模式

答:分为两种确认模式。

  1. 发送方确认模式
  • 消息被分配唯一ID,当消息投递到队列后,信道发送ack给生产者,出错就发送nack。
  • 异步,在等待确认过程中仍能继续发消息。
  1. 接收方确认模式
  • 消费者每接收一条消息就确认一次。当消息被确认,才从队列中删除。
  • 没有超时机制。只要consumer连接不中断,就认为消费者一直在处理。

2.3.5 消息丢失

答:分为三种情况。

  1. 生产者丢失消息
  • transaction事务机制:把消息的发送作为一个事务,过程中有异常就回滚,发送成功就提交。
  • confirm模式:就是发送方消息确认模式。
  1. 消息队列丢失消息
  • 一般都是在开启持久化时发生。
  • 和confirm机制配合使用,在消息投递队列并持久化后,再返回ack。
  1. 消费者丢失消息
  • 消费者接收消息后,处理消息前,回复MQ已收到。这时异常会导致消息丢失。
  • 修改为手动确认消息。

2.3.6 消息积压

答:处理方法是临时扩容。将queue和consumer资源扩大10倍(申请10倍的空间建立queue和10倍的机器部署consumer),积压消息消费完后,恢复原先架构。

2.3.7 消息失效

答:RabbitMQ能设置消息的TTL,一旦消息积压过久到达TTL就会被自动清理。解决方案是手动批量重导,手动将丢失数据,查出来并发送到MQ中。

2.3.8 延迟队列

消息/队列 TTL + 死信队列 DLX + Router转发队列

设置了TTL后,当消息在队列中变成死信,就被转发到其他队列中。

2.4 集群模式

答:RabbitMQ基于主从模式实现高可用,分为普通集群和镜像集群模式。

  1. 普通集群模式
  • 在多台机器上启动多个RabbitMQ实例。queue放在一个实例A上,但每个实例都同步queue的元数据。
  • 消费时,若连接到另一个实例B,则实例B会从实例A上拉取数据。

总结:集群的多个节点服务一个queue的读写操作

  1. 镜像集群模式
  • 每个RabbitMQ都有一个queue的完整镜像。每次写消息到queue时,自动把消息同步到多个实例上。
  • 好处是单点宕机不怕,有备份。坏处是开销大。

2.5 交换器类型

  • fanout:把消息不做判断全部放进队列。用来广播信息。
  • direct:把消息放进路由完全匹配的队列中。用来处理优先级任务。
  • topic:路由模糊匹配。
  • header:利用消息的header属性进行匹配。基本不用。