18 年出版的书,豆瓣评分比较高。书名叫基础架构,其实基本没有,反倒是很多关于平台的思考。非常建议大数据平台架构的 RD 和 PM 都读一遍。
这本书优点是作者的思考很多,也接地气(适合中型公司);缺点则是只有思考、现状、畅想未来,怎么做的基本上都是一笔带过(过程难度很大、跨团队、链条很长、取舍等等),以及废话有点多。。。
书里提到的很多问题,我在做大数据平台时也多少经历过,有一些共鸣。
1. Eat Your Own Dog Food
工作经常在集群、计算引擎、平台后端之间切换,但是每当面对平台功能需求时,我就会反复想到这句话。
多用用自己的产品吧。带实时计算的新人时,除了模块串讲,我还会要求新人多用用平台,同时指出平台功能当前的问题(>=3个)。
比如 Flink 1.9 版本,Kafka DDL 里仅支持设置 offset 不支持设置时间,但我观察到实际使用场景是:
- 大部分场景:用户指定 consumer group ,任务运行时 commit offset,失败时从对应的 group.id 恢复
- 新增/修改指标的场景,先使用离线 T-1 更新数据,然后实时任务从 T-1 的任意时间点开始消费 kafka,直到数据追赶上
- 之后即使任务失败,也从 group.id 而不能从指定的时间点恢复(否则导致大量重复数据/offset 已过期等问题)
因此可以提炼出两个需求:
- DDL 里支持指定时间,而不仅仅是对用户没有意义的 offset
- 任务首次启动按照 DDL 里指定的时间,但是 offset 一旦 commit,之后任务重启需要从 commit 的 offset 恢复
怎么支持呢?
关于 1,可以修改 kafka connector 的 KafkaTableSourceSinkFactory
,实际上 DataStream 接口里,setStartFromTimestamp
可以支持指定消费的起始时间。但有两点需要考虑:
- DDL 属性指定什么名字?后续 flink 版本升级的兼容性问题
- 如果用户指定了错误的时间呢?或者时间正确,但是 topic 在指定的时间数据实际上已经过期了。这两种情况都会启动任务,但是导致错误的数据结果。用户如何能够知晓?通过日志的话,日志在哪看?怎么确保用户能看到?
因此一个折衷的办法,是在辅助页面,支持用户传入时间,后台查找对应的 offset,并生成对应的 flink DDL 片段,如果有数据过期,则提示用户。
关于 2,则是类似于后端上线里 post_script 的功能,支持任务运行后更新配置。
关于功能,除了阶段性的新增功能,原有功能的兼容性、易用性也要不断 review,“每个人都捡起自己看到的垃圾”。我觉得比较舒服的状态,是维护了一个需求池,平台升级迭代就像 app 发版一样,本次修复/优化/新增了xxx,定时发版,人力不受影响。
关于服务,平台要提供的也不是“海底捞式的服务”,而是给用户一个清晰明确的预期,收到的反馈/case多了,对应的再做成功能。因此服务的目的是了解一线情况,做成功能,最终使得用户自助服务。
太细致的服务,可能适得其反。比如我写过很详细的上手文档/FAQ,可还是会总有人问。功能对单个业务方做定向支持,其他业务方会觉得不好用;做通用的支持,业务方会觉得支持太慢。这些不只是大数据平台独有的,只要有合作就会有这类问题,心态要平和。
这两点知易行难,依赖团队氛围、人员的长期培养以及人力的精细排兵。
2. 平台
平台不是堆砌一堆集群组件或者功能,否则就是一个组件运维团队而不是架构团队了。
2.1. 作业调度系统
作者将作业调度系统看的极重,花了大量篇幅介绍。
数据依赖还是任务依赖?
每个做过任务调度系统的人,都会有这样的疑问。任务调度系统的核心功能之一,就是要准确的解决依赖问题。
- 数据依赖:依赖表分区、依赖文件等;优点是直观,缺点则是在全局补数时难以判断上下游关系
- 任务依赖:依赖任务是否成功;优点是天然生成了任务关系,方便补数,缺点则是任务变动时的影响,比如依赖的某个任务修改了写入到表,那当前任务也需要及时修改
作者认为 1 是一个伪命题,不过我觉得实际情况是主推 2,但是还是 1 2 混合的方式,才能覆盖全部场景。
平台在易用性上可以更进一步,比如根据 SQL 解析出 Insert/From/Join 的表,进而推断出对应的任务,提示给用户对应的数据/任务依赖,提高开发效率;上游任务依赖下线时,平台也需要让下游任务感知得到,提高运维效率。
任务异常比较常见,一般也会支持用户配置重试。但同样值得继续分析,比如系统相关失败,是否应当纳入用户重试次数?需要做什么预处理?任务失败应该向谁报警?以什么方式报警?什么情况下停止报警?任务运行得慢要不要报警?怎么知道任务运行得比以前慢?多慢报警?不同的任务能否区别对待?等等,都是平台 owner 应当考虑的点。
更进一步,任务失败重跑后,是否要触发下游任务?就是全局数据回溯的问题了。
平台要解决用户开发效率问题,因此提供了较多的默认值。默认值应该设置多少合理?主要有两个原则:
- 符合大多数用户
- 默认值有问题的给出明确反馈
关于调度的并发上,按照业务线、调度类型、优先级进行控制,我看快手1 也是做了 P0 P1 P2 多级。当前我们做的还远远不够,但是架构设计上是一致的,只是卡在了多个分组的上线效率上。
通讯协议上,内部系统采用 RPC,外部交互采用 HTTP. 这点也符合我对大数据平台架构的设计初衷,最初尝试定义了各个模块之间统一的 proto,http code/msg/data 也应该有统一的规范,但是限于组织人力,没有推广开来。
报警服务应当独立于调度系统,跟 trace、权限 一样,属于贯穿大数据各个平台的基础系统。目的有两个:
- 对外提供统一的报警组、角色、报警方式
- 对内提供统一实现,以及报警收敛的跟进机制,避免报警泛滥
上述无论从功能、稳定性设计,还是周边交互,都覆盖很广,也是作者认为调度重要的原因之一。
2.2. 集成开发环境门户建设
主要是线上/测试环境的隔离问题。
后端开发环境隔离相对容易,主要是配置(DB、上下游地址等)修改。
举我之前所在的 spider 架构为例,单独测试环境并不复杂。但是 spider 流量很大,测试环境需要线上真实流量测试单并发的 qps. 为此 QA 专门基于当时架构,开发了一套引流系统,按照时间/数据量从线上 dump 数据。该文件可以 replay 用于测试。
这个思路放到大数据,似乎也是可行的,比如从 binlog/log 采集,架构全链路就支持旁路输出。但是问题是大数据大量的表 join 操作,如果两条流/表的数据关联不上,SQL 任务结果可能为空,这种测试毫无价值。
进一步的,是否可以按照同一个 id 取模的方式,从线上采样流量到测试环境?理论上可行,但是耦合了业务逻辑,比如用哪个 id?每个表可能有 join 的不同字段,比如订单表,有用户维表、地区维表、年级维表,从这点看维表就只能全量而不能采样。所以这个思路也不可行。
作者提到他们最后还是通过平台化的方式解决,比如自动替换测试项目的 SQL 的 database:
INSERT OVERWRITE TABLE online_trade.dim_user
-- 替换为
INSERT OVERWRITE TABLE test_trade.dim_user
部分情况下允许读线上 database,强制写测试 database.
也就是没有从网络上完全禁止。18 年作者是这个方案,到现在我也没有想更好的方案,而且多个业务线是共用 1 个测试库还是多个,怎么管理?
不过回到问题本身,为什么不能用线上?
- 怕数据污染线上
- 怕资源影响线上
- 怕数据泄露
大概从这 3 点上想办法,尽量避免降低用户开发效率。否则强推平台或者用户修改,就纯粹是耍流氓。
2.3. 数据采集、传输、交换、同步服务
好像现在云厂商/开源系统背后的商业公司,都推的“数据集成”这个名字,功能上大同小异。
主要注意主键、分库分表等问题。
2.4. 数据可视化平台
作者采用的自研,主要考虑云厂商的 BI 产品比较封闭,对接公司内第三方系统效率很低。
技术层面的关键点:
- 可视化(控件、辅助能力(下钻、过滤、排序,播报)),避免追求华而不实的堆砌组件
- 上下游集成
- 权限控制
2.5. 安全与权限管控
考虑到 hadoop 各个组件的权限管控不统一、功能设计等,作者推荐将权限管控放到平台,对接 LDAP 方便且人员变动对任务无影响。
当数据流变长,会出现诸多类似的问题:
- 底层管理复杂,平台管理简单
- 底层管理报错反馈慢,平台管理报错反馈快
因此平台的入口和交互非常重要,不能简单的理解为一个 proxy 模块。
但是也提到了不在平台管控的任务,通过开放平台的 REST API 解决。随着技术发展,我觉得现在更好的解决方式是类似于 Airflow 的做法,支持用户同时实现写代码。也就是将声明式(SQL 查询数据结果)和定义式(自定义本地的计算逻辑)结合到一块,已解决算法场景需要获取数仓数据同时又有本地计算的问题。
这部分管控一定程度上会降低用户开发效率,但是尽量想办法抵消或者避免。一个重点是页面显示空还是不可更改的灰值的问题:
比如任务 A 使用了 a 队列,用户有任务 A 的可读权限,但是没有 a 队列的使用权限,那当查看任务 A 时,队列的值是不显示,还是显示置灰的 a ?明显后者更好一些,因为可以引导用户申请对应的权限。
进一步,用户有任务 A 的修改权限,这个控件又该怎么显示。
你可能无法将这本书真的作为指南使用,但是当实践时,一定会有作者抛出来的这些疑问。