更多技术交换、求职机会、试用福利,欢送关注字节跳动数据平台微信公众号,回复【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节俭了大量的存储资源。
将来布局
相干技术实际曾经通过火山引擎数据中台产品对外输入,大家感兴趣的话也能够登陆火山引擎的官网进行理解。