这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战
一、概述
如图:
Druid 总体包含以下 6 类节点:
Coordinator node: 主要负责历史节点的数据负载均衡, 以及通过规则管理数据的生命周期。协调节点告诉历史节点加载新数据、卸载过期数据、复制数据、 和为了负载均衡移动数据。
Coordinator 是周期性运行的(由 druid.coordinator.period 配置指定,默认执行间隔为 60s);
Coordinator 需要维护和 ZooKeeper 的连接, 以获取集群的信息。Segment 和 Rule 的信息保存在元数据库中, 所以也需要维护与元数据库的连接。
-
Overlord node: 进程监视MiddleManager进程, 并且是Druid数据摄入的主节点。负责将提取任务分配给MiddleManagers并协调Segement发布, 包括接受、拆解、分配 Task, 以及创建Task相关的锁, 并返回Task的状态。 -
Historical node: 加载生成好的数据文件, 以供数据查询。Historical node是整个集群查询性能的核心所在,Historical会承担绝大部分的segment查询。
Historical 进程从 Deep Storage 中下载 Segment,并响应有关这些 Segment 的查询请求(这些请求来自 Broker 进程);
Historical 进程不处理写入请求;
Historical 进程采用了无共享架构设计,它知道如何去加载和删除 Segment,以及如何基于 Segment 来响应查询。
即便底层的深度存储无法正常工作,Historical 进程还是能针对其已同步的 Segments,正常提供查询服务。
MiddleManager node: 及时摄入实时数据, 生成Segment数据文件。
MiddleManager 进程是执行提交任务的工作节点。MiddleManagers 将任务转发给在不同 JVM 中运行的 Peon 进程。MiddleManager、Peon、Task 的对应关系是,每个 Peon 进程一次只能运行一个Task 任务, 但一个MiddleManager 却可以管理多个 Peon 进程。
Broker node: 接收客户端查询请求, 并将这些查询转发给Historicals和MiddleManagers。当Brokers从这些子查询中收到结果时, 它们会合并这些结果并将它们返回给调用者。
Broker 节点负责转发 Client 查询请求的;
Broker 通过 zookeeper 能够知道哪个 Segment 在哪些节点上, 将查询转发给相应节点;
所有节点返回数据后, Broker 会将所有节点的数据进行合并, 然后返回给 Client;
Router node(可选的): 负责将请求路由到Broker、Coordinators、Overlords。
Router 进程可以在 Brokers、Overlords 和 Coordinators 进程之上,提供一层统一的 API网关。Router 进程是可选的, 如果集群数据规模已经达到了 TB 级别, 需要考虑启用(druid.router.managementProxy.enabled=true)。
一旦集群规模达到一定的数量级, 那么发生故障的概率就会变得不容忽视, 而 Router 支持将请求只发送给健康的节点, 避免请求失败。同时, 查询的响应时间和资源消耗, 也会随着数据量的增长而变高, 而 Router 支持设置查询的优先级和负载均衡策略, 避免了大查询造成的队列堆积或查询热点等问题。
Druid 的进程可以被任意部署, 为了理解与部署组织方便。这些进程分为了三类:
-
Master:Coordinator、Overlord负责数据可用性和摄取 -
Query:Broker and Router, 负责处理外部请求 -
Data:Historical and MiddleManager, 负责实际的Ingestion负载和数据存储
Druid 还包含 3 类外部依赖:
Deep Storage: 存放生成的Segment数据文件, 并供历史服务器下载, 对于单节点集群可以是本地磁盘, 而对于分布式集群一般是HDFS
Druid 使用 deep storage来做数据的备份, 也作为在Druid进程之间在后台传输数据的一种方式。
当响应查询时,Historical首先从本地磁盘读取预取的段,这也意味着需要在deep storage和加载的数据的Historical中拥有足够的磁盘空间。
Metadata Storage:存储Druid集群的元数据信息, 如Segment的相关信息, 一般使用MySQL
| 表名 | 作用 |
|---|---|
| druid_dataSource | 存储DataSources,以便 Kafka Index Service查找 |
| druid_pendingSegments | 存储 pending 的 Segments |
| druid_segments | 存储每个 Segments 的metadata 信息 |
| druid_rules | 关于 Segment 的 load / drop 规则 |
| druid_config | 存放运行时的配置信息 |
| druid_tasks | 为Indexing Service 保存 Task 信息 |
| druid_tasklogs | 为Indexing Service 保存 Task 日志 |
| druid_tasklocks | 为Indexing Service 保存 Task 锁 |
| druid_supervisors | 为Indexing Service保存 SuperVisor 信息 |
| druid_audit | 记录配置、Coordinator规则的变化 |
Zookeeper: 为Druid集群提供以执行协调服务。如内部服务的监控, 协调和领导者选举。
Coordinator 节点的 Leader 选举
Historical 节点发布 Segment 的协议
Coordinator 和 Historical 之间 load / drop Segment 的协议
Overlord 节点的 Leader 选举
Overlord 和 MiddleManager 之间的 Task 管理
二、架构演进
Apache Druid 初始版本架构图 ~ 0.6.0(2012~2013),如图:
Apache Druid 旧架构图——数据流转 0.7.0 ~ 0.12.0(2013~2018),如图:
Apache Druid 旧架构图——集群管理,如图:
0.13.0 ~ 当前版本(2018~now),如图:
Lambda 架构
从大的架构上看, Druid 是一个 Lambda 架构。
Lambda 架构是由 Storm 的作者 Nathan Marz 提出的一个实时大数据处理框架。
Lambda 架构设计是为了在处理大规模数据时, 同时发挥流处理和批处理的优势:
- 通过批处理提供全面、准确的数据
- 通过流处理提供低延迟的数据
从而达到平衡延迟、吞吐量和容错性的目的, 为了满足下游的即席查询, 批处理和流处理的结果会进行合并。
Lambda 架构包含三层:
-
Batch Layer: 批处理层。对离线的历史数据进行预计算, 为了下游能够快速查询想要的结果。由于批处理基于完整的历史数据集, 准确性可以得到保证。批处理层可以用Hadoop、Spark和Flink等框架计算 -
Speed Layer: 加速处理层。处理实时的增量数据, 这一层重点在于低延迟。加速层的数据不如批处理层那样完整和准确, 但是可以填补批处理高延迟导致的数据空白。加速层可以用Storm、Spark streaming和Flink等框架计算 -
Serving Layer: 合并层。将历史数据、实时数据合并在一起, 输出到数据库或者其他介质, 供下游分析
如图:
流式数据的链路为: Row data → Kafka → Streaming processor (Optional, 实时 ETL) → Kafka(Optional) → Druid → Application / User
批处理数据的链路为: Raw data → Kafka(Optional) → HDFS → ETL process(Optional) → Druid → Application / User