关于埋点:字节跳动埋点数据流建设与治理实践

4次阅读

共计 8897 个字符,预计需要花费 23 分钟才能阅读完成。

更多技术交换、求职机会、试用福利,欢送关注字节跳动数据平台微信公众号,回复【1】进入官网交换群

本文将介绍字节跳动在埋点数据流业务场景遇到的需要和挑战以及具体实际,蕴含埋点数据流简介、埋点数据流建设实际、埋点数据流治理实际以及将来布局。关注字节跳动数据平台微信公众号,回复【0627】取得本次分享资料。

埋点数据流

埋点数据流在字节跳动

埋点数据流次要解决的数据是埋点,埋点也叫 Event Tracking,是数据和业务之间的桥梁,也是数据分析、举荐、经营的基石。

用户在应用 App、小程序、Web 等各种线上利用时产生的用户行为数据次要通过埋点的模式进行采集上报,按不同的起源能够分为:

  • 客户端埋点
  • Web 端埋点
  • 服务端埋点

    埋点通过埋点收集服务接管到 MQ,通过一系列的 Flink 实时 ETL 对埋点进行数据标准化、数据荡涤、数据字段裁减、实时风控反作弊等解决,最终散发到不同的上游。上游次要包含举荐、广告、ABTest、行为剖析零碎、实时数仓、离线数仓等。因为埋点数据流处在整个数据处理链路的最上游,所以决定了“稳定性”是埋点数据流最为关注的一点。

字节跳动的埋点数据流规模

字节跳动埋点数据流的规模比拟大,体现在以下几个方面:

接入的业务数量很多,包含抖音、今日头条、西瓜视频、番茄小说在内的多个 App 和服务,都接入了埋点数据流。

流量很大,以后字节跳动埋点数据流峰值流量超过 1 亿每秒,每天解决超过万亿量级埋点,PB 级数据存储增量。

ETL 工作规模体量较大,在多个机房部署了超过 1000 个 Flink 工作和超过 1000 个 MQ Topic,应用了超过 50 万 Core CPU 资源,单个工作最大超过 12 万 Core CPU,单个 MQ Topic 最大达到 10000 个 partition。

那么在这么微小的流量和工作规模下,埋点数据流次要解决的是哪些问题呢?咱们来看几个具体的业务场景。

业务场景

UserAction ETL

在举荐场景中,因为埋点品种多、流量微小,而举荐只关注其中局部埋点,因而须要通过 UserAction ETL 对埋点流进行解决,对这个场景来说有两个需要点:

  • 数据流的时效性
  • ETL 规定动静更新

    为了晋升上流举荐零碎的解决效率,咱们在数据流配置 ETL 规定对举荐关注的埋点进行过滤,并对字段进行删减、映射、标准化等荡涤解决,将埋点打上不同的动作类型标识,解决之后的埋点外部个别称为 UserAction。UserAction 与服务端展示、Feature 等数据会在举荐 Joiner 工作的分钟级窗口中进行拼接解决,产出 instance 训练样本。

举个例子:一个客户端的文章点赞埋点,形容了一个用户在某一个工夫点对某一篇文章进行了点赞操作,这个埋点通过埋点收集服务进入 ETL 链路,通过 UserAction ETL 解决后,实时地进入举荐 Joiner 工作中拼接生成样本,更新举荐模型,从而晋升用户的应用体验。

如果产出 UserAction 数据的 ETL 链路呈现比拟大的提早,就不能在拼接窗口内及时地实现训练样本的拼接,可能会导致用户体验的降落,因而对于举荐来说,数据流的时效性是比拟强的需要。而举荐模型的迭代和产品埋点的变动都可能导致 UserAction ETL 规定的变动,如果咱们把这个 ETL 规定硬编码在代码中,每次批改都须要降级代码并重启相干的 Flink ETL 工作,这样会影响数据流的稳定性和数据的时效性,因而这个场景的另一个需要是 ETL 规定的动静更新。

数据分流

抖音的埋点 Topic 晚顶峰超过一亿每秒,而上游电商、直播、短视频等不同业务关注的埋点都只是其中一部分。如果每个业务都别离应用一个 Flink 工作去生产抖音的全量埋点去过滤出本人关注的埋点,会耗费大量的计算资源,同时也会造成 MQ 集群带宽扇出十分重大,影响 MQ 集群的稳定性。

因而咱们提供了数据分流服务,实现上是咱们应用一个 Flink 工作去生产上游埋点 Topic,通过在工作中配置分流规定的形式,将各个业务关注的埋点分流到上游的小 Topic 中提供给各业务生产,缩小不必要的资源开销,同时也升高了 MQ 集群出带宽。

分流需要大多对 SLA 有肯定要求,断流和数据提早可能会影响上流的举荐成果、广告支出以及数据报表更新等。另外随着业务的倒退,实时数据需要日益减少,分流规定新增和批改变得十分频繁,如果每次规定变动都须要批改代码和重启工作会对上游造成较大影响,因而在数据分流这个场景,规定的动静更新也是比拟强的需要。

容灾降级


另一个场景是容灾降级。数据流容灾首先思考的是避免单个机房级别的故障导致埋点数据流齐全不可用,因而埋点数据流须要反对多机房的容灾部署。其次当呈现机房级别的故障时,须要将故障机房的流量疾速调度到可用机房实现服务的容灾复原,因而须要埋点数据流具备机房间疾速切流的能力。

而数据流降级次要思考的是埋点数据流容量不足以承载全副流量的场景,比方春晚流动、电商大促这类有较大突发流量的场景。为了保障链路的稳定性和可用性,须要服务具备被动或者被动的降级能力。

埋点数据流遇到挑战

挑战次要是流量大和业务多导致的。流量大服务规模就大,不仅会导致老本治理的问题,还会带来单机故障多、性能瓶颈等因素引发的稳定性问题。而上游业务多、需要变动频繁,举荐、广告、实时数仓等上游业务对稳定性和实时性都有比拟高的要求。

在流量大、业务多这样的背景下,如何保障埋点数据流稳定性的同时降低成本、提高效率,是埋点数据流稳定性治理和老本治理面对的挑战。

埋点数据流建设实际

上文咱们理解了埋点数据流的业务场景和面对的挑战,接下来会介绍埋点数据流在 ETL 链路建设和容灾与降级能力上的一些实际。

ETL 链路建设

倒退历程

埋点数据流 ETL 链路倒退到当初次要经验了三个阶段。

第一个阶段是 2018 年以前,业务需要疾速迭代的晚期阶段。那时咱们次要应用 PyJStorm 与基于 Python 的规定引擎构建次要的流式解决链路。特点是比拟灵便,能够疾速反对业务的各种需要,随同着埋点量的疾速上涨,PyJStorm 暴露出很多稳定性和运维上的问题,性能也不足以撑持业务增长。2018 年外部开始大力推广 Flink,并且针对大量旧工作应用 PyJStorm 的状况提供了 PyJStorm 到 PyFlink 的兼容适配,流式工作托管平台的建设肯定水平上也解决了流式工作运维治理问题,数据流 ETL 链路也在 2018 年全面迁徙到了 PyFlink,进入到 Flink 流式计算的新时代。

第二个阶段是 2018 年到 2020 年,随着流量的进一步上涨,PyFlink 和 kafka 的性能瓶颈以及过后应用的 JSON 数据格式带来的性能和数据品质问题纷纷显现出来。与此同时,上流业务对数据提早、数据品质的敏感水平一劳永逸。咱们不仅对一些痛点进行了针对性优化,还破费一年多的工夫将整个 ETL 链路从 PyFlink 切换到 Java Flink,应用基于 Groovy 的规定引擎替换了基于 Python 的规定引擎,应用 Protobuf 代替了 JSON,新链路相比旧链路性能晋升了数倍。同时大数据开发平台和流量平台的建设晋升了埋点数据流在工作开发、ETL 规定治理、埋点治理、多机房容灾降级等多方面的能力。

第三个阶段是从 2021 年开始至今,进一步晋升数据流 ETL 链路的性能和稳定性,在满足流量增长和需要增长的同时,升高资源老本和运维老本是这一阶段的次要指标。咱们次要从三个方面进行了优化。

优化了引擎性能,随着流量和 ETL 规定的一直减少,咱们基于 Groovy 的规定引擎应用的资源也在一直减少,所以基于 Janino 对规定引擎进行了重构,引擎的性能失去了十倍的晋升。

基于流量平台建设了一套比较完善的埋点治理体系,通过埋点下线、埋点管控、埋点采样等伎俩升高埋点老本。

将链路进行了分级,不同的等级的链路保障不同的 SLA,在资源有余的状况下,优先保障高优链路。

接下来是咱们 2018 至 2020 年之间埋点数据流 ETL 链路建设的一些具体实际。

基于规定引擎的 Flink ETL

在介绍业务场景时,提到咱们一个次要的需要是 ETL 规定的动静更新,那么咱们来看一下埋点数据流 Flink ETL 工作是如何基于规定引擎反对动静更新的,如何在不重启工作的状况下,实时的更新上下游的 Schema 信息、规定的解决逻辑以及批改路由拓扑。

首先,咱们在流量平台上配置了上下游数据集的拓扑关系、Schema 和 ETL 规定,而后通过 ConfigCenter 将这些元数据发送给 Flink ETL Job,每个 Flink ETL Job 的 TaskManager 都有一个 Meta Updater 更新线程,更新线程每分钟通过 RPC 申请从流量平台拉取并更新相干的元数据,Source operator 从 MQ Topic 中生产到的数据传入 ProcessFunction,依据 MQ Topic 对应的 Schema 信息反序列化为 InputMessage,而后进入到规定引擎中,通过规定索引算法匹配出须要运行的规定,每条规定咱们形象为一个 Filter 模块和一个 Action 模块,Fliter 和 Action 都反对 UDF,Filter 筛选命中后,会通过 Action 模块对数据进行字段的映射和荡涤,而后输入到 OutputMessage 中,每条规定也指定了对应的上游数据集,路由信息也会一并写出。

当 OutputMessage 输入到 Slink 后,Slink 依据其中的路由信息将数据发送到 SlinkManager 治理的不同的 Client 中,而后由对应的 Client 发送到上游的 MQ 中。

规定引擎

规定引擎为埋点数据流 ETL 链路提供了动静更新规定的能力,而埋点数据流 Flink ETL Job 应用的规定引擎也经验了从 Python 到 Groovy 再到 Janino 的迭代。

因为 Python 脚本语言自身的灵活性,基于 Python 实现动静加载规定比较简单。通过 Compile 函数能够将一段代码片段编译成字节代码,再通过 eval 函数进行调用就能够实现。但 Python 规定引擎存在性能较弱、规定不足治理等问题。

迁徙到 Java Flink 后,在流量平台上对立治理运维 ETL 规定以及 schema、数据集等元数据,用户在流量平台编辑相应的 ETL 规定,从前端发送到后端,通过一系列的校验最终保留为逻辑规定。引擎会将这个逻辑规定编译为理论执行的物理规定,基于 Groovy 的引擎通过 GroovyClassLoader 动静加载规定和对应的 UDF。尽管 Groovy 引擎性能比 Python 引擎晋升了多倍,但 Groovy 自身也存在额定的性能开销,因而咱们又借助 Janino 能够动静高效地编译 Java 代码间接执行的能力,将 Groovy 替换成了 Janino,同时也将解决 Protobuf 数据时应用的 DynamicMessage 替换成了 GeneratedMessage,整体性能晋升了 10 倍。

除了规定引擎的迭代,咱们在平台侧的测试公布和监控方面也做了很多建设。测试公布环节反对了规定的线下测试,线上调试,以及灰度公布的性能。监控环节反对了字段、规定、工作等不同粒度的异样监控,如规定的流量稳定报警、工作的资源报警等。

Flink 拆分工作

规定引擎的利用解决了埋点数据流 ETL 链路如何疾速响应业务需要的问题,实现了 ETL 规定的动静更新,从而批改 ETL 规定不须要批改代码和重启工作。

但规定引擎自身的迭代、流量增长导致的资源扩容等场景,还是须要降级重启 Flink 工作,导致上游断流。

除了重启断流外,大工作还可能在重启时遇到启动慢、队列资源有余或者资源碎片导致起不来等状况。

针对这些痛点咱们上线了 Flink 拆分工作,实质上是将一个大工作拆分为一组子工作,每个子工作按比例去生产上游 Topic 的局部 Partition,按雷同的逻辑解决后再别离写出到上游 Topic。

举个例子:上游 Topic 有 200 个 Partition,咱们在一站式开发平台下来配置 Flink 拆分工作时只须要指定每个子工作的流量比例,每个子工作就能主动计算出它须要生产的 topic partition 区间,其余参数也反对按流量比例主动调整。

拆分工作的利用使得数据流除了规定粒度的灰度公布能力之外,还具备了 Job 粒度的灰度公布能力,降级扩容的时候不会产生断流,上线的危险更可控。同时因为拆分工作的各子工作是独立的,因而单个子工作呈现反压、Failover 对上游的影响更小。另一个长处是,单个子工作的资源使用量更小,资源能够同时在多个队列进行灵便的部署。

容灾与降级能力建设

说到 ETL 链路建设,埋点数据流在容灾与降级能力建设方面也进行了一些实际。

首先是容灾能力的建设,埋点数据流在 Flink、MQ、Yarn、HDFS 等组件反对多机房容灾的根底上实现了多机房容灾部署,并筹备了多种流量调度的预案。

失常状况下流量会平均打到多个机房,MQ 在多个机房间同步,Flink ETL Job 默认从本地 MQ 进行生产,如果某个机房呈现故障,咱们依据状况能够抉择通过配置下发的形式从客户端将流量调度到其余非受灾机房,也能够在 CDN 侧将流量调度到其余非受灾机房。埋点数据流 ETL 链路能够分钟级地进入容灾模式,迅速将故障机房的 Flink Job 切换到可用的机房。

其次是服务降级能力的建设,次要蕴含服务端降级策略和客户端降级策略。服务端降级策略次要通过 LB 限流、客户端进行退却重试的机制来实现,客户端降级策略通过配置下发能够升高埋点的上报频率。

举个例子:在春晚流动中参加的用户很多,口播期间更是有着十分微小的流量洪峰,2021 年春晚流动期间为了应对口播期间的流量洪峰,埋点数据流开启了客户端的降级策略,动静升高了肯定比例用户的埋点上报频率,在口播期间不上报,口播完结后迅速复原上报。在降级场景下,上游的指标计算是通过生产未降级用户上报的埋点去估算整体指标。目前咱们在此基础上进行了优化,客户端目前的降级策略能够更近一步的依据埋点的分级信息去保障高优的埋点不降级,这样能够在流动场景下保障流动相干的埋点不降级的上报,反对上游指标的精确计算。

埋点数据流治理实际

介绍完埋点数据流建设的实际,接下来给大家分享的是埋点数据流治理方面的一些实际。埋点数据流治理蕴含多个治理畛域,比方稳定性、老本、埋点品质等,每个治理畛域上面又有很多具体的治理我的项目。

比方在稳定性治理中咱们通过优化缩小了因为单机问题、MQ 性能问题和混布问题等导致的各种稳定性问题;

老本治理方面,咱们通过组件选型、性能优化、埋点治理等形式获得了显著降本增效的成绩;

埋点品质治理方面,咱们对脏数据问题、埋点字段类型谬误问题和埋点数据的失落反复问题进行了监控和治理。

这次咱们次要选取了其中局部治理我的项目和大家分享。

单机问题优化

Flink BacklogRescale

Yarn 单机问题导致 Flink 工作 Failover、反压、生产能力降落是比拟常见的 case。

单机问题的类型有很多:队列负载不均、单机 load 高或者其余过程导致 CPU 负载高,以及一些硬件故障都可能导致 Yarn 单机问题。针对 Yarn 单机问题,咱们从 Flink 和 Yarn 两个层面别离进行了优化,最终使单机 load 高导致的数据提早缩小了 80% 以上。

首先是 Flink 层面的优化,在埋点数据流 ETL 场景中,为了缩小不必要的网络传输,咱们的 Partitioner 次要采纳的是 Rescale Partitioner,而 Rescale Partitioner 会应用 Round-Robin 的形式发送数据到上游 Channel 中。因为单机问题可能导致上游个别 Task 反压或者解决提早从而引起反压,而实际上在这个场景外面,数据从上游 task 发送到任何一个上游的 Task 都是能够的,正当的策略应该是依据上游的 Task 的解决能力去发送数据,而不是用 Round-Robin 形式。

另一方面咱们留神到 Flink Credit-Based flow control 反压机制中,能够用 backlog size 去判断上游 Task 的解决负载,咱们也就能够将 Round Robin 的发送形式批改为依据 Channel 的 Backlog size 信息,去抉择负载更低的上游 Channel 进行发送。这个 Feature 上线后,队列的负载变得更加平衡,CPU 的使用率也晋升了 10%。

Yarn 优化

Yarn 层面的优化,第一个是队列资源层面,咱们应用独立的 Label 队列能够防止高峰期被其余低优工作影响。

第二个是对于 Yarn 节点上的 DataNode 把带宽打满或者 CPU 应用比拟高影响节点上埋点数据流 Flink 工作稳定性的状况,通过给 DataNode 进行网络限速,CPU 绑核等操作,防止了 DataNode 对 Flink 过程的影响。

第三个是 Yarn 反调度的策略,目前字节跳动 Flink 应用的 Yarn Gang Scheduler 会按条件束缚选择性地调配 Yarn 资源,在工作启动时平衡的搁置 Container,然而因为工夫的推移,流量的变动等各种因素,队列还是会呈现负载不平衡的状况,所以反调度策略就是为了解决这种负载不平衡而生的二次调度机制。

反调度策略中,Yarn 会定期检查不满足原有束缚的 Container,并在这些 Container 所在节点上筛选出须要从新调度的 Container 返还给 Flink Job Manager,而后 Flink 会从新调度这些 Container,从新调度会依照原有的约束条件尝试申请等量的可用资源,申请胜利后再进行迁徙。

另外咱们会针对一些频繁出问题的节点把它们退出调度的黑名单,在调度的时候防止将 container 调度到这些节点。

MQ 优化

Databus 利用


在流量迅速增长的阶段,埋点数据流 Flink 工作一开始是通过 Kafka Connecter 间接写入 Kafka。但因为工作解决的流量十分大,Flink 工作中 Sink 并发比拟多,导致批量发送的效率不高,Kafka 集群写入的申请量十分大。并且因为每个 Sink 一个或多个 Client,Client 与 Kafka 之间建设的连接数也十分多。而 Kafka 因为 Controller 的性能瓶颈无奈持续扩容,所以为了缓解 Kafka 集群的压力,埋点数据流的 Flink 工作引入了 Databus 组件。Databus 是一种以 Agent 形式部署在各个节点上的 MQ 写入组件。Databus Agent 能够配置多个 Channel,每个 Channel 对应一个 Kafka 的 Topic。Flink Job 每个 Task Manager 外面的 Sink 会通过 Unix Domain Socket 的形式将数据发送到节点上 Databus Agent 的 Channel 外面,再由 Databus 将数据批量地发送到对应的 Kafka Topic。因为一个节点上会有多个 Task Manager,每个 Task Manager 都会先把数据发送到节点上的 Databus Agent,Databus Agent 中的每个 Channel 实际上聚合了节点上所有 Task Manager 写往同一个 Topic 数据,因而批量发送的效率十分高,极大地升高了 Kafka 集群的写入申请量,并且与 Kafka 集群之间建设的连接数也更少,通过 Agent 也能不便地设置数据压缩算法,因为批量发送的起因压缩效率比拟高。在咱们开启了 Zstd 压缩后,Kafka 集群的写入带宽升高了 37%,极大地缓解了 Kafka 集群的压力。

Kafka 迁徙 BMQ


在埋点数据流这种大流量场景下应用 Kafka,会常常遇到 Broker 或者磁盘负载不均、磁盘坏掉等状况导致的稳定性问题,以及 Kafka 扩容、Broker 替换等运维操作也会影响集群工作失常的读写性能,除此之外 Kafka 还有 controller 性能瓶颈、多机房容灾部署老本低等毛病。

为了优化这些问题,BMQ 这款字节跳动自研的存储计算拆散的 MQ 应运而生。BMQ 的数据存储应用了 HDFS 分布式存储,每个 Partition 的数据切分为多个 segment,每个 segment 对应一个 HDFS 文件,Proxy 和 Broker 都是无状态的,因而能够反对疾速的扩缩容,并且因为没有数据拷贝所以扩缩容操作也不会影响读写性能。

受害于 HDFS 曾经建设得比较完善的多机房容灾能力,BMQ 多机房容灾部署就变的非常简单,数据同时写入所有容灾机房后再返回胜利即可保障多机房容灾。数据生产是在每个机房读取本地的 HDFS 进行生产,缩小了跨机房带宽。除此之外,因为基于多机房 HDFS 存储比 Kafka 集群多机房部署所需的正本更少,所以最终实现了单 GB 流量老本比照 Kafka 降落了 50% 的资源收益。

老本治理 - 埋点治理


在埋点治理方面,通过对流量平台的建设,提供了从埋点设计、埋点注册、埋点验证、埋点上报、埋点采样、流式 ETL 解决,再到埋点下线的埋点全生命周期的治理能力。

埋点管控


目前字节跳动所有的产品都开启了埋点管控。所有的埋点都须要在咱们的流量平台上注册埋点元数据之后能力上报。而咱们的埋点数据流 ETL 也只会解决曾经注册的埋点,这是从埋点接入流程上进行的管控。

在埋点上报环节,通过在流量平台配置埋点的采样率对指定的埋点进行采样上报,在一些不须要统计全量埋点的场景能显著地升高埋点的上报量。

对于曾经上报的埋点,通过埋点血统统计出曾经没有在应用的埋点,主动告诉埋点负责人在流量平台进行自助下线。埋点下线流程实现后会通过服务端动静下发配置到埋点 SDK 以及埋点数据流 ETL 工作中,确保未注册的埋点在上报或者 ETL 环节被抛弃掉。还反对通过埋点黑名单的形式对一些异样的埋点进行动静的封禁。

埋点分级

埋点分级次要是针对离线存储老本进行优化,首先在流量平台上对埋点进行分级,埋点数据流 ETL 工作会将分级信息写入到埋点数据中。埋点数据在从 MQ Dump 到 HDFS 这个阶段依据这些分级的信息将埋点数据写入不同的 HDFS 分区门路下。而后通过不同的 Spark 工作生产不同分级分区的 HDFS 数据写入 Hive Table。不同等级的分区能够优先保障高优埋点的产出,另外不同分区也能够配置不同的 TTL,通过缩减低优数据的 TTL 节俭了大量的存储资源。

将来布局


相干技术实际曾经通过火山引擎数据中台产品对外输入,大家感兴趣的话也能够登陆火山引擎的官网进行理解。

正文完
 0