关于推荐系统:vivo-应用商店推荐系统探索与实践

4次阅读

共计 3848 个字符,预计需要花费 10 分钟才能阅读完成。

介绍 vivo 利用商店举荐零碎如何高效撑持个性化的举荐需要。

一、前言

商店的利用数据次要来源于经营排期、CPD、游戏、算法等渠道,成立举荐我的项目之后也没有变动,发生变化的是由举荐零碎负责和数据源进行对接,商店服务端只须要和利用举荐零碎进行对接即可。

如果读者认为咱们单纯是把商店服务端代码给照搬到举荐零碎这边来了那就真的是 too young too simple 了,不做优化或者降级间接 copy 一个零碎是不可能的,这辈子都不可能。以下我将介绍咱们如何去设计和布局利用举荐零碎的。

二、面临的挑战

在笔者眼中,商店利用举荐零碎除了要具备高性能、高可用性及外围指标的监控能力之外,还有一个外围的能力就是高效撑持商店流量场景接入个性化举荐。

如何定义高效撑持?

  • 最起码能撑持三四个并行的需要同时进行吧。
  • 一个需要开发周期最起码不能超过 2 天吧。
  • bug 少一点吧,均匀下来每个场景不应该超过 2 个吧。
  • 产品同学的常态性的需要根本都能疾速反对吧。

分享咱们一个利用举荐的策动 case 看看:

在 xx 场景下,

如果主利用 A 属于利用类,

  • 则首先从从 x1 数据源去取 Q1 队列。
  • 而后从 x2 数据源去取 Q2 队列。
  • 而后用 Q2 队列去截断 Q1 队列,交加之后进行同开发者过滤和一级分类过滤。
  • 如果交加为空则用 Q2 去兜底,而后取交加队列的 n1 和 n2 地位上的元素作为返回队列。
  • 如果后面都没有取到数据的话从大数据 xxx 表中依照主利用下利用点击的概率取点击率最高的分类下的 n 个,同时须要对这些数据进行队列内的同开发者过滤。

如果主利用 A 属于游戏类,

  • 则 xxxx
  • 进行二级分类过滤
  • 如果量有余的话,则从 x(n)取数据而后进行解决,
  • 如果数据有余 3 个的话,须要从周榜单中取同一级分类下的利用依照下载排行进行兜底。

没错,读者敌人不要狐疑本人,为了不把各位读者大大绕晕,咱们这里只是筛选了一个简略的需要。实现这么一个性能也没有什么大不了的,然而当这种个性化举荐需要有几十个,前面还可能统一扩大上来的时候会不会心里发慌?来,简略看下咱们当初个性化举荐的一部分需要,如图(一)所示:

图(一)

应用商店服务端之前的 case by case 的开发计划,无论如何都无奈实现上文中形容的要撑持商店高效接入举荐场景了,接着就是咱们如何去实现优化的过程了。

三、如何解决

为了更好的阐明解决思路,咱们从理论思考过程登程,一步步解说问题的解决过程。

3.1 业务流程形象

单纯从策动下面来说,咱们每个场景都须要至多做如图(二)中的几件事件:

图(二)

  • 获取举荐列表:调用各个数据源获取的举荐队列(须要留神的是不同场景下调用的接口并不统一,此外接口返回的字段和构造可能也不一样)。
  • 队列交融:将 1 中提到的进行交加或者并集并等操作。
  • 数据过滤(队列内 / 队列间):在队列中进行各项过滤,过滤操作次要是为了晋升相关性。
  • 数据兜底:指在队列数据不够的时候,用榜单兜底,可能取周榜单数据的同一级分类数据,同二级分类数据。

笔者从开发便捷性登程,对模型进行了进一步的调整,调整后为图(三)

图(三)

获取队列后对队列进行装置过滤和队列内过滤 (如主利用同开发者过滤等) 能够进行流程合并,次要有如下的起因

  • 不便定义每一个数据源的过滤策略,理论需要中不同的队列也会应用不同的过滤策略。
  • 这种形式十分匹配模板设计模式,能确保咱们获取举荐列表过程是统一和稳固的。

3.2 形象流程延长

到图(三)这里,读者会发现咱们仍然没有可能解决咱们后面提到的各种举荐场景外面的差异化过程。

其实在接触几个需要当前,咱们会发现,想要在一套代码外面去解决这么大的差异性,简直不可能,或者即便实现了,那么也会让代码变得无比简单。与其这样子,咱们还不如正视这种差异性,让差别在场景插件外面去实现,咱们花更多的精力去打理骨干。

那么为了反对让场景可能具备灵便的扩大能力,笔者在基于图(三)的根底上减少了四个环节:

  • 队列后果线程内共享:应用 ThreadLocal 来实现。存储各举荐队列的后果次要是为了便于后续应用某举荐队列做填充的需要,另外就是防止须要再反复申请三方数据接口,缩小接口反复调用。
  • 插件队列兜底:次要目标是在过滤后在数量有余需要的状况下,应用指定的队列实现填充,场景插件亦可按需填写实现填充逻辑,实现队列内容的补充。
  • 插件接口回调:该环节次要是对后面的队列做个性化的解决,如对队列进行干涉等,没有将插件接口回调和插件队列兜底交融在一起次要起因是插件队列交融能够实现可配置化的设置。
  • 周榜单兜底:提供通用的周榜单数据查问能力,反对依照各种维度进行查问,此局部数据作为队列的最初兜底。

拓展后的流程图如图(四)所示

图(四)

3.3 整体逻辑框图

通过上述的剖析可知,咱们能够尽可能的把个性化的场景内容放在插件层实现,框架层负责加载按场景加载场景插件的具体个性化举荐逻辑。

零碎从分层思路上讲从上至下共分为:插件层,框架层,协定适配层,数据源服务层,原子服务层,根底服务层,下层通过 SDK 依赖上层的服务(接口),各层次职责为:

  • 插件层:各个场景对应的插件,框架层对插件回调或者扩大接口提供默认实现,插件层按需实现具体的逻辑。
  • 框架层:定义举荐数据的外围流程和执行逻辑,回调插件层的实现的扩大和回调接口。
  • 协定适配层:负责依照场景找到场景对应的数据源服务,并封装转换协定和进行数据转换。
  • 数据源服务层:与各个队列提供方提供的 RPC 服务封装层。
  • 原子服务层:过滤类型的相干服务,次要是依赖于商店的 RPC 服务,应用组合的设计模式,服务能够进行组合。
  • 根底服务层:反对从开发者、一级分类、二级分类、利用类型等纬度进行相关性的判断或者过滤,同原子服务层一样,此层服务也是原子粒度,反对进行组合管制。

至此,置信大家都通晓了,针对于个性化的举荐,咱们的开发工作最终将聚焦于开发场景插件,不须要再额定开发每一个业务流程了。

利用举荐零碎架构

3.4 要害实现

在实现第三步整体逻辑框图设计之后,咱们从场景参数定义,服务设计准则,设计模式应用,场景热插拔等方面进行了相干的计划钻研并最终实现了计划的落地。

3.4.1 场景服务参数定义

为实现举荐场景足够通用,咱们将数据源层,原子服务层,根底服务层的内容进行了服务配置的映射,通过在配置中定义对应的配置项来实现服务的映射和组合,针对于差异性的内容在插件层进行实现。以如下的配置项示意图来阐明:

  • sourceMap:场景服务定义为 map 用于反对场景下多个模块或者实验组的情景,其中 key 为模块 ID,商店服务端申请举荐的时候,须要携带此参数。
  • cpdRequest、algorithmRequest、gameRequest:用于定义对应的 RPC 调用的申请参数。
  • filterRequest:用于定义队列内的过滤申请,如主利用同开发者过滤等。
  • unionStrategy:定义队列合并和交融及队列间的合并规定。
  • supplement:兜底策略;
  • sourceList:应用的数据源,如上图中定义了两个数据源,则示意在此场景下须要从两个数据源获取数据,而后进行队列合并及后处理。

3.4.2 服务原子化与惟一化

实现服务原子化与服务惟一化对本零碎至关重要,在实现过程中是严格遵循如下两点来:

利用举荐依赖的三方 RPC 服务及外部的一些过滤逻辑都封装成了细粒度的原子服务(办法)的 SDK。SDK 中的内容不蕴含个性化举荐场景的具体业务性的能力,体现的重点是根底性能项,业务内容须要在场景插件中进行实现,对立类型的服务尽可能反对组合。

服务惟一化在对于实现零碎的收敛和代码规模可控至关重要,咱们也是一直的在朝着这个致力。各服务层都是以 SDK 的模式对外提供相干的性能,在 SDK 中实现服务调用入口的唯一性。

3.4.3 正当应用设计模式

零碎中应用了较多的设计模式来优化整体架构,如下重点来介绍应用的模板设计模式、策略模式及组合模式:

在获取举荐原始队列中应用了模板设计模式和策略模式来实现此过程。

应用模板设计模式的益处不言而喻,可能容易促成此局部解决逻辑流程化。

针对不同的数据源,须要应用不同的数据源服务和办法,应用策略模式的益处是便于定义在不同场景下对不同的接口的调用。

同类型的原子服务或者办法尽可能反对组合模式,这种会为后续的扩大提供很大的便利性。

以理论的实现办法来阐明,在咱们定义过滤类型的时候,反对传入多个过滤类型,下层业务在应用的时候按需传入即可。应用组合的设计模式在晋升扩展性方面起到了微小的作用。

3.4.4 场景的热插拔

零碎中为实现场景之间的隔离和互不烦扰,笔者应用了 Java SPI 的形式,在框架层定义了场景接口,接口实现类则在各个场景在单独的 Jar 中实现。这种形式有助于插件程序对框架层和根底服务层的侵入性降到最低。

四、带来的扭转

以前商店服务端在各个接口的 service 层写残缺的举荐队列获取、交融、组装、过滤逻辑,有大量的反复内容,且随着版本的一直迭代,有很多版本不同的解决逻辑夹杂在一起,导致革新难降级难,牵一动员全身。目前利用举荐零碎在两个方向带来较大改善:

  1. 流程框架的逻辑齐全形象并独立,各个业务场景只须要按需写很少的插件回调逻辑即可,(不波及非常非凡的场景可齐全不必写插件回调扩大,通过配置对应的场景规定配置即可,可齐全实现免开发,目前有 30% 左右的场景免开发)。
  2. 场景之间齐全隔离和独立,波及简单的性能降级可通过降级对应的场景 id 或者模块 id 来做增量实现,不影响现有逻辑。

五、写在最初

通过上述相干的计划落地,针对于各个举荐场景,咱们大略缩小了 75% 的开发工作量,同时 bug 率也失去大幅度的升高。

作者:vivo-Huang Xiaoqun

正文完
 0