Flink在爱奇艺广告业务的实践
黑色素生成能力检测 http://pf.39.net/bdfyy/bdflx/180503/6210222.html 一、业务场景 实时数据在广告业务的使用场景主要可以分为四个方面:数据大屏:包括曝光、点击、收入等核心指标的展示,以及故障率等监控指标;异常监测:因为广告投放的链路比较?,所以如果链路上发生任何波动的话,都会对整体的投放效果产生影响。除此之外,各个团队在上线过程中是否会对整体投放产生影响,都是通过异常监测系统能够观测到的。我们还能够观测业务指标走势是否合理,比如在库存正常的情况下,曝光是否有不同的波动情况,这可以用来实时发现问题;数据分析:主要用于数据赋能业务发展。我们可以实时分析广告投放过程中的一些异常问题,或者基于当前的投放效果去研究怎样优化,从而达到更好的效果;特征工程:广告算法团队主要是做一些模型训练,用于支持线上投放。技术特征最初大部分是离线,随着实时的发展,开始把一些工程转到实时。二、业务实践 业务实践主要分为两类,第一个是实时数仓,第二个是特征工程。1.实时数仓■1.1实时数仓-目标实时数仓的目标包括数据完整性、服务稳定性和查询能力。数据完整性:在广告业务里,实时数据主要是用于指导决策,比如广告主需要根据当前投放的实时数据,指导后面的出价或调整预算。另外,故障率的监控需要数据本身是稳定的。如果数据是波动的,指导意义就非常差,甚至没有什么指导意义。因此完整性本身是对时效性和完整性之间做了一个权衡;服务稳定性:生产链包括数据接入、计算(多层)、数据写入、进度服务和查询服务。除此之外还有数据质量,包括数据的准确性以及数据趋势是否符合预期;查询能力:在广告业务有多种使用场景,在不同场景里可能使用了不同的OLAP引擎,所以查询方式和性能的要求不一致。另外,在做数据分析的时候,除了最新最稳定的实时数据之外,同时也会实时+离线做分析查询,此外还包括数据跨源和查询性能等要求。■1.2实时数仓-挑战数据进度服务:需要在时效性和完整性之间做一个权衡;数据稳定性:由于生产链路比较长,中间可能会用到多种功能组件,所以端到端的服务稳定性对整体数据准确性的影响是比较关键的;查询性能:主要包括OLAP分析能力。在实际场景中,数据表包含了离线和实时,单表规模达上百列,行数也是非常大的。■1.3广告数据平台架构上图为广告数据平台基础架构图,从下往上看:底部是数据采集层,这里与大部分公司基本一致。业务数据库主要包含了广告主的下单数据以及投放的策略;埋点日志和计费日志是广告投放链路过程中产生的日志;中间是数据生产的部分,数据生产的底层是大数据的基础设施,这部分由公司的一个云平台团队提供,其中包含Spark/Flink计算引擎,Babel统一的管理平台。Talos是实时数仓服务,RAP和OLAP对应不同的实时分析以及OLAP存储和查询服务。数据生产的中间层是广告团队包含的一些服务,例如在生产里比较典型的离线计算和实时计算。离线是比较常见的一个分层模型,调度系统是对生产出的离线任务做有效的管理和调度。实时计算这边使用的引擎也比较多,我们的实时化是从年开始,当时选的是SparkStreaming,后面随着大数据技术发展以及公司业务需求产生了不同场景,又引入了计算引擎Flink。实时计算底层调度依赖于云计算的Babel系统,除了计算之外还会伴随数据治理,包括进度管理,就是指实时计算里一个数据报表当前已经稳定的进度到哪个时间点。离线里其实就对应一个表,有哪些分区。血缘管理包括两方面,离线包括表级别的血缘以及字段血缘。实时主要还是在任务层面的血缘。至于生命周期管理,在离线的一个数仓里,它的计算是持续迭代的。但是数据保留时间非常长的话,数据量对于底层的存储压力就会比较大。数据生命周期管理主要是根据业务需求和存储成本之间做一个权衡。质量管理主要包括两方面,一部分在数据接入层,判断数据本身是否合理;另外一部分在数据出口,就是结果指标这一层。因为我们的数据会供给其他很多团队使用,因此在数据出口这一层要保证数据计算没有问题。再上层是统一查询服务,我们会封装很多接口进行查询。因为数据化包括离线和实时,另外还有跨集群,所以在智能路由这里会进行一些选集群、选表以及复杂查询、拆分等核心功能。查询服务会对历史查询进行热度的统一管理。这样一方面可以更应进一步服务生命周期管理,另一方面可以去看哪些数据对于业务的意义非常大。除了生命周期管理之外,它还可以指导我们的调度系统,比如哪些报表比较关键,在资源紧张的时候就可以优先调度这些任务。再往上是数据应用,包括报表系统、Add-hoc查询、数据可视化、异常监控和下游团队。■1.4实时数仓-生产链路数据生产链路是从时间粒度来讲的,我们最开始是离线数仓链路,在最底层的这一行,随着实时化需求推进,就产生了一个实时链路,整理来说,是一个典型的Lambda架构。另外,我们的一些核心指标,比如计费指标,因为它的稳定性对下游比较关键,所以我们这边采用异路多活。异路多活是源端日志产生之后,在计算层和下游存储层做了完全的冗余,在后面的查询里做统一处理。■1.5实时数仓-进度服务上文介绍了我们要求提供出去的实时数据的指标是稳定不变的,进度服务实现的核心点包括时间窗口里指标的变化趋势,同时结合了实时计算任务本身的状态,因为在实时数仓里,很多指标是基于时间窗口做聚合计算。比如一个实时指标,我们输出的指标是3分钟,也就是说4:00这个时间点的指标的就包括了4:00~4:03的数据,4:03包括了4:03~4:06的数据,其实就是指一个时间窗口的数据,什么时候是对外可见的。因为在实时计算里,数据不断进来,4:00的时间窗口的数据从4:00开始,指标就已经开始产生了。随着时间叠加,指标不断上升,最后趋于稳定。我们基于时间窗口指标的变化率,来判断它是否趋于稳定。但如果只是基于这个点来看,那么它还存在一定的弊端。因为这个结果表的计算链会依赖很多个计算任务,如果这个链路上面哪个任务出现问题,可能会导致当前的指标虽然走势已经趋于正常,但是最终并不完整。所以在这基础之上,我们又引入了实时计算任务状态,在指标趋于稳定的时候,同时去看生产链路上这些计算任务是否正常,如果是正常的话,表示任务本身时间点的指标已经稳定,可以对外提供服务。如果计算有卡顿、堆积,或者已经有异常在重启过程中,就需要继续等待迭代处理。■1.6实时数仓-查询服务上图为查询服务架构图。最下方是数据,里面有实时存储引擎,包括Druid等。在离线中,数据在Hive里边,但是在做查询的时候,会把它们进行OLAP的同步,在这边使用的是两种引擎。为了和Kudu做union查询,会把它同步到OLAP引擎,然后上面去统一使用Impala做查询。另外,对于使用场景里比较固定的方式,可以导到Kylin里,然后在上面做数据分析。基于这些数据,会有多个查询节点,再上面是一个智能路由层。从最上面查询网关,当有一个查询请求进来,首先判断它是不是一个复杂场景。比如在一个查询里,如果它的时长同时跨越了离线和实时,这里就会同时使用到离线表和实时表。另外,离线表里还有更复杂的选表逻辑,比如小时级别,天级别。经过复杂场景分析之后,就会把最终选择的表大概确定下来。其实在做智能路由的时候,才会去参考左边的一些基础服务,比如元数据管理,当前这些表的进度到哪个点了。对于查询性能的优化,在数据里,底层扫描的数据量对最终性能的影响是非常大的。所以会有一个报表降维,根据历史的查询去做分析。比如在一个降维表包含哪些维度,可以覆盖到百分之多少的查询。■1.7数据生产-规划之前在实时数据报表生产里提到,它主要是基于API的方式实现的。Lambda架构本身有一个问题就是实时跟离线是两个计算团队,对于同一个需求,需要两个团队同时去开发,这样会带来几个问题。一方面是他们的逻辑可能会发生差异,最终导致结果表不一致;另一方面是人力成本,同时需要两个团队进行开发。因此我们的诉求是流批一体,思考在计算层是否可以使用一个逻辑来表示同一个业务需求,比如可以同时使用流或者批的计算引擎来达到计算的效果。在这个链路里边,原始数据通过Kafka的方式接入进来,经过统一的ETL逻辑,接着把数据放在数据湖里。因为数据湖本身可以同时支持流和批的方式进行读写,而且数据湖本身可以实时消费,所以它既可以做实时计算,也可以做离线计算,然后统一把数据再写回数据湖。前文提到在做查询的时候,会使用离线跟实时做统一整合,所以在数据湖里写同一个表,在存储层面可以省去很多工作,另外也可以节省存储空间。■1.8数据生产-SQL化SQL化是Talos实时数仓平台提供的能力。从页面上来看,它包括了几个功能,左边是项目管理,右边包括Source、Transform和Sink。有一些业务团队本身对于计算引擎算子非常熟,那么他们便可以做一些代码开发;但是很多业务团队可能对引擎并不是那么了解,或者没有强烈的意愿去了解,他们就可以通过这种可视化的方式,拼接出一个作业。例如,可以拖一个Kafka的数据源进来,在上面做数据过滤,然后就可以拖一个Filter算子达到过滤逻辑,后面可以再去做一些Project,Union的计算,最后输出到某个地方就可以了。对于能力稍微高一些的同学,可以去做一些更高层面的计算。这里也可以实现到实时数仓的目的,在里面创建一些数据源,然后通过SQL的方式,把逻辑表示出来,最终把这个数据输出到某种存储。上面是从开发层面来讲,在系统层面上,它其实还提供了一些其他的功能,比如规则校验,还有开发/测试/上线,在这里可以统一管理。此外还有监控,对线上跑的实时任务有很多实时指标,可以通过查看这些指标来判断当前的任务是不是正常的状态。2.特征工程特征工程有两方面的需求:第一个需求是实时化,因为数据价值随着时间的递增会越来越低。比如某用户表现出来的观影行为是喜欢看儿童内容,平台就会推荐儿童相关的广告。另外,用户在看广告过程中,会有一些正/负反馈的行为,如果把这些数据实时迭代到特征里,就可以有效提升后续的转化效果。实时化的另一个重点是准确性,之前很多特征工程是离线的,在生产环节里面存在计算时的数据跟投放过程中的特征有偏差,基础特征数据不是很准确,因此我们要求数据要更实时、更准确。特征工程的第二个需求是服务稳定性。首先是作业容错,比如作业在异常的时候能否正常恢复;另外是数据质量,在实时数据里追求端到端精确一次。■2.1点击率预估下面是在特征实时化里的实践,首先是点击率预估的需求。点击率预估案例的背景如上所示,从投放链路上来说,在广告前端用户产生观影行为,前端会向广告引擎请求广告,然后广告引擎在做广告召回粗排/精排的时候会拿到用户特征和广告特征。把广告返回给前端之后,后续用户行为可能产生曝光、点击等行为事件,在做点击率预估的时候,需要把前面请求阶段的特征跟后续用户行为流里的曝光和点击关联起来,形成一个Session数据,这就是我们的数据需求。落实到具体实践的话包括两方面:一方面是Tracking流里曝光、点击事件的关联;另一方面是特征流跟用户行为的关联。在实践过程中有哪些挑战?第一个挑战是数据量;第二个挑战是实时数据乱序和延迟;第三个挑战是精确性要求高。在时序上来说,特征肯定是早于Tracking,但是两个流成功关联率在99%以上的时候,这个特征需要保留多久?因为在广告业务中,用户可以离线下载一个内容,在下载的时候就已经完成了广告请求和返回了。但是后续如果用户在没有网的情况下观看,这个事件并不会立马返回,只有当状态恢复的时候,才会有后续曝光和点击事件回传。所以这个时候,其实特征流和Tracking的时间概括是非常长的。我们经过离线的数据分析,如果两个流的关联率达99%以上,那么特征数据就需要保留比较长的时间,目前是保留7天,这个量级还是比较大的。上图为点击率预测的整体架构,刚才我们提到关联包括两部分:第一个部分是用户行为流里曝光跟点击事件的关联,这里通过CEP实现。第二个部分是两个流的关联,前面介绍特征需要保留7天,它的状态较大,已经是上百TB。这个量级在内存里做管理,对数据稳定性有比较大的影响,所以我们把特征数据放在一个外部存储(Hbase)里,然后和HBase特征做一个实时数据查询,就可以达到这样一个效果。但是因为两个流的时序本身可能是错开的,就是说,当曝光、点击出现的时候,可能这个特征还没有到,那么就拿不到这个特征。所以我们做了一个多级重试队列,保证最终两个流关联的完整性。■2.2点击率预估-流内事件关联上图右边是更细的讲解,阐述了流内事件关联为什么选择CEP方案。业务需求是把用户行为流里属于同一次广告请求,并且是同一个广告的曝光跟点击关联起来。曝光之后,比如5分钟之内产生点击,作为一个正样本,5分钟之后出现的点击则抛弃不要了。可以想象一下,当遇到这样的场景,通过什么样的方案可以实现这样的效果。其实在一个流里多个事件的处理,可以用窗口来实现。但窗口的问题是:如果事件序列本身都在同一个窗口之内,数据没有问题;但是当事件序列跨窗口的时候,是达不到正常关联效果的。所以当时经过很多技术调研后,发现Flink里的CEP可以实现这样的效果,用类似政策匹配的方式,描述这些序列需要满足哪些匹配方式。另外它可以指定一个时间窗口,比如曝光和点击间隔15分钟。上图左边是匹配规则的描述,begin里定义一个曝光,实现曝光之后5分钟之内的点击,后面是描述一个可以出现多次的点击,within表示关联窗口是多长时间。在生产实践过程中,这个方案大部分情况下可以关联上,但是在做数据对比的时候,才发现存在某些曝光点击没有正常关联到。经过数据分析,发现这些数据本身的特点是曝光跟点击的时间戳都是毫秒级别,当它们有相同毫秒时间戳的时候,这个事件就不能正常匹配。于是我们采用一个方案,人为地对于点击事件加一毫秒,进行人工错位,这样就保证曝光跟点击能够成功关联上。■2.3点击率预估-双流关联前文提到特征数据需要保留7天,所以状态是上百TB。需要把数据放在一个外部存储里,因此在做技术选型时对外部存储有一定的要求:首先支持比较高的读写并发能力;另外它的时效性需要非常低;同时因为数据要保留7天,所以它最好具备生命周期管理能力。基于以上几个点,最终选择了HBase,形成上图的解决方案。上面一行表示通过CEP之后把曝光点击序列关联在一起,最下面是把特征流通过Flink写到HBase里,去做外部状态存储,中间核心模块是用于达到两个流的关联。拿到曝光点击关联之后去查HBase数据,如果能够正常查到,就会把它输出到一个正常结果流里。而对于那些不能构成关联的数据,做了一个多级重试队列,在多次重试的时候会产生队列降级,并且在重试的时候为了减轻对HBase的扫描压力,重试Gap会逐级增加。另外还有一个退出机制,因为重试不是无限进行的。退出机制的存在原因主要包括两个点:第一点是特征数据保留了7天,如果对应特征是在7天之前,那么它本身是关联不到的。另外在广告业务里,存在一些外部的刷量行为,比如刷曝光或刷点击,但它本身并没有真实存在的广告请求,所以这种场景也拿不到对应特征。因此,退出机制意味着在重试多次之后就会过期,然后会到重试过期的数据里。■2.4有效点击在有效点击场景里,其实也是两个流的关联,但是两个场景里的技术选型是完全不一样的。首先看一下项目背景,在网大场景里,影片本身就是一个广告。用户在点击之后,就会进入到一个播放页面。在播放页面里,用户可以免费观看6分钟,6分钟之后想要继续观看,需要是会员或者购买才行,在这里需要统计的数据是有效点击,定义是在点击之后观影时长超过6分钟即可。这种场景落实到技术上是两个流的关联,包括了点击流和播放心跳流。点击流比较好理解,包括用户的曝光和点击等行为,从里面筛选点击事件即可。播放行为流是在用户观看的过程,会定时地把心跳信息回传,比如三秒钟回传一个心跳,表明用户在持续观看。在定义时长超过6分钟的时候,需要把这个状态本身做一些处理,才能满足6分钟的条件。在这个场景里,两个流动Gap相对比较小,而在电影里时长一般是两个多小时,所以点击之后的行为,Gap基本是在三个小时以内才能完成,因此这里本身的状态是相对比较小的,使用Flink的状态管理可以达到这样的效果。接下来我们看一个具体的方案。从流上来看,绿色部分是点击流,蓝色部分是播放心跳流。在左边的状态里面,一个点击事件进来之后,会对这个点击做一个状态记录,同时会注册一个定时器做定期清理,定时器是三个小时。因为大部分影片的时长在三小时以内,如果这个时候对应的播放事件还没有一个目标状态,点击事件基本就可以过期了。在右边的播放心跳流里,这个状态是对时长做累计,它本身是一个心跳流,比如每三秒传一个心跳过来。我们需要在这里做一个计算,看它累计播放时长是不是达到6分钟了,另外也看当前记录是不是到了6分钟。对应Flink里的一个实现就是把两个流通过Connect算子关系在一起,然后可以制定一个CoProcessFunction,在这里面有两个核心算子。第一个算子是拿到状态1的流事件之后,需要做一些什么样的处理;第二个算子是拿到第2个流事件之后,可以自定义哪些功能。算子给用户提供了很多灵活性,用户可以在里面做很多逻辑控制。相比很多的InputJoin,用户可发挥的空间比较大。■2.5特征工程-小结针对以上案例做一个小结。现在双流管理已经非常普遍,有许多方案可以选择,比如Windowjoin,Intervaljoin,还有我们使用的Connect+CoProcessFunction。除此之外,还有一些用户自定义的方案。在选型的时候,建议从业务出发,去做对应的技术选型。首先要思考多个流之间的事件关系,然后判断出状态是什么规模,一定程度上可以从上面很多方案里排除不可行的方案。三、Flink使用过程中的问题及解决1.容错在Flink内部主要是通过Checkpoint做容错,Checkpoint本身是对于Job内部的Task级别的容错,但是当Job主动或异常重启时,状态无法从历史状态恢复。因此我们这边做了一个小的改进,就是一个作业在启动的时候,它也会去Checkpoint里把最后一次成功的历史状态拿到,然后做初始化管理,这样就达到状态恢复的效果。2.数据质量Flink本身实现端到端精确一次,首先需要开启Checkpoint功能,并且在Checkpoint里指定精确一次的语义。另外,如果在下游比如Sink端,它本身支持事务,就可以结合两阶段提交与Checkpoint以及下游的事务做联动,达到端到端精确一次。在上图右边就是描述了这个过程。这是一个预提交的过程,就是Checkpoint协调器在做Checkpoint的时候,会往Source端注入一些Barrier数据,每个Source拿到Barrier之后会做状态存储,然后把完成状态反馈给协调器。这样每个算子拿到Barrier,其实是做相同的一个功能。到Sink端之后,它会在Kafka里提交一个预提交标记,后面主要是Kafka本身事务机制来保证的。在所有的算子都完成Checkpoint之后,协调器会给所有的算子发一个ACK,发送一个确认状态,这时候Sink端做一个提交动作就可以了。3.SinkKafka在之前的实践中我们发现,下游Kafka增加分区数时,新增分区无数据写入。原理是FlinkKafkaProducer默认使用FlinkFixedPartitioner,每个Task只会发送到下游对应的一个Partition中,如果下游Kafka的Topic的Partition大于当前任务的并行度,就会出现该问题。解决办法有两个:第一个办法是用户自定义一个FlinkKafkaPartitioner;另一个办法是默认不配置,默认轮询写入各个Partition。4.监控加强对于运行中的Flink作业,我们需要查看它本身的一些状态。比如在FlinkUI里面,它的很多指标都是在Task粒度,没有整体的效果。平台这边对这些指标做了进一步的聚合,统一在一个页面里面展示。从上图可以看到,展示信息包括反压状态,时延情况以及运行过程中JobManager和TaskManage的CPU/内存的利用率。另外还有Checkpoint的监控,比如它是否超时,最近是否有Checkpoint已经失败了,后面我们会针对这些监控指标做一些报警通知。5.监控报警当实时任务运营异常的时候,用户是需要及时知道这个状态的,如上图所示,有一些报警项,包括报警订阅人、报警级别,下面还有一些指标,根据前面设置的指标值,如果满足这些报警策略规则,就会给报警订阅人推送报警,报警方式包括邮件、电话以及内部通讯工具,从而实现任务异常状态通知。通过这种方式,当任务异常的时候,用户可以及时知晓这个状态,然后进行人为干预。6.实时数据生产最后总结一下爱奇艺广告业务在实时链路生产上面的关键节点。我们的实时是从年开始起步,当时主要功能点是做一些指标实时化,使用的是SparkStreaming;年上线了点击率实时特征;年上线了Flink的端到端精确到一次和监控强化。年上线了有效点击实时特征;同年10月,逐步推进实时数仓的改进,把API生产方式逐渐SQL化;年4月,进行流批一体的探索,目前先把流批一体放在ETL实现。之前我们的ETL实时跟离线是分别做的,通过批处理的方式,然后换到Hive表里边,后面跟的是离线数仓。在实时里,经过实时ETL,放到Kafka里边,然后去做后续的实时数仓。先在ETL做流批一体的第一个好处是离线数仓时效性提升,因为数据需要做反作弊,所以我们给广告算法提供基础特征的时候,反作弊之后的时效性对于后续整体效果的提升是比较大的,所以如果把ETL做成统一实时化之后,对于后续的指导意义非常大。ETL做到流批一体之后,我们会把数据放在数据湖里面,后续离线数仓和实时数仓都可以基于数据湖实现。流批一体可以分为两个阶段,第一阶段是先把ETL做到一体,另外报表端也可以放在数据湖里边,这样我们的查询服务可以做到一个更新的量级。因为之前需要离线表跟实时表做一个Union的计算,在数据湖里面,我们通过离线和实时写一个表就可以实现了。四、未来规划关于未来规划:首先是流批一体,这里包括两个方面:第一个是ETL一体,目前已经是基本达到可线上的状态。第二个是实时报表SQL化和数据湖的结合。另外,现在的反作弊主要是通过离线的方式实现,后面可能会把一些线上的反作弊模型转成实时化,把风险降到最低。预览时标签不可点收录于合集#个上一篇下一篇 |
转载请注明地址:http://www.qingguoa.com/qgls/11023.html
- 上一篇文章: 专利申请公司如何投放广告
- 下一篇文章: 没有了