火山引擎 VeDI 数据服务平台:在电商场景中,如何解决 API 编排问题?
一、火山引擎 VeDI 数据服务平台介绍
数据服务平台简介
火山引擎 VeDI 是新一代企业级数据智能平台,基于字节跳动多年的“数据驱动”实践经验,汇集了端到端的数智产品、场景化的行业解决方案和专业的企业数智化转型咨询。
旗下大数据研发治理产品 DataLeap 的数据服务平台(下文简称数据服务平台)是 API 服务的一站式平台,能帮助用户将数据快速服务化,提供 API 创建、管理、运维和共享的全生命周期管理能力。
在保证服务高可靠性和高安全性的同时,为各业务线搭建数据服务统一出口,促进数据共享,为数据和应用之间建立了一座“沟通桥梁”,解决数据理解困难、异构、重复建设、审计运维困难等问题,实现统一且多样化数据服务以及数据应用价值最大化的目标。
暂时无法在飞书文档外展示此内容
暂时无法在飞书文档外展示此内容
在业务开发过程中,用户可以自行选择直查各种引擎,但各种引擎请求方式不同,返回数据格式不统一,需要针对各种引擎适配对应的查询代码,取数逻辑需要维护在代码中。这样的取数方式,会给研发带来较大的接入成本、运维成本。当取数逻辑变更也会需要研发更改代码上线,可能导致上线期间线上取数逻辑的不一致。
而数据服务平台创建的 API 统一了查询和数据返回的格式,用户只需维护 API 中 SQL 的取数逻辑和请求参数即可,大大降低了上层应用获取数据的成本。
领域介绍
用户可以在数据服务平台上创建:项目、数据源、物理表/逻辑表、就绪时间、API、API 编排,App 应用(psm)等主体资源;其中数据源包含查询数据库连接信息;物理表是底层数据源中的表;逻辑表是数据服务平台的概念,目的是能够增强物理表的能力,例如逻辑表监控报警、切换主备、授权调用等等;就绪时间是数据的最新可用日期;API 和 API 编排是数据出口,应用即消费者身份标识。
暂时无法在飞书文档外展示此内容
架构概览
火山引擎 VeDI 的数据服务平台整个系统主要分为两个服务,平台服务和引擎服务。在过去的一段时间内,我们主要从质量、效率、成本几个方面对数据服务平台的能力进行建设,在保障数据服务核心通道能力质量同时,提高用户使用平台承接数据链路需求的效率,降低使用数据服务的成本。当前,数据服务平台已经具备了以下能力。
暂时无法在飞书文档外展示此内容
质量:保障数据通道的稳定与安全,为用户提供事前监控告警以及事中容灾的平台能力。
监控告警
监控能力:数据服务引擎对查询的全部流量的,请求状态、耗时情况进行上报。基于上报数据,为用户搭建了 API 粒度监控看板,可以对 API 调用的 SLA、PCT99 等指标进行监控和分析。
告警能力:数据服务平台在 API 创建过程会为 API 绑定默认的失败率告警规则,并支持用户在平台上直接为 API 配置失败率以及耗时相关的告警。
权限管控
项目级别:可以对 PSM 授予整个项目的权限,即 PSM 对该项目下所有 API 都有调用权限。
API 级别:可以对 PSM 授予 API 的权限,授权后,PSM 具备该 API 的调用权限。
限流能力
数据源:平台支持在数据源粒度上配置限流,即可以控制存储集群或库的整体从数据服务平台的出口流量的大小,主要用于存储并发能力较低的情况。数据源上层的所有物理表、逻辑表、API 链路会共享数据的限流配置,能够有效防止突发流量对存储整体造成的冲击。
API:平台支持在 API 粒度上配置限流,主要用于 API 共享场景,API Owner 可以通过设置 API 限流,防止整体流量对底层造成过大压力,API 上的所有访问 PSM 会共用该限流配置。
API x PSM:平台支持对 API 配置访问 PSM 的精确限流,对于每个 API 已授权的 PSM 可以进行限流,超过 QPS 配置后,该 PSM 调用当前 API 的后续请求都会被拒绝。
切流能力
数据源切流:数据服务平台支持在数据源上进行集群粒度的同机房、跨机房切流。在数仓提前进行了数据双链路建设的情况下,能够在集群整体故障、或者其他区域机房故障的情况下,对集群进行容灾切流。
逻辑表主备切换
整表切换:逻辑表上支持对物理表配置备份表,并且能够一键切换链接的物理表,提供了表级别的容灾切流能力。
字段切换:对于 NoSQL 类存储,数据服务平台在整表备份的基础上,还提供了细化到字段级别的备份和切流能力。
API 版本管理:数据服务平台的 API 提供了版本管理的能力
一键回退:在 API 误上线的情况,我们支持对版本进行一键回退。
版本灰度:基于 API 版本,我们支持了版本灰度发布(完全随机灰度、逐渐 HASH 灰度)能力,用户基于版本管理的新数据口径可以灰度发布到线上。
暂时无法在飞书文档外展示此内容
效率:
多种 API 形式:
脚本式:支持自行编写 API 的查询 SQL,该方式可满足高阶需求,支持选择同源多张逻辑表进行处理。 同时也支持了动态 SQL(XML)。适用于同源多表 Join 等复杂 SQL 查询场景。
向导式:无需代码编写,在界面勾选配置即可快速生成 API(仅支持单张逻辑表)。请求参数为 Where 条件、返回参数为 Select 字段,系统自动生成查询语句。 简单易上手,适用于简单取数场景。
原生式:支持用户灵活查询数据集的一种 Query,目标是对在圈选范围内逻辑表进行灵活的重组查询,适合数据分析面板类场景。
指标查询 API:结合 Nuwa 的指标、维度配置,组合为一个 API 进行指标查询。
API 编排能力:第二节详细介绍。
函数能力:可以创建自定义函数,支持 Python,JAVA 等方式,在 API 前置或后置执行,以满足特定的数据处理。
逻辑表轻加工
逻辑资产主题:数据服务平台的逻辑表支持了轻加工能力,用户可以将多张物理表或者逻辑表,通过主键关联的形式,聚合成一张新的逻辑表。主要用于构建逻辑宽表、数据主题、逻辑数仓 APP 层建设等场景。
运维中心
运维大盘:提供整体的运维概况,支持按照项目或者业务线的粒度,来聚合查看整体 API、数据源、物理表、逻辑表、PSM 的使用情况。提供了包括 QPS、SLA、PCT 以及活跃率等多种信息。让开发者和运维人员更方便地掌握数据资产的运行状况,能及时发现问题,来保证用户服务的稳定性和可用性。
自助分析:采用了 AI 技术来构建智能助手,提供智能问答和查询分析两种能力。
智能问答:用户可以输入在使用过程中遇到的各种问题,基于数据服务平台的文档中心,分析助手会提炼出可能的解决方案和可供参考的文档链接。
查询分析:用户输入 LOGID,系统会自动诊断本次查询过程中数据服务出现的报错情况,以及耗时情况。结合报错信息,会给用户提供诊断意见。这个能力在部分场景提高线上问题排查定位的效率,解决了上层业务接入数据服务后对查询链路问题黑盒的情况。
成本:目标帮助平台用户对数据服务平台的数据资产进行成本管理和治理,本模块能力处于建设中,未来会上线成本中心功能。
二、API 编排能力介绍
API 编排简介
什么是 API 编排?
随着 API 的数量不断增加,单一的 API 调用已经无法满足复杂业务流程的需求。这就像我们平时做菜的时候面对众多食材,单靠直觉和经验可能难以保证每次都能烹饪出完美的菜肴。这时候,我们需要一本食谱来指导如何将食材结合起来,使之成为一道佳肴。在 API 的世界里,这本食谱就是 API 编排。
解决什么问题?
一个复杂的业务逻辑可能需要多个 API 的参与,业务侧还需要编写大量的代码将多个 API 关联使用,如果关联逻辑发生变更,还需要研发修改代码上线来支持,这样的模式,研发成本较高、可维护性较差。
基于类似的场景,下面举几个例子以帮助大家来更好的理解 API 编排可以解决哪些复杂业务场景:
目前有两个 API,一个是获取实时数据的 API,另一个是获取离线数据的 API,根据就绪时间判断是否要获取离线数据,如果就绪时间未就绪就查实时 API,已就绪就查离线 API。
业务场景中使用了多方数据源,想实现异构数据源的数据整合。
想对 API 的返回参数进行一些数据处理或者计算,例如电商平台希望根据用户的购买历史和浏览行为来提供个性化的产品推荐。API 节点首先调用产品信息 API 和用户行为 API 获取所需数据,然后编程节点对这些数据进行分析和处理,最终生成推荐列表。
API 编排主要是依赖各个 API 节点来拓展 API 的数据能力,所以想要高效快速实现一个 API 编排,API 的生产是第一步。同时 API 编排的测试、调用和 API 一样,可以把一个 API 编排视为一个 API。
暂时无法在飞书文档外展示此内容
针对编排中目前已有的七个节点,下面表格中有详细的介绍:
原理介绍
编排架构
暂时无法在飞书文档外展示此内容
API 编排目前作为 DataLeap 数据服务平台较为进阶的功能,一方面需要保证调用方式和原有 API 调用一致,尽量让用户以最小的方式接入编排;另一方面又要求数据服务平台的引擎在执行编排过程中无需进行复杂的元信息拼装。我们将编排的元信息拼接到了 API 的元信息中,这样编排也能复用 API 的一些例如 QPS、缓存等配置信息。
因为考虑到目前 API 元信息结构已经比较复杂,在一些 SQL 较长,输入输出参数、绑定逻辑表较多的业务场景中属于大 Key,大 Key 的元信息会一定程度上导致引擎执行耗时长,元信息存储使用的 redis、abase 的内存消耗大等一系列问题。所以我们并没有将编排内所有节点的元信息一并组装到 API 元信息中。目前元信息中存储的是编排的调用链路,各个节点的元信息作为单独的 key 单独存储。
暂时无法在飞书文档外展示此内容
编排执行原理
整体方案设计
在 API 编排 中,主要有两类元素:节点和有向边,节点是进行数据处理的基本单元,也是数据服务平台提供给用户进行自定义数据处理的节点;有向边是数据传输的抽象表示,是用户设置的数据输入和输出。
暂时无法在飞书文档外展示此内容
调度执行流程图:
暂时无法在飞书文档外展示此内容
数据服务平台的编排调度能力如下:
编排 DAG 合法性检测,例如:环的校验、孤立节点检测
节点可以选择性调度下游节点,没有被任意上游节点调度的节点不会被执行
节点的超时设置、限流、重试
日志和埋点接入
每个节点的状态机如下:
暂时无法在飞书文档外展示此内容
详细方案设计
数据服务平台的编排任务主要是让输入数据通过特定的工作流加工后,返回输出结果。编排服务主要是工作流的构建和调度,与很多其它的编排调度系统类似,核心在于构建 DAG(Directed Acyclic Graph,有向无环图)。
数据服务平台的编排调度与其它编排调度框架构建 DAG 图 Directed Acyclic Graph,有向无环图)并调度执行的不同之处在于:
只有一个开始节点和一个结束节点
数据经过每个节点的加工之后,可以有选择的传递给下游若干个节点
部分节点可以在运行时有选择的调度它的下游节点,而不是全部调度
OneService 对编排调度框架的要求是高性能、低时延,易扩展,根据自己的需求,我们开发了一套适合 OneService 的编排调度框架,主要细节如下:
静态构建 DAG 结构,运行时节点可以参数和判断条件动态选择调度下游
数据保存在各个节点的输入输出中,减少存储在公共变量中导致的竞态访问
通过任务队列保存可以并发执行的任务,同一任务队列中的任务并发执行
任务之间相互独立,任务与调度器解耦
代码 UML 类图如下:
调度节点设计
每个节点会转换成一个执行任务,结构定义如下:
暂时无法在飞书文档外展示此内容
数据封装设计
暂时无法在飞书文档外展示此内容
调度器设计
暂时无法在飞书文档外展示此内容
调度执行逻辑
在初始时,根据元信息构建 DAG,并进行有效性验证,然后初始化每个节点的 inDegree 和 skipInDegree 为上游节点依赖数,最后按照调度执行逻辑进行调度,在全部任务执行完成之后,返回最终数据或者执行中发生的错误。
三、API 编排能力在电商场景的最佳实践
场景一:直播大屏分钟数据的冷热分离
业务背景
直播大屏是面向电商主播、商家,提供直播间核心实时数据,在直播过程中依据实时数据做相关决策,比如在流量下降时投流、发福袋等,是典型实时盯盘场景。数据团队提供的实时数据需具备查询 QPS 高、数据延迟秒级、数据更新秒级、数据准确性高和稳定性高的特点,尤其是当大屏上核心指标出现的短暂 GMV 停更、分钟数据跌 0 的情况,如 5~8 分钟,会有大面积用户客诉。
为了满足实时数据的上述特点,我们的存储选型主要是 Abase,数据格式是 hash 结构,此结构在分钟数据 or 大 in 的场景下,查询放大非常的明显,极端情况下可能会打爆底层存储,出现大量查询失败,从而导致线上故障的发生。
当前痛点
Abase Hgetall 性能低下,频繁出现查询失败的问题:当前我们的分钟数据主要是 Abase hash 列存表,在 OneService 的查询过程中,底层是使用了 hgetall 指令,该指令的性能比较差,会走到 rocksdb 的 scan 操作,由于 scan queue 限制可能导致部分 hgetall 命令查询失败,OneService 查询会报查询你失败。因 scan queue 的 size 是 abase 侧配置的,为了对磁盘进行保护,不可能无限制的调大 scan queue。为了解决此问题,我们对所在的核心 Abase 集群 HashScan 进行摸查,查询流量 TOP2 表分别是直播间分钟、直播间商品分钟,流量达到了 85%以上。因此我们只需要完成这两份数据的迁移,很大程度上可以缓解查询失败频繁的问题,达到提升 Abase 集群整体的稳定性、降低 OS 服务的查询失败率
数据下游服务多,服务端实现编排逻辑,切换成本高:下游服务 20+,不同业务方都需实现一堆 ifelse 编排逻辑,代码可读性较低,彼此间不能复用,按照以往切换经验来说,一般需花费一个 Q 下游才能切换完成
底层存储持续迭代时,服务端均需迭代,迭代成本高:当后续切换到新存储时,下游服务端需要再走一次之前的切换流程,如新建 API->服务端编排逻辑调整->新服务测试->新服务发布等等,迭代成本比较高
解决方案
冷热数据分离:直播间开播是冷热分布非常的明显,即开播 7 天内直播间占比达到 99.9%以上,因此直播大屏上的查询同样具备冷热分布的特点,我们只需要在热查询时换存储,如 redis,承担主要的查询压力,冷数据的查询打到 abase,通过这样的查询策略,可以很大程度上缓解 Abase 集群的压力,同时保证了大屏链路的稳定性
使用 API 编排来实现冷热数据编排逻辑:以直播间分钟趋势 API 编排接口为例,如下图,整体链路中,最核心的是分支节点,通过是否开播 7 天的标识位分别查询对应的冷热数据,即开播 7 日内读 redis,开播超过 7 日的读取 abase
PS:API 编排对应的时间比较函数还不支持整型,我们需要服务端对开播时间进行简单加工,未来时间比较函数支持整型的话,可以去掉此处加工逻辑
效果 &收益
服务加工逻辑清晰:通过 API 编排的能力,我们实现了可视化拖拽式服务开发,无需盘点底层的代码逻辑,一眼识别出服务加工逻辑,对服务后续迭代、线上问题排查都非常的友好;
服务逻辑切换口径统一:口径使用 API 编排来收敛,理论上均可通过 PSM 授权来接入,但由于涉及到跨团队权限问题,目前是相同项目下可授权使用,不同项目下可复制对应的 API 编排接口后使用
迭代成本低:后续再遇到底层存储切换类似的场景,只需要在 API 节点替换新的 API,走一次 API 编排的版本发布即可
大屏稳定性提升:下游切换完成后,Abase 集群的 hgetall 查询次数下降 77.6%(19.34 亿->4.3 亿),下游查询基本不会再出现失败频繁的情况
场景二:电商天指标的实时离线数据切换
业务背景
某大屏产品上需透出电商一些天指标,一般的查询流程为 当离线数据未就绪时用实时数据兜底,离线数据就绪后直接查询对应的离线数据表,返回离线数据,并在产品上进行相关数据披露
当前痛点
服务端维护实时离线切换的代码逻辑,分散在各处,迭代成本高:在数据服务平台完成对应逻辑表就绪时间回调配置,会生产对应的回调任务,下游服务端会使用 OS 平台提供的 RPC 接口拿到离线表的就绪时间,跟查询参数中的日期进行大小比对后,从而实现实时离线切换的逻辑,当遇到底层存储频繁切换时,迭代成本高
下游大量使用原生式 API,动态生成的查询 SQL 支持多个场景,运维成本极高:因历史技术债务,下游使用了大量的原生 API,同一个 API 可复用在多个场景,由于白泽目前是不支持此类 API 字段级别血缘,因此不能准确识别到此类 API 到底有几种查询、具体的返回结果等,当线上出现问题时,需要服务端人肉检索出相应的查询语句,问题排查定位时间花费大,运维成本极高
解决方案
API 编排支持就绪时间:分支节点能够拿到离线表的就绪时间,基于就绪时间进行判断是读离线数据,还是实时数据
以电商分载体天指标实时离线切换接口为例,我们对图中核心的两个节点,进行简单阐述:
分支节点,实现实时离线切换逻辑:
读离线数据接口:查询日期 = 离线就绪日期 or 就绪日期> 查询日期,即 date = LOGIC_TABLE_DATE_PARTITION(logic_table_id) or LOGIC_TABLE_DATE_PARTITION(logic_table_id) > date
读实时数据接口:查询日期 为 今日 or ( 日期为昨日,离线就绪日期 < 昨日)即
date = curdate or LOGIC_TABLE_DATE_PARTITION(logic_table_id) < date
合并节点,实现实时离线接口数据合并逻辑:建议是两边 API 返回参数完全一致,且采用 Append 模式合并,未来的话能够支持 API 返回参数不一致,取他们的并集来实现合并
效果 &收益
API 编排收口实时离线切换逻辑,加工逻辑清晰,迭代成本低:通过 API 编排的能力,我们实现了可视化拖拽式服务开发,无需盘点底层的代码逻辑,一眼识别出服务加工逻辑,后续若遇到存储切换 or 其他类似场景的话,前者切换对应的 API 节点,后者直接在现有的 API 编排接口上另存新编排接口,只需要替换相关 API 接口即可,开发迭代成本低
下游使用脚本式 API,能够建立清晰的产品模块->API->OS 表的消费血缘,大幅度降低 API 运维成本:使用 API 编排的附加收益,我们能够建立比较系统且全面的消费端血缘,当线上遇到问题时,能够快速定位到 OS 表、查询脚本以及影响的产品模块,并能给出恰当的止损手段,能有效提升线上问题的排查效率
评论