基于 Flink 打造的伴鱼实时计算平台 Palink 的设计与实现
在伴鱼发展早期,出现了一系列实时性相关的需求,比如算法工程师期望可以拿到用户的实时特征数据做实时推荐,产品经理希望数据方可以提供实时指标看板做实时运营分析。
一、核心原则
通过调研阿里云、网易等各大厂商提供的实时计算服务,我们基本确定了 Palink 的整个产品形态。同时,在系统设计过程中紧紧围绕以下几个核心原则:
极简性:保持简易设计,快速落地,不过度追求功能的完整性,满足核心需求为主;
高质量:保持项目质量严要求,核心模块思虑周全;
可扩展:保持较高的可扩展性,便于后续方案的迭代升级。
二、系统设计
平台整体架构
Web UI:前端操作页面;
Palink (GO) 服务:实时作业管理服务,负责作业元信息及作业生命周期内全部状态的管理,承接全部的前端流量。包括作业调度、作业提交、作业状态同步及作业 HA 管理几个核心模块;
PalinkProxy(JAVA) 服务:SQL 化服务,Flink SQL 作业将由此模块编译、提交至远端集群。包括 SQL 语法校验、SQL 作业调试及 SQL 作业编译和提交几个核心模块;
Flink On Yarn:基于 Hadoop Yarn 做集群的资源管理。
一是伴鱼拥有一套非常完善的基于 GO 语言实现的微服务基础框架,基于它可以快速构建服务并拥有包括服务监控在内的一系列周边配套,公司目前 95% 以上的服务是基于此服务框架构建的;
二是 SQL 化模块是基于开源项目二次开发实现的(这个在后文会做详细介绍),而该开源项目使用的是 JAVA 语言;
三是内部服务增加一次远程调用的成本是可以接受的。
作业调度&执行
type PalinkJobCommand struct {
ID uint64 `json:"id"`
PalinkJobID uint64 `json:"palink_job_id"`
CommandParams string `json:"command_params"`
CommandState int8 `json:"command_state"`
Log string `json:"log"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
调度流程
执行流程
Flink JAR 作业:我们摒弃了用户直接上传 JAR 文件的交互方式。用户只需提供作业 gitlab 仓库地址即可,打包构建全流程平台直接完成。由于每一个服务实例都内嵌 Flink 客户端,任务是直接通过 Flink run 方式提交的。 PyFlink 作业:与 Flink JAR 方式类似,少了编译的过程,提交命令也有所不同。 Flink SQL 作业:与上两种方式区别较大。对于 Flink SQL 作业而言,用户只需提交相对简单的 SQL 文本信息,这个内容我们是直接维护在平台的元信息中,故没有和 gitlab 仓库交互的地方。SQL 文本将进一步提交给 PalinkProxy 服务进行后续的编译,然后使用 Yarn Client 方式提交。
Command 状态机
UNDO:初始状态,将被调度实例监测。 DOING:执行中状态,同样会调度实例监测,防止长期处于进行中的脏状态产生。 SUCCESSED:执行成功状态。随着用户的后续行为,如重新提交、重新启动操作,状态会再次回到 UNDO 态。 FAILED:执行失败状态。同上,状态可能会再次回到 UNDO 态。
作业状态同步
状态同步流程
Job 状态机
DEPLOYING:作业初始状态,将随着 PalinkJobCommand 的状态驱动向 DEPLOY_SUCCESSED 和 DEPLOY_FAILED 流转。 DEPLOY_SUCCESSED:部署成功状态,依赖作业「状态同步」驱动向 RUNNING 状态或者其他终态流转。 DEPLOY_FAILED:部署失败状态,依赖用户重新提交向 DEPLOYING 状态流转。 RUNNING:运行中状态。可通过用户执行暂停操作向 FINISHED 状态流转,或执行终止操作向 KILLED 状态流转,或因为内部异常向 FAILED 状态流转。 FINISHED:完成状态,作业终态之一。通过用户执行暂停操作,作业将回到此状态。 KILLED:终止状态,作业终态之一。通过用户执行终止操作,作业将回到此状态。 FAILED:失败状态,作业终态之一。作业异常会转为此状态。
作业 HA 管理
作业是有状态的,但是作业需要代码升级,如何处理? 作业异常失败了,怎么做到从失败的时间点恢复?
暂停操作通过调用 Flink cancel api 实现,将触发作业生成 Savepoint。 终止操作则是通过调用 yarn kill application api 实现,用于快速结束一个任务。
一是任务自身可以设置重启策略自动恢复,外部平台无感知; 二是,对于内部重启依旧失败的任务在平台侧可再次设置上层重启策略; 三是,手动重启或重新提交。仅在重新提交时,由用户决定按照那种方式启动,其余场景皆按照最近的保存点启动。
任务 SQL 化
实现机制
构建 PackagedProgram
PackagedProgram.newBuilder()
.setJarFile(coreJarFile)
.setArguments(execArgs)
.setSavepointRestoreSettings(savepointRestoreSettings)
.build();
定制开发
服务化:整个 SQL 化模块作为 proxy 独立部署和管理,以 HTTP 形式暴露服务; 支持语法校验特性; 支持调试特性:通过解析 SQL 结构可直接获取到 source 表和 sink 表的结构信息。平台可通过人工构造或线上抓取源表数据的方式得到测试数据集,sink 算子被 localTest connector 算子直接替换,以截取结果数据输出; 支持更多的 connector plugin,如 pulsar connector; 其他特性。
DDL 语句注入 UDF 管理 租户管理 版本管理 作业监控 日志收集
三、线上效果
■ 作业总览
四、未来工作
随着业务的继续推进,平台将在以下几方面继续迭代优化:
稳定性建设:实时任务的稳定性建设必然是未来工作中的首要事项。作业参数如何设置,作业如何自动调优,作业在流量高峰如何保持稳定的性能,这些问题需要不断探索并沉淀更多的最佳实践;
提升开发效率:SQL 化建设。尽管 SQL 化已初具雏形,但开发起来依旧具备一定的学习成本,其中最明显的就是 DDL 的构建,用户对于 source、sink 的 schema 并不清楚,最好的方式是平台可以和我们的元数据中心打通将构建 DDL 的过程自动化,这一点也是我们目前正在做的;
优化使用体验:体验上的问题在一定程度上也直接影响到了开发的效率。通过不断收集用户反馈,持续改进;
探索更多业务场景:目前伴鱼内部已开始基于 Flink 开展 AI 、实时数仓等场景的建设。未来我们将继续推进 Flink 在更多场景上的实践。