消息队列和分布式定时任务 | 青训营笔记

973 阅读7分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第12篇笔记

GO-学习笔记

消息队列

  • Message Queue,保存消息的容器,本质上是个队列,需要支持高吞吐,高并发,高可用

kafka

  • 日志信息、Metrics(运行情况)数据、用户行为
  • 使用
  1. 创建集群
  2. 新增topic,逻辑队列
  3. 生产者
  4. 消费者
  • zookeeper 负责存储集群元信息,分区分配信息 image.png

基本概念

  • topic,逻辑队列,每个逻辑队列中有多个partition,存放真正的消息
  • offset,消息在partition内的相对位置,作为唯一ID
  • replica,每个partition有多个副本,保证容灾
  1. leader 负责写入
  2. follower,不断同步leader
  • ISR,In Sync Replicas,如果副本和leader差距过大,将不会同步;在ISR中,leader发生宕机,在ISR中选出新leader
  • broker,集群的一个节点 image.png

生产端

  • 通过Batch,批量发送消息,减少IO次数,加强发送能力
  • 通过压缩,减少消息大小
  • 顺序写入,提高写入效率,减少寻道时间

接收端

  • 内部通过二分查找目标消息文件

对于时间索引,增加时间戳作为二级索引

  • 通过零拷贝,传输数据,通过DMA拷贝,从磁盘拷贝出来,再通过SG-DMA直接拷贝到网卡,发送给消费者进程
  • partition的分配
  1. 手动分配,手动为consumer配置处理哪几个partition,不能自动容灾
  2. 通过coordinator,自动分配,进行rebalance;coordinator也是集群里的某个节点;在consumers中选择一个leader确定分配方案,通过coordinator将分配方案告诉其他consumer

缺点

  • 每一个broker都有topic的分区,需要保持同步
  1. 重启机器后,需要同步数据,影响topic可用性
  • 负载不均衡,某个partition过多,迁移消耗大量IO资源
  • 没有自己的缓存,完全依赖PageCache

BMQ

  • 兼容Kafka,存算分离,云原生消息队列 image.png
  • proxy/broker,无状态,秒级重启节点
  • HDFS,分布式文件式的写入,每次写入都随机选择部分节点(dataNode)进行写入
  • 状态机,保证任意分片同一时刻只能在一个broker上存活
  1. revoer,选择一个节点进行写入,进行节点恢复和数据同步
  2. failover,DataNode宕机,选择一个新的节点 image.png
  • 写入流程
  1. 可以修改返回策略
  2. build index建立索引
  3. checkpoint,告诉系统数据已经完全写入到某个offset image.png
  • 读取流程
  1. wait等待机制,proxy没有数据的时候,间隔一段时间再请求 image.png

高级特性

  • 泳道消息,对每一个topic都有附带topic,配置都一样,并隔离资源;消费者只消费对应的泳道消息
  1. BOE Bytedance Offline Environment,完全独立的线下机房环境
  2. PPE Product Preview Environment,产品预览环境
  • Databus
  1. 降低开发难度,简化消息队列客户端复杂度
  2. 解耦业务和Topic
  • Mirror
  1. 跨地区的业务部署
  2. 通过mirror异步写入其他region的BMQ
  • Index,直接在BMQ中将数据结构化,通过消息的Index查询日志信息
  • Parquet,列式存储格式,列挨着存储,提高查询列的效率

RocketMQ

  • 跟Kafka的对比 image.png
  • 架构
  1. NameServer,作为路由,指出MessageQueue在哪个副本上 image.png
  • 存储模型,密集索引,每个消息都有一个索引 image.png

高级特性

  • 事务场景,异步处理多个业务,通过事务保证业务之间的关联

订单发起业务中,库存/订单/通知商家,是在一个业务中的

  • 延迟发送,提前发送给消息好,并设置可以被消费的时间,定时会被消费处理

先发送给ScheduleTopic,ScheduleMessage通过轮询,到达时间点后回写给CommitLog,正常提交进行消费

  • 消费重试和死信队列
  1. 消费失败,进行重试,没有达到重试次数,延期投递,发送给RetryTopic,再进行消费
  2. 达到重试次数,发送到死信队列,进行记录

定时任务

  • Linux,CronJob命令
  • Java,Timer/ScheduledExecutorService复用任务线程
  • GO,Ticker
  • 触发
  1. 定时发生
  2. 延时发生
  3. 周期发生

分布式定时任务

  • 支持海量数据
  • 是一个平台,把分散的、可靠性差的定时任务纳入统一的平台,并实现集群管理调度和分布式部署的一种定时任务的管理方式
  1. 单机任务,一台机器执行
  2. 广播任务,所有机器执行同一个任务
  3. Map任务,一个任务分为多个子任务,一个机器负责一部分的计算
  4. MapReduce热舞,对所有子任务进行汇总计算
  • 现有框架
  1. Xxl-job 美团
  2. SchedulerX 阿里
  3. TCT 腾讯

实现原理

  • 要解决触发、调度、执行
  1. 触发器,Trigger,解析任务,生成出发事件
  2. 调度器,Scheduler,分配任务,管理任务生命周期
  3. 执行器,Executor,获取执行任务单元,执行任务逻辑
  4. 控制台,Admin,提供任务管理和干预
  • 数据流 image.png
  • 细节架构 image.png

控制台

  • Job,任务,任务元数据
  1. 基础信息
  2. 调度时机
  3. 执行行为
  4. 执行方式
  • JobInstance,任务实例,一个任务,可以有多个任务实例
  1. JobId,绑定任务
  2. 触发时间
  3. 状态/结果
  4. 过程信息,通过消息队列进行调度,可以通过消息ID进行追回
  • JobResult,任务实例的运行结果

一个任务实例,可以有多种结果,出错需要重试

  • JobHistory,任务历史

存储历史任务

触发器

  • 需要解析触发规则,在规定的时间点触发任务
  1. 支持大量任务
  2. 支持秒级的调度
  3. 周期任务需要多次执行
  4. 保证秒级扫描的高性能,避免资源浪费
  • 定时扫描+延时消息
  1. 从db获取将要执行的任务
  2. 通过消息队列发送延时消息 image.png
  • 时间轮,Quartz
  1. 环形队列,底层采用数组存储,每个元素可以存放一个定时任务列表,根据时间取对应的列表
  2. 链表/最小堆/时间轮
  3. 多级时间轮,根据时分秒三种时间轮,分配到不同的列表
  • 高可用
  1. 不同业务,需要进行资源隔离
  2. 部署,需要多机房集群化部署
  3. 数据库行锁,保证任务只会被触发一次;多台机器频繁竞争锁,性能较差
  4. 分布式锁,抢占分布式锁

调度器

  • 资源来源/资源调度/任务执行
  • 业务系统提供机器资源
  1. 任务执行逻辑与业务系统共用资源,利用率高
  2. 定时任务脚本影响在线服务,不能由定时任务平台控制扩缩容
  • 定时任务平台提供机器资源
  1. 任务执行逻辑与业务系统提供的在线服务隔离,避免互相影响;支持扩缩容
  2. 消耗更多机器资源,需要额外为定时任务平台申请接口权限,不能直接继承业务系统的权限
  • 节点选择
  1. 随机节点执行,定时对账
  2. 广播执行,广播到所有节点上执行,批量运维
  3. 分片执行,任务拆分
  • 任务编排,任务之间有同步执行关系

使用无向图进行任务编排

  • 故障转移,一致性Hash,策略分发任务,如果失败,选择另外的机器执行
  • 高可用
  1. 靠消息队列的重试机制保障任务一定会被调用

执行器

  • 基于注册中心,可以让调度器调度
  • 心跳机制,定时状态上报

业务应用

  • 电商/互动/游戏
  • 发货超过十天未收货,自动确认收货
  1. 分布式定时任务的延时任务
  2. 消息队列的延时消息
  • 集卡
  1. 分布式定时任务的MapReduce任务
  2. 大数据离线处理引擎Hive
  3. 大数据实时处理引擎Flink
  • 方案对比 image.png