消息队列笔记

185 阅读6分钟

kafka

吞吐量高

Kafka 的生产者采用的是异步发送消息机制,当发送一条消息时,消息并没有发送到 Broker 而是缓存起来,然后直接向业务返回成功,当缓存的消息达到一定数量时再批量发送给 Broker 。这种做法减少了网络 io ,从而提高了消息发送的吞吐量,但是如果消息生产者宕机,会导致消息丢失,业务出错,所以理论上 kafka 利用此机制提高了性能却降低了可靠性。

日志采集

生态好;吞吐量高

作用

异步、削峰、解耦,消息通信

高可用

  • 由多个broker组成,每个broker是一个节点;
  • 一个topic可以划分成多个partition,每个partition存在不同的broker上;
  • 每个partition的数据都会同步到其他机器上,形成多个副本;每个副本都会推选出一个leader,其余都是follower;
  • 只能在leader上进行读写;leader会把数据同步到follower;
  • 如果某个broker宕机了,且这个broker上有某个partition的leader,此时会从follower中重新选举一个新的leader。
  • 写数据的时候,生产者只写leader,leader把数据落盘到本地磁盘,其他follower主动从leader pull数据、完成后发送ack给leader,leader收到后返回写成功消息给生产者
  • 消费的时候,只从leader读,只有当一个消息已经被所有follower都同步成功返回ack后,这个消息才会被消费者读到

保证消息不重复消费(幂等性)

  • 靠研发自己实现,通过对消息使用redis加锁

保证消息的可靠传输(不丢失

  • 消费者丢数据:关闭自动提交offset,会导致重复消费,需要代码保证幂等性
  • kafka丢数据:replication.factor:topic设置值大于1,保证每个partition至少由2个副本;min.insync.replicas:kafka服务端设置值大于1,要求leader至少感知到1个follower保持联系;acks=all:producer端设置,要求每条数据写入所有副本后才能认为写成功;retries=MAX:producer端设置,要求一旦写入失败就无限重试,会导致卡住

生产者丢数据:设置方式同上

如何保证消息的顺序性

  • kafka新建一个topic,假如有3个partition,生产者写的时候可以指定一个key(例如订单id),相关的消息都分发到同一个partition中,partition中的消息一定有序
  • 同一个partition能保证生产、消费有序,但如果是多线程并发处理消息、顺序可能错乱;写N个内存queue,具有相同key的数据都到同一个内存queue;对于N个线程,每个线程分别消费一个内存queue,从而保证顺序性。

大量消息在MQ里积压了几个小时怎么解决(紧急扩容

  • 先修复consumer的问题,确保其恢复消费速度
  • 新建一个topic,partition是原来的10倍,临时建立好原先10倍的queue数量
  • 写一个临时的分发数据consumer程序,用来消费积压的数据,消费之后不做处理、均匀轮询写入临时建立好的queue中
  • 临时征用10倍机器部署consumer,每批consumer消费一个临时queue的数据
  • 快速消费完积压数据后,恢复原先部署的架构,重新用原来的consumer机器来消费消息
  • 或者:优化消费逻辑,比如之前逐条消费改成批量处理;水平扩容,增加topic的队列数和消费组机器的数量,提升整体消费能力

消息过期失效

  • 大量数据会丢失,批量重导

快写满了

  • 临时写程序,接入数据来消费,消费一个丢弃一个,快速消费掉所有消息、然后补数据

技术选型

吞吐量高,ms级时延

在大数据的实时计算和日志采集中被大规模使用,是业界的标准

如何保证数据一致性,事务消息如何实现

  • 生产者产生消息,发送一条半事务消息到MQ服务器
  • MQ收到消息后,将消息持久化到存储系统,这条消息的状态是待发送状态。
  • MQ服务器返回ACK确认到生产者,此时MQ不会触发消息推送事件
  • 生产者执行本地事务
  • 如果本地事务执行成功,即commit执行结果到MQ服务器;如果执行失败,发送rollback。
  • 如果是正常的commit,MQ服务器更新消息状态为可发送;如果是rollback,即删除消息。
  • 如果消息状态更新为可发送,则MQ服务器会push消息给消费者。消费者消费完就回ACK。
  • 如果MQ服务器长时间没有收到生产者的commit或者rollback,它会反查生产者,然后根据查询到的结果执行最终状态。

怎么设计一个消息队列

  • 消息队列的整体流程:producer发送消息给broker,broker存储后再发送给consumer消费,consumer回复消费确认等。
  • RPC如何设计:producer发送消息给broker,broker发消息给consumer消费,那就需要两次RPC了,RPC如何设计呢?可以参考开源框架Dubbo,可以说说服务发现、序列化协议等等
  • broker如何持久化:是放文件系统还是数据库呢,会不会消息堆积呢,消息堆积如何处理呢。
  • 消费关系如何保存:点对点还是广播方式呢?广播关系又是如何维护呢?zk还是config server
  • 消息可靠性怎么保证:消息重复、幂等处理方式
  • 消息队列的高可用:多副本 -> leader & follower -> broker 挂了重新选举 leader 即可对外服务。
  • 消息的事务特性:与本地业务同个事务,本地消息落库;消息投递到服务端,本地才删除;定时任务扫描本地消息库,补偿发送。
  • MQ的伸缩性和可扩展性:如果消息积压或者资源不够时,如何支持快速扩容,提高吞吐?参照Kafka 的设计理念,broker -> topic -> partition,每个 partition 放一个机器,就存一部分数据。如果资源不够就给 topic 增加 partition,然后做数据迁移,增加机器

生产消费的方式

rocketMQ

单机吞吐量10W级

  • QPS:每秒钟请求的数量

  • 并发数:系统同时处理的请求数量

  • 吞吐量,单位时间内系统处理用户的请求数量

    • 业务角度,请求数量/秒
    • 网络角度,字节/秒

消息队列的消息模型

  1. 队列模型
  2. 发布订阅模型

Rocket的消息模型

标准的发布订阅模型

  1. message
  2. topic
  3. tag
  4. group
  5. message queue:如果一个consumer需要获取topic下所有消息,需要遍历所有message queue
  6. offset

消息的消费模式

  1. 集群消费:一个消费者组共同消费一个主题的多个队列,一个队列只会被一个消费者消费
  2. 广播消费

基本架构