MQ相关学习

基础篇

消息队列本质:一发一存一消费。

最原始模型包含两个关键词:消息和队列。生产者将消息传递给队列的容器,然后再从容器中取出消息,转发给消费者。

常用的消息队列产品都在最原始模型上做了扩展,同时提出新名词:topic,partition,queue等

  • 队列模型:允许多个生产者往同一队列发送消息,但是如果有多个消费者,实际上是竞争关系,一条消息只能被一个消费者接收,读完即删除。

  • 发布-订阅模型:如果需要一份消息数据分发给多个消费者,每个消费者都要求收到全量消息。可行的方案是为每个消费者创建单独的队列,让生产者发送多份。同一数据复制多份浪费空间。为解决此问题就演化出发布订阅模型。存放消息的容器变为topic主题,订阅者在接收消息之前需要先订阅主题。每个订阅者都可以收到同一topic下的全量消息。和队列模型不同在于消息是否可以被多次消费。

MQ应用场景:系统解耦,异步通信,流量削峰。初次之外还可以延迟通知,最终一致性保证,顺序消息和流式处理等。

引入MQ之后之前一次RPC变成两次,生产者只和队列耦合,无需知道消费者的存在。多了中间节点队列进行消息转储,将同步变成异步。

如电商中订单业务中订单支付场景:支付成功后,需要更新订单状态,更新用户积分,通知商家新订单,更新推荐系统中用户画像等。 引入MQ之后只需要关注其最主要的流程:更新订单状态。其他全部交给MQ来通知。

  • 系统解耦:后续业务再扩展也不涉及核心流程的稳定性,降低维护成本;
  • 异步通信:其他步骤变成异步执行,减少订单支付的整体耗时,提升订单系统吞吐量;
  • 流量削峰:队列转储消息,对于超负载场景可以使用MQ作为漏斗进行限流;
  • 顺序投递,延时消费:利用队列本身的顺序性满足顺序消费场景,叠加定时任务完成消息的延时消费;

如何设计MQ:

  • 简单版:两次RPC+消息转储。利用成熟的RPC框架Dubbo或Thrift实现发消息和读消息接口;消息放在本地内存中,利用jdk自带的ArrayBlockingQueue
  • 适用生产环境:如何满足高性能、高可靠等非功能性需求。
    • RPC通信:直接利用成熟的RPC框架,不需要考虑服务注册与发现、负载均衡、通信协议、序列化方式等;也可以基于netty做底层通信,用zookeeper、Euraka等做注册中心,然后自定义通信协议,或者基于AMQP标准化的MQ协议来实现。后者定制化能力和优化空间更大。
    • 高可用:Broker高可用只需要保证可水平扩展集群部署,通过服务自动注册与发现、负载均衡、超时重试机制、发送和消费消息时的ack保证;存储高可用有两个思路1)kafka的分区+多副本模式,要考虑分布式下数据复制和一致性方案,类似zab、raft协议,并实现自动故障转移;2)用主流的DB、分布式文件系统、带持久化能力的kv系统;
    • 存储设计:追加写日志文件+索引文件的方式,索引设计可以考虑稠密或稀疏,查找消息可以利用跳表、二分查找等,还可以通过OS的页缓存,zero-copy技术提升磁盘读写;
    • 消费关系管理:基于Zookeeper,Apollo等配置中心管理
    • 高性能:Reactor网络IO模型,业务线程池,生产者批量发送,broker异步刷盘,消费端批量拉取等

Kafka神秘面纱

Kafka是linkedin内部孵化项目,设计用于处理1)运营活动场景:记录用户的浏览、搜索、点击、活跃度等行为;2)系统运维场景:监控CPU、内存、请求耗时等性能指标。两种数据都属于日志范畴,热点是数据实时生产,数据量大。Apache Kafka 是一个开源的分布式流处理平台。

演化过程:

  • 将一份消息分发给多个消费者,每个消费者获取全量的消息。即广播
  • 并非每个消费者都需要全部数据,MQ不理解消费语义无法分类投递。将难题抛给生产者,发送消息时进行逻辑分类,即利用topic
  • 多个消费者都对同一个topic感兴趣,如果采用传统的队列模式,取出消息后立即删除,另一个消费者就拿不到。很自然想到当topic增加消费者时复制数据队列。但是复制成本会导致性能问题。
  • 将所有消息进行持久化存储,由消费者自己各取所需,只需要传递一个消息的offset即可。将复杂的消费问题转嫁给消费者,使得kafka本身复杂度降低,为高性能和高扩展性打下基础。

kafka架构设计

海量消息存储问题就是kafka架构设计中最大的难点。容易想到方案:对数据进行分片存储。

常见分片存储场景:1)数据库中分库分表;2)缓存设计中分片集群架构;类似拆分思想在HDFS,ES中都能看到。

kafka也采用partition分区进行水平拆分。分区路由可以简单理解为hash函数。分区规则设定合理可以将消息均匀分配到不同分区中。

消费者和partition相结合做到并行处理需要满足两个基本诉求:1)广播消费能力:同一个topic可以被多个消费者订阅,一条消息可以被消费多次;2)集群消费能力:当消费者本身也是集群时,每条消息只能发送给集群中一个消费者进行处理。

kafka引入消费组的概念,组间进行广播消费,组内进行集群消费。并且限制每个 Partition 只能由消费组中的一个消费者进行消费。

存储可扩展、消息并行处理都解决后,对于高可用设计,kafka集群利用多副本机制实现自动故障转移能力。同一分区不同副本保存相同消息,副本之间一主多从,leader负责读写请求,follower只负责和leader同步,当leader故障时有机会被选举为新的leader并对外提供服务。