实践分享|流式系统集成服务的十倍速优化

文摘   2022-12-02 11:00  


背景介绍


1

Integration Engine的

3种服务集成模式

Partner Integration Engine(后简称IE)是 Marketing Tech 研发的一个提供统一的与外部第三方API服务做集成的平台。它的职责是:对内提供语义丰富、统一、稳定的交互接口,对外按照客户意图,翻译客户的原始需求与形形色色的外部服务提供商进行数据与服务交互。


简单来说,我们可以将其理解为一个数据的海关,在公司的内网与外网环境之间,控制着Marketing Tech部门数据的流入与流出,并屏蔽外部服务提供商的变化与复杂的交互流程。在需要与众多广告平台交互的营销系统中,IE扮演了至关重要的角色。

根据客户端提交请求特性的不同,IE提供3种不同的服务集成方式。

(点击可查看大图)

(1)对于内容简单、频率随机、数据量小,且要求即时回复的用户请求,IE走Speed模式的服务集成通道,有一个简明的IE-Speed子系统去简单代理用户请求至外部服务提供商。


(2)对于频率固定、单次请求数据量大,且可接受异步回复的用户请求,IE走Batch模式的服务集成通道,有一个IE-Batch系统批量地分解、处理用户的请求。


(3)对于24小时*7天,全天候持续产生数据、吞吐量大、且需要实时处理的请求,IE走Streaming模式的服务集成通道,有一个IE-Streaming平台来支持这部分数据的处理。


今天我们分享的主题,主要是针对第三种服务集成方式,即IE-Streaming平台升级改造过程中沉淀的一些实践经验与教训。

2

第一代IE-Streaming平台

与其局限性

第一代IE-Streaming平台于2019年第4季度开发上线。在这次彻底升级前,提供Facebook, Tiktok, AppsFlyer这3个外部服务提供商的系统集成服务。共有13条Dataflow运行在IE-Streaming平台上,日均处理record数量达到5.8亿。


典型的用例包括:eBay用户在商品页面行为的整理与同步、eBay商品清单在外部服务提供商处的更新与清理等。

技术上,第一代IE-Streaming平台采用Apache Beam的编程模型构建,采用Spark平台作为底层的Runner。Apache Beam是Google推出的一个雄心勃勃的大数据领域的开源编程模型,其核心理念在于以统一的方式定义批与流的数据处理的Pipeline。并且,当你采用Apache Beam的编程模型构建大数据应用的Pipeline后,可以任意选择Apache Beam支持的Runner,作为实际计算的执行引擎。


目前Apache Beam支持的Runner有Apache Apex、Apache Flink、Apache Gearpump(孵化中)、Apache Samza、Apache Spark 和 Google Cloud Dataflow等,其中Google Cloud Dataflow原生支持使用Apache Beam定义Pipeline。

对于不熟悉此技术的读者来说,Google推出Apache Beam,有点类似于当年Sun公司推出Java,背后的理念有点那种一次编写处处运行的味道。


而且理论上,可能也是Apache Beam社区追求的终极目标,即你只要深入掌握Apache Beam的精髓,你之后可以不用再去学习世间那些纷繁复杂的各种大数据平台了,就像你现在基本不用去学习各种不同架构下的汇编语言一样,Apache Beam会将你定义的符合编程规范的模型,翻译成不同runner上具体运行的job。这背后的战略考量 ,非常值得玩味。

第一代IE-Streaming平台运行了3年时间,为什么我们考虑对其做彻底的重构与升级呢?

 NO.1  


公司基础设施架构的变化与升级所带来的要求。其中也包括一些流程和合规方面的考量,因为与本文分享主题无关,此处就不予赘述了。

 NO.2  


Spark执行环境只能通过mini-batch的方式近似模拟流式应用的处理。我们希望我们能实现真正的流式实时处理。这里读者可能会有疑问,既然Apache Beam可以自由选择底层的执行引擎,那你们换一个支持真流式处理的执行引擎不就可以了么?这里就牵涉到第三个原因,我们马上阐述。

 NO.3  


第三点,可能也是最重要的一点,我们在过去三年的实际的生产实践过程中,我们发现使用Apache Beam构建的流式系统,具有如下显著的优缺点

优点


(1)理论上,能一次编写,处处运行(在不同大数据平台上)。但考虑到执行引擎的迁移并不会经常发生,且迁移有很多额外成本,涉及公司内部流程与资源、权限申请等,所以这个优点对于我们而言,意义不大;


(2)批流统一,编程模型简单易用;


(3)它是Google Cloud Dataflow指定的原生官方语言。但我们的应用还是考虑在公司内部支持的大数据平台上运行,因此这个优点对我们而言,也不是特别明显。

缺点

(1)由于翻译层的存在,生产环境中,Apache Beam难以维护,定位问题不直接,DAG图会变形不直观;


(2)缺少对翻译的控制,有时翻译出的Job会有许多冗余的步骤。我们还观察到,某些情况下,本来可以在一个mini-batch内完成的任务,在翻译过后的job中,会分配到2个连续的mini-batch中运行,而我们对翻译的过程几乎没有控制的能力;


(3)特性较为滞后,性能优化空间小。由于Apache Beam试图兼容所有主流大数据平台,这意味着它很多时候只能支持各主流主流数据平台的特性的交集。许多平台自己特有的最新特性,难以被采用,Pipeline深层次的性能优化也因此受阻。

我们仔细分析了IE-Streaming平台的应用场景,对于ApacheBeam的优点,我们并不能充分利用,而ApacheBeam的缺点,我们每天的日常工作与维护中都会遇到。


因此,我们决定彻底在代码中去除Apache Beam,改用纯Flink技术栈升级我们的Streaming平台,以节省日常维护中的工作量与复杂性。(后文会叙述我们选择Flink的原因)

 NO.4  


业务方面,我们需要支持一个新的Partner: Google与其相关的新用例。Google新用例最终将会使我们平台每天处理的数据量翻倍,而我们基于Apache Beam的第一代IE-Streaming Platform,其性能调优已经做到了极限。因此,我们希望通过技术栈更换,使我们有能力直接操作大数据平台的原生底层API并使用其最新特性,来对IE-Streaming平台做彻底的升级改造。


3

第二代IE-Streaming平台的设计

目标与技术选型

除了降低维护成本,提升性能以支持新的业务场景的考虑,我们还希望通过这次平台的彻底升级,实现一些框架层面的优化,我们的设计目标如下,其中方案部分后文会有更详细的叙述

(点击可查看大图)


我们之所以最终选用Flink作为项目升级的目标技术选型,是从技术与支持,一硬一软两个方面考虑的。

从技术方面来说

(1)Flink天然支持流式应用与实时数据处理;

(2)Flnik内置Checkpoint与Backpressure机制,开箱即用;

(3)Flink的很多底层API,是public接口,允许深度定制与优化。

从支持方面来说

(1)我们的应用需要整合Kafka与Flink,这两个组件在我们公司内是同一个Team支持的基础设施,出现问题不用分别找2个Team协调;

(2)这个Team的技术支持水平很高,对Flink与Kafka的理解十分深入;

(3)迁移到Flink平台,我们能申请独立的集群,做到良好的资源隔离,满足公司的一些合规要求。

分享以上这些内容,我们其实是想说明,在决定目标技术的选型时,除了硬的技术层面的考量,很多软的方面的考量对于项目的成功实施同样重要,而这部分内容是因公司而异,需要具体问题具体分析的。


系统设计


1

4层结构设计

类似于OSI网络的7层模型设计,我们将第二代IE-Streaming平台划分为4层结构。其中,每层结构为上一层结构提供构建单元,除了基础设施层,每层结构都对外暴露可定制的接口。如图所示,这4层结构自底向上依次为:

1. 基础设施层;

2. 预定义算子层;

3. 预定义Pipeline层;

4. 具体服务集成层。

(点击可查看大图)

因为Integration Engine的业务特性,很多算子与Pipeline的定义在不同服务与API的集成中都是可重复利用的。


我们通过将他们单独抽象成不同层次上,可插拔的构件,当我们需要集成新的服务时,只需要像搭建乐高积木一样,取出事先定义的算子与Pipeline进行组合,就能以最少的代码量快速上线新服务的注册与支持

2

单节点链式执行

在大数据应用中,Shuffling是一个非常消耗资源的操作,数据被迫在同一DC甚至不同DC之间的节点间传输。一般而言,当你需要做诸如求和或者算平均值这类操作时,将同属一个key的数据聚合在一起是必要的,否则你就不能得到正确的结果。


在第一代IE-Streaming平台中,我们花费了相当多的资源执行数据的shuffle操作。在此次平台升级过程中,我们仔细梳理了业务场景,发现在很多业务场景下,我们并不需要如此强一致的归并操作。在大多数用例中,同一节点的pre-partition已经足以满足我们的业务需求。


在另一些key高度敏感的操作中,我们也可以通过让上游发送Kafka Message时,设置按照不同key放入不同partition的方法,预先将输入整理好。


借助于Flink最新的将预分类数据转译为keyedStream的特性,我们专门设计了一个预定义的Pipeline可以全程避免数据在节点之间的传输,以提高性能。单节点链式执行也使我们可以任意横向拓展系统,提升系统性能。

3

平台内置原生异步处理特性

在第一代IE-Streaming平台中,与外部服务提供商的交互都是同步进行的。异步操作依赖于服务在平台上的具体集成方式,只有少数服务使用了异步的集成操作,且他们的代码实现中有很多重复工作。

通过梳理业务场景,我们意识到在大多数用例中,都可以通过异步调用增加吞吐量,减少请求处理的总等待时间。因此,我们借助于Flink的Asynchronous I/O特性,在平台的预定义pipeline中,内置了Api的异步调用方式。

通过将服务注册在不同类型的pipeline上,服务的集成可以无缝在同步调用与异步调用之间切换。IE-Streaming平台是一个I/O密集型的系统,内置异步调用极大地提高了系统的吞吐量,缩短了系统的响应时间。



4

Exactly once与服务降级

Flink应用重启时,会从上一个保存点出发,重新处理数据,因此存在数据重复处理与发送的可能。


●  Flink文档有阐述其自带的End To End的Exactly once特性,比如在读写Kafka的情况下。然而,这种Exactly once高度依赖与Flink交互的组件是否具备回滚的能力,对于IE-Streaming平台来说,其交互对象为第三方服务商提供的服务。我们无法保证第三方服务提供回滚的能力,因此严格意义上的Exactly once,我们无法保证


●  然而,我们仍然努力通过NoSql的Cache机制,在大多数情境下实现Exactly once。实现的方法如下:我们的每个待处理的record都带有唯一可识别的traceId,我们会在与第三方服务交互的前一刻,在cache中查找待处理record 的状态。


当record显示可进一步处理时,我们才会实际执行与第三方服务的交互。在交互结束后,我们会设置这个record的进一步处理状态为已处理。这样当Flink应用重启时,只要Cache服务可达,我们就不会重复处理、发送数据






Flink的Checkpoint机制保证了At-Least-Once的语义,配合Cache机制保证的At-Most-Once语义,我们可以在大多数服务集成中实现Exactly-Once。


有读者可能会问,如果Cache不可用的情况下,我们如何应对呢?一旦NoSql Cache不可用,默认情况下,我们的平台会侦测到这种情况,并降级为At-Least-Once的语义。降级的逻辑在服务集成时是可配置的,默认行为可被覆盖。


5

染色Record机制

生物学中,当我们试图理解复杂的生物组织内发生了什么时,我们有时可以注入一些经过染色或带有放射性的有机体,通过观察有机体在组织内的运动过程与轨迹,获得对生物组织的状态、规律的更深刻的理解。


借鉴这个思想,我们在设计实现第二代IE-Streaming平台时,设计了一个染色Record机制,用于做日志采样与更方便的运维工作。我们在每个record自带的traceId上做文章,根据traceId format的不同,我们将Record类型分为以下三种:

1. Normal record;

2. Dyeing record;

3. Action record。

对于Dyeing Record,IE-Streaming平台会特别对待,将其一路上经过的路径打点,并对运行环境信息,算子内关键变量的值进行更详细的记录。


这套方案虽然不复杂,但在我们日常的运维、排查问题的工作中,起了非常大的帮助。当我们需要对Pipeline做诊断时,我们可以主动注入一些染色record,通过染色record在Pipeline中留下的信息,来辅助我们的问题诊断。


我们还设计了一种Action record,其traceId内还包含在IE-Streaming平台内预先注册的函数名。通过Action record在Pipeline中的流动,我们获得了动态影响、乃至改变Pipeline行为模式的能力。目前这一块暂时还没有特别好的实际用例,我们会在后续的工作中继续探索Action record的应用场景。

6

主动限流

经过升级的第二代IE-Streaming平台在性能上有了非常大的提升(详见后文实验对比部分 ),以至于在生产环境中,我们很容易就会越过服务提供商给我们提供服务的Quota限制。因此我们在框架内引入了主动限流的模块,根据第三方服务商提供的服务协议,配置Quota,擦着Quota的上限进行服务集成与交互。


这个模块核心利用了Integration Engine小组开发的另一个独立组件Quota Limiter,其背后是一套线程安全的基于令牌桶算法的解决方案 。这套解决方案广泛运用于Integration Engine的各个子系统中,未来有机会我们也可以对这方面的内容进行介绍。


实验对比与结论


1

处理速度

我们对比了Google Delete Page Feed Item 这一业务场景,在第一代和第二代IE-Streaming系统上的表现。实验表明,在并行度统一设置为60的情景下,第一代平台需要25分钟处理800万的数据量,而在第二代的平台上,我们只用了2分钟时间,速度提升了12.5倍

2

时延

通过将技术栈从Spark的mini-batch迁移到Flink的实时处理,我们将record处理的时延从5分钟降低到10秒以下


从下图中我们能清晰地看到,数据处理从一个个波浪型的mini-batch的处理模式,变成了平滑的实时处理模式。


(点击可查看大图)

(点击可查看大图)

3

可维护性

IE-Streaming平台升级到2.0版本后,每个月新提交的技术支持的ticket数量减少了66%,平均单个ticket从提交到resolve的时间,也从4.5小时缩短为1小时以内。无论对于客户还是组内当周oncall的同学都是很大的体验改善。


这些改进来源于架构演进后平台本身稳定性的提升,以及优化技术栈与加入染色record机制后,定位问题的便捷性带来的可维护性的提升。从回顾分析来看,这些改进其实还有进一步提升的空间,这会是我们后续继续努力的方向。


综述



本文回顾了我们升级Integration Engine的过程。包括我们对旧平台中存在的问题与瓶颈的认识,选择新平台技术栈时的考量角度,新平台中加入的新设计等。最后,我们对比了新旧平台,对同一个API服务集成时的性能优化与时延比较。我们相信第二代IE-Streaming平台还有很多可以优化的地方。我们撰写此文是希望,其他同学可以从我们的经历中吸取经验教训。谢谢大家的关注和时间!


END


eBay技术荟
eBay技术荟,与你分享最卓越的技术,最前沿的讯息,最多元的文化。
 最新文章