腾讯内容生态实时信号系统实践
导读: 在内容生态,会围绕海量数据产生大量实时信号场景,本文将介绍千亿级实时计算优化思路、统一的规则引擎触发系统、实时高可用保障手段,带读者深入浅出理解实时体系的建设。
主要内容包括以下四大部分:
-
实时信号应用
-
问题与挑战
-
架构与解决方案
-
未来规划
分享嘉宾|杨浩 腾讯 研发工程师
编辑整理|晏世千 斑马网络
出品社区|DataFun
01/实时信号应用
腾讯内容中台提供从内容生产、内容加工、内容分发、内容结算等全链路环节的一站式服务,在这个过程中,会产生大量的数据以及围绕这些数据衍生的实时流业务应用,如内容周期智能管理,内容加工智能路由,内容创作精细运营等。
1. 内容周期智能管理
为满足不同用户的体验,需要给内容进行多种场景适配,随着内容不断增加,服务商成本非常高。为此,我们提供了一种基于内容周期提供分级服务的能力,在保障整体体验的前提下,可有效降低成本。
2. 内容加工智能路由
内容加工场景中有很多的加工精度,会抽象为一个微服务的编排系统。其中会有调度器控制任务的分发、路径寻优、弹性伸缩等工作。通过实时的数据采集, 如算法、耗时、加工状态等信息,并进行实时流加工,产生不同算法的实时效果特征信号,将这个实时信号反馈给调度器。可以进一步提高调度效果,减少加工耗时,提高处理成功率。
3. 内容创作精细运营
互联网平台主要围绕着技术、产品和运营,运营是一个非常关键的环节,运营人员会不断发布精细化运营活动,创作者会领取运营任务并发文。基于实时计算的消费量、互动量等特征信号,可以进行活动达标判断,进而将激励实时触达给创作者,提升运营活动效率。
--
02/问题与挑战
在上述场景中会遇到很多问题和挑战,可以抽象为四个方面:数据源、信号加工、信号触发和数据治理。
1. 数据源
腾讯内部各个业务方生产的数据各异,且拥有各自的 ID 体系;随着业务发展,数据源还会动态添加消息 Topic,需要实时动态感知新增的数据源,并以中台统一的 ID 视角串联各个业务的内容数据。
2. 信号加工
内容加工时会产生较多的复杂计算需求,比如,我们需要在有限资源内保障 TB 级多条实时数据拼接工作,以及长时间运行下需要对实时流应用的计算口径进行调整而面临的批数据重建流式数据状态等问题,我们探索了一系列自研技术,解决了海量数据实时流计算问题。
3. 信号触发
内容生态系统很多场景依赖实时信号,并且基于规则进行控制和流转,烟囱式开发有较大成本,我们需要构建一套日千亿次匹配的规则引擎信号服务,保障资源共享,实现新增场景一键配置即可支持。
4. 数据治理体系
我们希望建立全流程、全生命周期地建设数据治理体系。主要有以下几个可通用的方法论:
**(1)可观测性:**链路长,感知和定位问题慢。需要一个流程可观测系统,帮助我们快速解决问题。
**(2)退场机制:**互联网迭代非常快,探索性需求多,计算机系统资源开销大。需要建立一套退场机制,当一些服务项失效的时候,及时将资源释放,来降低服务的成本。
--
03/架构与解决方案
接下来,针对以上难点和挑战,来介绍一下我们的解决方案。
1. 整体架构
腾讯内容生态实时信号系统的整体架构如下图所示。
自下而上来看,首先,原始数据包括各个业务渠道的生产流水、加工流水和消费流水等。
接着是数据接入,通过动态多源、ID 映射、渠道衍生、数据清洗等能力,保证基础数据的完整性和可用性。
再往上是信号生产,包括日千亿次滑动大窗口计算,延迟流数据滚动大窗口计算,实时流数据拼接,融合批数据重建流状态保障服务的不中断,单体流量适应水平拓展保障程序不出现瓶颈,以及提供了输出小文件数自适应流量的能力解决小文件过多的问题。
规则引擎,主要提供统一的规则引擎和触发系统,提供千亿次的匹配,高效系统分发,以满足各个业务系统的需求。
数据治理,主要包括,保证系统的高可用性,一套可观测性系统帮助我们快速地分析和解决问题,通过数据退场机制来降低成本,通过分层体系(常规的数据仓库的建设,ODS 层,TWD 层,ADS 层等)保障数据的规范性和数据的可理解性,云数据管理系统将数据存在云端,供使用方查询,并保障数据安全。
最上面是信号应用。
2. 数据接入------动态实时源自适应感知
腾讯内容中台,提供一站式工业化的内容加工能力,每个业务方可自定义编排加工内容的任务流拓扑。为了稳定性和隔离性,每条任务流拓扑内容加工操作流水会生成一个 Topic,随着业务发展,新的 Topic 会不断增加,同时存量 Topic 数据量可能变大。因新增 Topic 所属集群地址差异大,Flink Source 无法用正则匹配到,导致程序无法自动感知。因此,我们设计了 Topic 动态添加的自适应感知的技术方案,可以做到:
(1)数据完整性:自动感知新添加的拓扑 Topic,保证数据不遗漏。
(2)数据时效性:存量的 Topic 数据量级变大时,能够自动扩容,保障整体时效性。
主要由以下几个模块构成:
**(1)控制器模块:**监测消息队列并通过配置中心异步控制Flink的消费。新增 Topic 时,注册到配置中心。Topic 数据量变大导致消费延迟时,增加该 Topic 的消费并行度。
**(2)配置中心:**存放所有拓扑的消息队列,如拓扑 ID、消费并行度、Kafka 配置。
**(3)Flink 自适应 Source:**自适应消费 Kafka 数据,保障数据完整性和时效性。在 Task 内开启消费线程池,负责 Kafka 的消费;并有自适应 Client,负责控制线程池的消费,每分钟执行一次,保障消费的完整性和时效性。具体步骤如下:
① 步骤 1:拉取所有消息队列配置。
② 步骤 2:生成本 Task 消费的 Topic 消费列表,保障并行度 N 的 Topic 会被 N 个 Task 消费。总 Task 数目是 M,每个 Task 会被分到如下 Task 中:hash(pipeline_id)% M 到(hash(pipeline_id)+ N)% M。遍历 Topic 可能被消费的 Task 列表,如果其中包含本 Task,则可对其进行消费。
③ 步骤 3:调整线程池消费列表,如果步骤 2 中添加了 Topic,则添加对应 Topic 的消费。
3. 数据接入------十万级 QPS 高并发 ID 映射
核心问题有两点:
(1)数据孤岛:各个渠道有自己的内容 ID 体系。
(2)资源开销大:十万级 QPS,IO 大,成本难以接受。
我们通过图中所示的二级缓存,构建了一套高并发 ID 映射的解决方案,能够打通中台 ID,同时节省百倍的计算资源。
ID 映射有二级缓存,一级是在 Flink 内构建的渠道 ID 到中台 ID 的映射,同时有一个远程的第三方服务,可以提供实时的映射。整体的执行机制为,当有消费流水进来时,首先判断在 Flink 应用内是否有缓存,如果有就直接返回中台 ID,如果没有,则进行远程的 ID 映射,更新 Flink 状态,返回中台 ID,最后给业务输出含中台 ID 的消费流水。
我们在 Flink 中构建了两种 cache,一种是可以映射的,将渠道 ID 映射到中台 ID,另一种是未能映射的渠道 ID 和映射值。我们会采用不同的机制,保障数据的时效性和可用性。对于可以映射的情况,为了应对映射状态的不断膨胀,我们将 TTL 设置了 7 天,同时设置了 LRU 的缓存机制来进行控制。而未能映射的情况,可能是当时没有映射上,而隔一段时间能够通过第三方 ID 映射服务重新映射到中台 ID。此时设置的 TTL 比较短,常为 1 小时。同时为了保障一段时间后仍然能映射上,采用了 FIFO 的机制,以保障映射的可用性,同时成本也能极大的降低。
4. 信号生产------滑动大窗口计算
在内容场景中,需要对内容消费数据的大时间窗口(如 24 小时)的每分钟滑动指标进行日千亿次的实时流计算,并基于这样的数据指标来控制业务流转,如果我们直接基于 Flink 内部的窗口函数,进行实时计算窗口指标时,因不能及时关闭窗口,状态数据会占用大量的内存,导致计算出现反压的情况,程序稳定性差,容易出现卡死现象。
基于上述挑战,我们设计了一种高性能的大窗口计算方法,主要有如下优点:
① 传统的方式需要每次对大窗口中的全量数据做计算,而现有方式可以复用前一次计算结果,可极大减少计算量。
② 我们方案中大窗口是逻辑上的大窗口,相比 Flink 原生的窗口计算会保留大窗口时间内的原始数据,我们在内存中并不存放这些原始数据,只存放算法提到的聚合维度的数据,同时利用数据淘汰机制,防止内存占用过大,节约大量的内存资源。
对实时流数据根据数据自身的事件时间是否连续分为如下不同的几种情况:
情况一:分钟级别滑动,每分钟窗口连续有流量的情况
当数据自身的事件时间连续的时候,我们需要拿到上次大窗口的计算结果值,在上次计算结果的基础上,对窗口的头部和尾部进行加减操作就可以得到新的大窗口的值。
分钟级滑动每分钟连续的大窗口
其中,T(6, 4)代表的是 6min 时候近 4min 的累计值大小,其中 6 代表的是当前最新时间,4 代表的是需要统计的窗口大小,是人为规定的。M(5) 代表的是第 5min 的值。
情况二:分钟级别滑动,每分钟窗口流量不连续情况
当间隔的时间小于窗口大小的情况下,计算当前窗口的值需要得到上一个窗口的值进行加减操作,由于数据自身的事件时间中断,所以要对最后一次窗口的值进行校准。
分钟级滑动每分钟不连续大窗口
其中,T(5, 4) 代表的是 5min 时候近 4min 的累计值大小,其中 5 代表的是当前最新时间,4 代表的是需要统计的窗口大小,是人为规定的,M(5) 代表的是第 5min 的值。
情况三:分钟级别滑动,每分钟窗口流量不连续并且当间隔的时间大于窗口的情况
当间隔的时间大于窗口大小的情况下,由于窗口时间内没有出现流量,可以直接认为大窗口的计算值为当前分钟流量值。
分钟级滑动每分钟不连续大窗口
其中,T(6, 4)代表的是 6min 时候近 4min 的累计值大小,其中 6 代表的是当前最新时间,4 代表的是需要统计的窗口大小,是人为规定的,M(5)代表的是第 5min 的值
5. 信号生产--超大滑动窗口的计算
还有一种场景是超大滑动窗口的计算,每分钟滑动一次,计算 30 天等超大滑动窗口指标。这种场景中状态极大,资源开销无法承受。以 30 天为例,如果只考虑半天的精度,可以将成本降低为千分之一,而精度只损失了百分之一,在成本和精度间达到了高效平衡。
如上图所示,计算单个内容 ID 的超大滑动窗口指标过程如下。
(1)状态更新:读取消费流水,更新该 ID 的状态值。
(2)计算超大窗口指标:基于应用内状态进行计算。具体如下:
① 如果内容产生时间在 N 天内:取累计流量。
② 如果内容产生时间在 N 天前:基于输入流量的时间取不同范围的数据,整体半天精度,如 30 天超大窗口的误差约 1.6%。时间区间划分如下:
a)00:00---15:00:取过去 N 天+当天流量值。
b)15:00---23:59:取过去 N-1 天+当天流量值。
6. 信号生产------TB 级实时流数据拼接
这里介绍的是 TB 级实时流数据拼接的场景。TB 级别数据拼接,计算慢、状态易丢失,Flink 难以支持高可用。通过引入第三方 KV 存储来备份状态,保证了高可用和秒级时效。
主要思路如上图所示,我们借助第三方 HBase 存储完成多流关联。
**阶段 1:特征拼接,**每个源单独加工,抽取自身特征后,进行如下过程:
① 步骤 1:将自身特征同步到 HBase 中,每个源只更新自身属性对应的列。HBase 中会包含每个内容最新最全的属性。
② 步骤 2:将有变更的内容推送到消息队列中。当前实现是将所有有变更的内容实时推送下游,可改造该过程,多流水位对齐后再推送,以支持多流拼接的多种语义。
在本阶段的存储设计中,HBase 的 Rowkey 为待关联的 Key,列分别为属性 Key 和属性值。同时,我们进行了大量优化设计:
① 批量访问:每 50 个 Key 合并访问,减少 IO。
② 随机主键:将 Key 进行 md5 哈希,让数据均匀分布在 HBase 中,防止热点,提高随机访问性能。
③ 存储压缩:部分属性值较大,将其序列化后,使用 GZIP 压缩,减少存储。
④ 过期机制:按需设置 TTL,防止数据无限膨胀。
**阶段 2:特征输出,**通过一个程序统一加工处理,可将每个内容的全量特征输出到目标业务系统中。
① 步骤 3:实时感知特征有变更的内容。
② 步骤 4:批量拉取内容的全量特征,HBase 中每一列对应一个特征,某个内容的全部列即为其全部特征。
③ 步骤 5:入库,将从 HBase 中获取的全量特征,转换成目标存储格式,输出到目标系统。
7. 基于规则引擎的实时信号触发器
根据很多配置规则,可以一键支持,秒级触发,按照规则分发给业务系统。
首先在规则管理平台 ,业务方配置规则;阈值可以通过预估模块去训练并进行更新,也可以手动设置。规则里面有静态规则或者动态规则。同时有一些阈值服务,包括阈值同步和阈值查询的能力。接着,在规则执行引擎 ,数据接入实时信号和消费流水,拉取到各个执行引擎里面,基于规则类型进行规则路由,分发到对应的动态规则匹配和固定规则模块匹配,进行相应的信号触发。配置加载 ,可以实时的加载阈值。设置信号去重的机制 ,可以保障同一信号短时间内不会重新触发给下游。之后进行数据的分发,产生的信号会按照规则,分发到各自的系统。
规则类型主要分为固定规则和动态规则。固定规则,即所有内容的阈值相同;动态规则,不同内容阈值可以精细化设置。动态规则的人力成本较高,但是对一些成本非常高的场景,可以降低整体成本。
规则定义可以分为规则条件表达式和规则动作。比如腾讯视频的流量大于多少就可以用一个条件表达式进行配置。同时会携带一些信息,比如去重周期等等。执行动作,是如何将匹配的信号分发给下游,通过 API 或者相应的消息队列。
执行优化有三方面。
**灵活引擎:**基于 Flink + Aviator, 就可以构建一个分布式的,支持规则动态添加和修改的轻量级的规则匹配引擎。
**二级缓存:**针对每一个输入信号,拉取其相关规则和阈值,因 IO 和 QPS 较高,整体成本非常大。二级缓存是规则 ID 和阈值 ID 直接去请求内容服务。然后引入二级缓存机制,通过拉取的阈值进行缓存之后,可以进行节省上百倍的资源。
**预编译:**规则执行的过程为,首先将内容流量转为 Map,表达式编译为字节码,进行执行,得到 True or False。如果是 False 就没匹配上。在整个过程中消耗比较大的是将表达式编译成字节码。构建表达式到字节码的缓存,该过程耗时会从 0.1ms 变成 0.04μs,在引入缓存的预编译以后,单次规则匹配就可以节省上千倍的算力开销。
8. 数据治理------端到端全链路服务可观测性
端到端的链路非常长,从 ODS 到 DWD, 到 DWS 层,到 ADS 层。对应一些延迟问题难以感知,有时需要业务方反馈才可知有延迟问题。同时当发现延迟以后的定位时间非常长。引入了服务可观测系统的能力,将延迟的感知和定位问题环节从小时级缩短到分钟级。
解决方案主要是引入了下面的几个模块:
**数据染色:**本模块集成到各个加工程序中,将本环节和上游各个环节的时效性信息染色到输出数据中,如事件时间、输出时间等。
**时效性统计:**因为每个环节的输出包含自身以及上游各个环节的时间信息,可基于某个环节的输出数据,统计从数据源到当前环节端到端分环节、分数据源的时效性信息。
**延迟监控:**基于统计模块计算的数据,监控端到端的延迟。
**可观测性分析工具:**可以提供全链路的延迟分析。用户可以选择对应的主题,个性化设置延迟的阈值,分析不同节点的延迟情况,当有延迟时,可以快速定位延迟源头。
9. 数据治理------退场
因为探索性需求多,成本不断膨胀,通过无效服务的下线,实现整体成本可控。通过看板无人使用的过程,与数据使用方的业务沟通来下线无人使用的数据看板。当确实无人使用的时候,把相对于的数据进行下线,节省成本。
同时还有一个解决方案是优化 TTL。部分服务场景起初需要去分析过去半年或者一年的数据,运行一段时间后,可能只用到过去一周的数据,这样我们就可以根据访问记录去和用户沟通。将一些热数据保存在实时的 OLAP 系统里面,一些冷数据通过离线进行分析,降低我们资源的成本 。
10. 数据治理------旁路系统保障状态高可用
部分场景对数据的一致性要求非常高,但是开发过程中,依赖众多,有小概率导致程序崩溃,当崩溃后程序状态就丢失了。引入旁路系统后,可以保障核心状态是 100% 可靠的。
我们构建了旁路系统,保障 Flink 状态异常丢失后核心状态的高可用。架构如图所示,主要由两个模块构成
① 旁路系统:程序外起一个异步作业,将核心状态从输出中实时同步到 Redis 中。
② Flink 应用内状态恢复模块:为访问 State 的前置逻辑,如果 Key 在应用内状态中丢失,则从远程 Redis 中恢复。
--
04/未来规划
未来规划主要在业务支撑、流批一体和资源优化三大方面。
(1)业务支撑。整体业务功能已经比较完备,接下来要更加关注精细化的运营需求,提高服务体验。还要进一步实现核心能力的实时化,提高推荐效果和分析决策的效率。
(2)流批一体,实现一套代码,两种执行模式;一套系统,统一技术栈;一套运维,统一资源调度。数仓开发主要分为计算和存储,存储将使用数据湖模式,同时探索用 SQL 来统一离线和实时的技术栈,来保证更高效的开发。
(3)资源优化。顺应降本增效的大环境,我们会探索资源弹性自适应技术的应用,和存储优化,进一步降低成本。
今天的分享就到这里,谢谢大家。
▌2023数据智能创新与实践大会
- 4大体系,专业解构数据智能
- 16个主题论坛,覆盖当下热点与趋势
- 70+演讲,兼具创新与最佳实践
- 1000+专业观众,内行人的技术盛会
第四届DataFunCon数据智能创新与实践大会将于⏰ 7月21-22日在北京召开,会议主题为新基建·新征程,聚焦数据智能四大体系:数据架构 、数据效能 、算法创新 、智能应用 。在这里,你将领略到数据智能技术实践最前沿的景观。
欢迎大家点击下方链接获取大会门票~
DataFunCon2023(北京站):数据智能创新与实践大会