事件驱动架构在 vivo 内容平台的实践
Posted vivo互联网技术
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了事件驱动架构在 vivo 内容平台的实践相关的知识,希望对你有一定的参考价值。
作者:vivo互联网服务器团队-Gao Xiang
一、什么是事件驱动架构
当下,随着微服务的兴起,容器化技术的发展,以及云原生、serverless 概念的普及,事件驱动再次引起业界的广泛关注。
所谓事件驱动的架构,也就是使用事件来实现跨多个服务的业务逻辑。事件驱动架构是一种设计应用的软件架构和模型,可以最大程度减少耦合度,很好地扩展与适配不同类型的服务组件。在这一架构里,当有重要事件发生时,比如更新业务数据,某个服务会发布事件,其它服务则订阅这些事件;当某一服务接收到事件就可以执行自己的业务流程,更新业务数据,同时发布新的事件触发下一步。
事件的发布与订阅,需要依赖于一个可靠的消息代理。见下图:
当然,事实上有不少软件项目都使用了消息队列,但是这里需要明确的是,对消息队列的使用并不意味着你的项目就一定是事件驱动架构,很多项目只是由于技术方面的驱动,小范围地采用了某些消息队列的产品而已。偌大一个系统,如果你的消息队列只是用作邮件发送的通知,那么这样系统自然谈不上采用了事件驱动架构。
在采用事件驱动架构时,我们需要考虑业务的建模、事件的设计、上下文的边界以及更多技术方面的因素,这个系统工程应该如何从头到尾的落地,是需要经过思考和推敲的。总而言之,“事件驱动架构”的设计并不是一件易事。本文在后面有个例子供参考。
另外,如果盲目使用事件驱动设计架构,就有可能要承担中断业务逻辑的风险,因为这些业务逻辑具有概念上的高度内聚,却采用了解耦机制将它们联系在一起。换句话说,就是将原本需要组织在一起的代码强行分离,并且这样难于定位处理流程,还有数据一致性保证等问题。为了防止我们的代码变成一堆复杂的逻辑,我们应当在某些明确场景下使用事件驱动架构。以经验来讲,以下三 种场景可以使用事件驱动开发:
组件的解耦
执行异步任务
跟踪状态的变化
二、什么时候使用事件驱动架构
2.1 组件的解耦
当服务(或组件) A 需要执行服务 B 中的业务逻辑,相比于直接调用,我们可以向事件代理(事件分发器)中发送一个事件。服务 B 通过监听分发器中的特殊事件类型,然后当这类事件被接收到时去执行它。
这意味着服务 A 和服务 B 都依赖于事件代理和事件,而无需关注彼此实现:即完成它们的解耦。见下图:
基于这种松耦合,服务可以用不同的语言实现。解耦后的服务能够轻松地在网络上相互独立地扩展,通过动态添加或删除事件生产者和消费者来修改他们的系统,而不需要更改任何服务中的任何逻辑。
2.2 执行异步任务
有时我们会有一系列需要执行的业务逻辑,但是由于它们需要耗费相当长的执行时间,所以我们不想看到用户耗费时间去等待这些逻辑处理完成。在这种情况下,最好将它们作为异步任务来运行,并立即向用户返回一条信息,通知其稍后继续处理相关操作。
比如,内容字段的检查等入库流程可以采用“同步”执行处理,但是执行内容理解则采用”异步“任务去处理。在这种情况下,我们所要做的是触发一个事件,将事件加入到任务队列中,直到一个服务能够获取并执行这个任务。此时,相关的业务逻辑是否处在同一个上下文中环境中并不重要,不管怎么说,业务逻辑都是被执行了。
2.3 跟踪状态的变化
在传统的数据存储方式中,我们通过实体模型存数据。当这些实体模型中的数据发生变化时,我们只需更新数据库中的行记录来表示新的值。这里有个问题,就是业务上我们无法准确存储数据的变更和修改时间。但是在事件驱动架构中,可以通过事件溯源将包含修改的内容存入到事件里。下面会详细讨论“事件溯源“。
三、为什么使用事件驱动架构
当大家谈论事件驱动架构时,比如大家说自己恰好在最近的项目中采用了事件驱动架构,实际上,他们可能在谈论下面这四种模式中的一种或者几种:
事件通知
事件承载状态转移
事件溯源
CQRS
注:概念来源2017年GOTO Conference上Martin Fowler分享的The many meanings of Event-Driven architecture。
3.1 事件通知
假设我们现在想要设计一个简易的内容平台,包含三部分:
内容引入系统
作者微服务
关注中心
当内容创作者通过内容引入系统上传视频之后,会触发如下的一个调用流程见下图:
内容引入系统收到创作者上传的视频,执行入库流程;
内容引入系统调用作者微服务的API,增加“视频-创作者”的从属关系;
作者服务调用关注中心的API,让关注中心给关注了这个创作者的其他用户发送作者视频更新的通知。
上面这个调用流程,不可避免地创建了下面的依赖关系:
内容引入系统依赖于作者微服务的API,虽然内容引入系统其实不太关心作者微服务的业务。
作者微服务依赖于关注中心的API,虽然作者微服务也不关心关注中心的业务和处理流程。
这种依赖关系很有可能并不是我们所期望的。内容引入系统是一个比较通用的业务,不同类型的内容引入系统很可能会有相似功能,如字段类型检查、入内容库、启动高敏审核等。作者服务则是一个非常专业的系统,如不同源、不同类型的内容关于作者的业务逻辑是不同的。让一个通用的系统依赖于一个专业的系统,不管从设计角度,还是后续系统维护角度,都是不一个好的方案。作者微服务可能会经常根据业务需求做变更,但内容引入系统相对稳定,而上面这种依赖关系让我们难以在“不对内容引入系统做调整的情况”下随意更改作者微服务。
从架构层面,我们希望让作者微服务依赖于内容引入系统,让一个专业的系统依赖于一个稳定的、通用的系统,增加系统的稳定性。这个时候我们可以借助于“事件通知”。见下图:
优点
架构更健壮。如果加入队列的事件能够在源组件中执行,但在其它组件中由于 bug 导致其无法执行(由于将其加入到队列任务中,它们可以在 bug 修复后再执行)。
业务处理减少延迟。当用户无需等待所有的逻辑都执行完成时,可以将这类工作加入到事件队列。
便于系统扩展,能够让组件的研发团队独立开发,加快项目进度、降低功能难度、减少问题发生并且更有组织性。
将信息封装在“事件”里,便于系统内传播。
缺点
如果没有合理使用,可能使我们的代码变成“面条式”代码。
数据一致性问题。由于流程依赖于最终的一致性,因此通常不支持ACID事务,因此重复或乱序事件的处理会使服务代码更加复杂,并且难以测试和调试所有情况。
“事件通知”的缺点和优点相对应,正是因为它提供了很好的解耦能力,我们会比较难通过阅读代码去得到整个系统和流程的全貌。因为这些逻辑之间的关系不再是之前的依赖关系。这将会是一个挑战。
3.2 事件承载状态转移
我们在使用事件通知时,事件里面往往不会包含下游系统处理这个事件需要的所有信息。比如当内容发生下架变更时,内容平台会生成一个“内容下架“的事件,但当下游系统处理这个事件时,往往还需要知道,该内容上个状态是什么,是谁触发下架等信息,才能完成后续处理。所以不可避免地,下游系统在处理这个事件时,往往还需要通过平台服务来获取这些额外信息。
为了解决这个问题,我们引入一个种新的模式,叫做“事件承载状态转移”。简单来说,就是让事件的消费方自己保留一份在业务处理过程中需要用到的上游系统的数据。比如让下游系统保留一份在处理内容状态变更事件时所需要用到的内容变更前的状态,避免回头去平台查询。
优点
架构更健壮。减少事件消费方对生产方的额外依赖(获取事件处理所需数据);
业务处理减少延迟。增加事件消费方系统的响应速度,因为不再需要调用平台API以获取事件处理所需数据;
无需担心被查询组件的负载(尤其是远程组件)。
缺点
尽管现在数据存储已经不再是问题根源,依然会保存多个只读的数据副本,一致性进一步被破坏;
增加数据处理的复杂度,即使处理逻辑符合规范,它也需要额外处理和维护外部数据的本地副本业务逻辑。
3.3 事件溯源
有些时候我们不但关心系统当前的状态,我们还关心如何变成当前这个状态的,但是数据库仅仅简单地保存实体的当前状态。事件溯源可以帮助我们解决这个问题。
事件溯源是一个特别的思路,它并不持久化实体对象,而是只把初始状态和每次变更的事件记录下来,并在内存中根据事件还原实体对象的最新状态,mysql主从备份用到的binary log以及redis的aof持久化机制,都可以认为是“事件溯源”的实现。
事件溯源在做完数据库更新之后,它将事件的发送操作转换为往数据库或者日志系统中写入一条事件记录,其它节点通过查询数据库或者文件系统,来得到这些事件,并通过回放来确保数据的最终一致性。
优点
可以呈现一个完整的变动历史;
提供更方便的debug手段;
可以回溯到任何一个历史状态;
方便修改当前事件;
缺点
要实现一个可靠和高性能的事件仓库(保存的事件记录)并不是一件容易的事情,应用代码需要根据事件库的 API 进行重写。
3.4 CQRS
CQRS全称是Command Query Responsibility Segregation。简单来说,就是针对系统的读写操作,使用不同的数据模型、API接口、安全机制等,来达到对读写操作的完全隔离,满足不同的业务需求。见下图:
根据存储在事件库中的事件集合,可以计算得到每个业务实体的状态,这些状态以物化视图的方式存储在一个数据库中。当有新的事件产生时,也同样会自动更新视图。这样,视图查询服务就可以像查询普通的数据库数据一样实现各种查询场景。具体的设计可参考下图所示:
四、事件驱动架构在内容平台中的实践
在当今社会,内容“横行”的时代,内容平台企业需要有极强的灵活性和应变能力。特别是在中国这样一个内容行业(如视频)飞速发展的市场里,企业要求平台能够快速地对内容业务需求做出应对,否则就会丧失先发优势。这有点类似于现代战争条件下,各国都要求部队具备快速反应能力,这种能力主要体现在平台能够通过快速开发或者重用 / 整合现有资源来达到快速响应业务需求。
随着内容行业业务越来越庞大复杂,所涉及的存储类型、处理器、账号体系、效率工具、数据和结算系统等非常多,这就要求平台有很强的整合能力以及对异构环境的适配能力。
最后,由于内容行业的发展日新月异,特定类型的内容业务(如小视频)都会在其初中期发展后迎来一个快速膨胀期,业务量和业务类型会急剧增加,这也要求平台有很好的可扩展性。相关平台架构见下图:
4.1 创建事件
事件其实是DDD(领域驱动设计)中的一个概念,表示的是在一个领域中所发生的一次对业务有价值的事情,落到技术层面就是任何影响业务流程或者状态的改变。事件具有自己的属性,比如发生的时间、发生了什么、事件之间的关系、状态以及变化,事件也可以生成新的事件,根据不同的事件生成新的业务事件。在创建事件时,首先需要记录事件的一些通用信息,比如唯一标识ID和创建时间等,为此创建事件基类ContentEvent:
AbstractContentEvent
eventId;
publisher;
receiver;
Long publishTime;
String videoId;
Article article; VideoStatusChangeEvent VideoEvent
preStatus; status;
vivo 敏感词匹配系统的设计与实践
前端质量提升利器-马可代码覆盖率平台
vivo 全球商城:商品系统架构设计与实践
vivo互联网机器学习平台的建设与实践
vivo 互联网产品团队 - Wang xiao
随着广告和内容等推荐场景的扩展,算法模型也在不断演进迭代中。业务的不断增长,模型的训练、产出迫切需要进行平台化管理。vivo互联网机器学习平台主要业务场景包括游戏分发、商店、商城、内容分发等。本文将从业务场景、平台功能实现两个方面介绍vivo内部的机器学习平台在建设与实践中的思考和优化思路。
一、写在前面
随着互联网领域的快速发展,数据体量的成倍增长以及算力的持续提升,行业内都在大力研发AI技术,实现业务赋能。算法业务往往专注于模型和调参,而工程领域是相对薄弱的一个环节。建设一个强大的分布式平台,整合各个资源池,提供统一的机器学习框架,将能大大加快训练速度,提升效率,带来更多的可能性,此外还有助于提升资源利用率。希望通过此文章,初学者能对机器学习平台,以及生产环境的复杂性有一定的认识。
二、业务背景
截止2022年8月份,vivo在网用户2.8亿,应用商店日活跃用户数7000万+。AI应用场景丰富,从语音识别、图像算法优化、以及互联网常见场景,围绕着应用商店、浏览器、游戏中心等业务场景的广告和推荐诉求持续上升。
如何让推荐系统的模型迭代更高效,用户体验更好,让业务场景的效果更佳,是机器学习平台的一大挑战,如何在成本、效率和体验上达到平衡。
从下图可以了解到,整个模型加工运用的场景是串行可闭环的,对于用户的反馈需要及时进行特征更新,不断提升模型的效果,基于这个链路关系的基础去做效率的优化,建设一个通用高效的平台是关键。
三、vivo机器学习平台的设计思路
3.1 功能模块
基于上图业务场景的链路关系,我们可以对业务场景进行归类,根据功能不同,通用的算法平台可划分为三步骤:数据处理「对应通用的特征平台,提供特征和样本的数据支撑」、模型训练「对应通用的机器学习平台,用于提供模型的训练产出」、模型服务「对应通用的模型服务部署,用于提供在线模型预估」,三个步骤都可自成体系,成为一个独立的平台。
本文将重点阐述模型训练部分,在建设vivo机器学习平台过程中遇到的挑战以及优化思路。
1.数据处理,围绕数据相关的工作,包括采集、加工、标记和存储。
其中,采集、加工、存储与大数据平台的场景相吻合,标记场景是算法平台所独有的。
- 数据采集,即从外部系统获得数据,使用Beesvivo数据采集平台来采集数据。
- 数据加工,即将数据在不同的数据源间导入导出,并对数据进行聚合、清洗等操作。
- 数据标记,是将人类的知识附加到数据上,产生样本数据,以便训练出模型能对新数据推理预测。
- 数据存储,根据存取的特点找到合适的存储方式。
2. 模型训练,即创建模型的过程,包括特征工程、试验、训练及评估模型。
- 特征工程,即通过算法工程师的知识来挖掘出数据更多的特征,将数据进行相应的转换后,作为模型的输入。
- 试验,即尝试各种算法、网络结构及超参,来找到能够解决当前问题的最好的模型。
- 模型训练,主要是平台的计算过程,平台能够有效利用计算资源,提高生产力并节省成本。
3.模型部署,是将模型部署到生产环境中进行推理应用,真正发挥模型的价值。
通过不断迭代演进,解决遇到的各种新问题,从而保持在较高的服务水平。
4. 对平台的通用要求,如扩展能力,运维支持,易用性,安全性等方面。
由于机器学习从研究到生产应用处于快速发展变化的阶段,所以框架、硬件、业务上灵活的扩展能力显得非常重要。任何团队都需要或多或少的运维工作,出色的运维能力能帮助团队有效的管理服务质量,提升生产效率。
易用性对于小团队上手、大团队中新人学习都非常有价值,良好的用户界面也有利于深入理解数据的意义。
安全性则是任何软件产品的重中之重,需要在开发过程中尽可能规避。
3.2 模型训练相关
模型训练包括了两个主要部分,一是算法工程师进行试验,找到对应场景的最佳模型及参数,称之为“模型试验”,二是计算机训练模型的过程,主要侧重平台支持的能力,称之为“训练模型”。
建模是算法工程师的核心工作之一。建模过程涉及到很多数据工作,称为特征工程,主要是调整、转换数据。主要任务是要让数据发挥出最大的价值,满足业务诉求。
3.2.1 模型试验
特征工作和超参调整是建模过程中的核心工作。特征工作主要对数据进行预处理,便于这部分输入模型的数据更好的表达信息,从而提升模型输出结果的质量。
数据和特征工程决定模型质量的上限,而算法和超参是无限逼近这个上限。
超参调整包括选择算法、确认网络结构、初始参数,这些依赖于算法工程师丰富的经验,同时需要平台支持试验来测试效果。
特征工程和超参调整是相辅相成的过程。加工完特征后,需要通过超参的组合来验证效果。效果不理想时,需要从特征工程、超参两个方面进行思索、改进,反复迭代后,才能达到理想的效果。
3.2.2 训练模型
可通过标准化数据接口来提高快速试验的速度,也能进行试验效果的比较。底层支持docker操作系统级的虚拟化方案,部署速度快,同时能将模型直接部署上线。用户无需对训练模型进行更多定制化的操作,批量提交任务能节约使用者的时间,平台可以将一组参数组合的试验进行比较,提供更友好的使用界面。
其次,由于训练的方向较多,需要算力管理自动规划任务和节点的分配,甚至可以根据负载情况,合理利用空闲资源。
四、vivo机器学习平台实践
前面我们介绍了机器学习平台的背景和发展方向,现在我们来介绍下,平台在解决用户问题部分的困扰和解决思路。
4.1 平台能力矩阵
机器学习平台主要目标是围绕模型训练进行深耕,并辅助用户进行模型决策,更快的进行模型部署。
以此为目标分为两个方向,训练框架的优化能够支撑大规模模型的分布式计算,调度能力优化能够支持批次模型的执行。
在调度能力上,平台由原生k8s调度,单个训练调度的效率较低,升级为kube-batch批量调度,到以混合云精细化编排为目标,当前主要处于灵活性调度策略的形式。
在训练框架上,从原生Tensorflow模型,随着特征和样本规模的扩大,自研了超大规模的训练框架vlps,当前处于TensorFlow+vlps结合的新框架状态。
4.2 平台能力介绍
平台能力建设主要围绕模型试验和训练模型的运用,运用过程中遇到的痛点和难点如何解决,是我们在实践中的关键。同时,训练框架也是平台关键能力的体验,基于业务的复杂度,持续对框架进行优化。
已覆盖公司内部算法工程师模型调试的工作,已达到亿级样本,百亿特征的规模。
4.2.1 资源管理
痛点:
机器学习平台属于计算密集型的平台。
- 业务场景不同,是否完全按照业务分组进行资源划分;
- 资源池划分过小,会导致资源利用率低且没办法满足业务激增的资源诉求;
- 资源不足以满足业务诉求时,会存在排队情况导致模型更新不及时;
- 如何管理好算力,提效与降本的平衡,是平台资源管理的一个核心问题。
解决思路:
资源管理的基本思路是将所有计算资源集中起来,按需分配,让资源使用率尽量接近100%。任何规模的资源都是有价值的。
比如,一个用户,只有一个计算节点,有多条计算任务时,资源管理通过队列可减少任务轮换间的空闲时间,比手工启动每条计算任务要高效很多。多计算节点的情况,资源管理能自动规划任务和节点的分配,让计算节点尽量都在使用中,而不需要人为规划资源,并启动任务。多用户的情况下,资源管理可以根据负载情况,合理利用其它用户或组的空闲资源。随着节点数量的增加,基于有限算力提供更多业务支持是必经之路。
1.以配额限资源滥用:
新增配额组和个人配额,减少业务之间的相互干扰,尽可能满足各组的资源需要,并且配额组支持临时扩容和共享,解决偶发性激增的资源诉求;限额后用户仅支持在有限资源下使用,让用户自我调节高优先级训练。
2.以调度促资源优化:
新增生产环境,确认模型已经正常迭代,在合理利用率的情况下切换至高优环境,提供更高性能的资源池;同时提供调度打分机制,围绕资源颗粒度、配置合理性等维度,让合理的训练资源更快的拉起,减少调度卡住情况;
上线多维度调度打分机制后,平台不合理训练任务有大幅度下降,资源效率提升。
围绕并不限于以下维度:最大运行时长、排队时长、cpu&内存&gpu颗粒度和总需求量等。
4.2.2 框架自研
痛点:
随着样本和特征规模增加后,框架的性能瓶颈凸显,需要提升推理计算的效率。
发展路径:
每一次的发展路径主要基于业务量的发展,寻求最佳的训练框架,框架的每一次版本升级都打包为镜像,支持更多模型训练。
当前效果:
4.2.3 训练管理
痛点:
如何支持多种分布式训练框架,满足算法工程师的业务诉求,让用户无需关心底层机器调度和运维;如何让算法工程师快速新建训练,执行训练,可查看训练状态,是训练管理的关键。
解决思路:
上传代码至平台的文件服务器和git都可以进行读取,同时在平台填写适量的参数即可快速发起分布式训练任务。同时还支持通过OpenAPI,便于开发者在脱离控制台的情况下也能完成机器学习业务。
围绕训练模型相关的配置信息,分为基础信息设置、资源信息设置、调度依赖设置、告警信息设置和高级设置。在试验超参的过程中,经常需要对一组参数组合进行试验。
批量提交任务能节约使用者时间。平台也可以将这组结果直接进行比较,提供更友好的界面。训练读取文件服务器或git的脚本,即可快速执行训练。
1.可视化高效创建训练
2. 准确化快速修改脚本
3. 实时化监控训练变动
4.2.4 交互式开发
痛点:
算法工程师调试脚本成本较高,算法工程师和大数据工程师有在线调试脚本的诉求,可直接通过浏览器运行代码,同时在代码块下方展示运行结果。
解决思路:
在交互式工具中进行试验、开发,如:jupyter notebook,提供所见即所得的交互式体验,对调试代码的过程非常方便。
在交互试验的场景下,需要独占计算资源。机器学习平台需要提供能为用户保留计算资源的功能。如果计算资源有限,可对每个用户申请的计算资源总量进行限制,并设定超时时间。
例如,若一周内用户没有进行资源使用后, 就收回保留资源。在收回资源后,可继续保留用户的数据。重新申请资源后,能够还原上次的工作内容。在小团队中,虽然每人保留一台机器自己决定如何使用更方便,但是用机器学习平台来统一管理,资源的利用率可以更高。团队可以聚焦于解决业务问题,不必处理计算机的操作系统、硬件等出现的与业务无关的问题。
五、总结
目前vivo机器学习平台支撑了互联网领域的算法离线训练,使算法工程师更关注于模型策略的迭代优化,从而实现为业务赋能。未来我们会在以下方面继续探索:
1.实现平台能力的贯通
- 当前特征、样本还是模型的读取都是通过hdfs实现的,在平台上的告警、日志信息都未关联上,后续可以进行平台能力贯通;
- 平台的数据和模型还有标准化的空间,降低学习成本,提升模型开发的效率。
2. 加强框架层面的预研
- 研究不同分布式训练框架对模型效果的影响,适配不同的业务场景;
- 提供预置的参数,实现算法、工程、平台的解耦,降低用户的使用门槛。
以上是关于事件驱动架构在 vivo 内容平台的实践的主要内容,如果未能解决你的问题,请参考以下文章