分享 vivo 互联网技术干货与沙龙活动,推荐最新行业动态与热门会议。
分类: 架构设计与优化
2021-03-22 10:13:35
介绍 vivo 应用商店推荐系统如何高效支撑个性化的推荐需求。
商店的应用数据主要来源于运营排期、CPD、游戏、算法等渠道,成立推荐项目之后也没有变化,发生变化的是由推荐系统负责和数据源进行对接,商店服务端只需要和应用推荐系统进行对接即可。
如果读者以为我们单纯是把商店服务端代码给照搬到推荐系统这边来了那就真的是too young too simple 了,不做优化或者升级直接copy一个系统是不可能的,这辈子都不可能。以下我将介绍我们如何去设计和规划应用推荐系统的。
在笔者眼中,商店应用推荐系统除了要具备高性能、高可用性及核心指标的监控能力之外,还有一个核心的能力就是高效支撑商店流量场景接入个性化推荐。
如何定义高效支撑?
分享我们一个应用推荐的策划case看看:
在xx场景下,
如果主应用A属于应用类,
- 则首先从从x1数据源去取Q1队列。
- 然后从x2数据源去取Q2队列。
- 然后用Q2队列去截断Q1队列,交集之后进行同开发者过滤和一级分类过滤。
- 如果交集为空则用Q2去兜底,然后取交集队列的n1和n2 位置上的元素作为返回队列。
- 如果前面都没有取到数据的话从大数据xxx表中按照主应用下应用点击的概率取点击率最高的分类下的n个,同时需要对这些数据进行队列内的同开发者过滤。
如果主应用A属于游戏类,
- 则xxxx
- 进行二级分类过滤
- 如果量不足的话,则从x(n)取数据然后进行处理,
- 如果数据不足3个的话,需要从周榜单中取同一级分类下的应用按照下载排行进行兜底。
没错,读者朋友不要怀疑自己,为了不把各位读者大大绕晕,我们这里只是挑选了一个简单的需求。实现这么一个功能也没有什么大不了的,但是当这种个性化推荐需求有几十个,后面还可能一致扩展下去的时候会不会心里发慌?来,简单看下我们现在个性化推荐的一部分需求,如图(一)所示:
图(一)
使用商店服务端之前的case by case的开发方案,无论如何都无法实现上文中描述的要支撑商店高效接入推荐场景了,接着就是我们如何去实现优化的过程了。
为了更好的说明解决思路,我们从实际思考过程出发,一步步讲解问题的解决过程。
单纯从策划上面来说,我们每个场景都需要至少做如图(二)中的几件事情:
图(二)
笔者从开发便捷性出发,对模型进行了进一步的调整,调整后为图(三)
图(三)
获取队列后对队列进行安装过滤和队列内过滤(如主应用同开发者过滤等)可以进行流程合并,主要有如下的原因
到图(三)这里,读者会发现我们依然没有能够解决我们前面提到的各种推荐场景里面的差异化过程。
其实在接触几个需求以后,我们会发现,想要在一套代码里面去解决这么大的差异性,几乎不可能,或者即使实现了,那么也会让代码变得无比复杂。与其这样子,我们还不如正视这种差异性,让差异在场景插件里面去实现,我们花更多的精力去打理主干。
那么为了支持让场景能够具备灵活的扩展能力,笔者在基于图(三)的基础上增加了四个环节:
拓展后的流程图如图(四)所示
图(四)
经过上述的分析可知,我们可以尽可能的把个性化的场景内容放在插件层实现,框架层负责加载按场景加载场景插件的具体个性化推荐逻辑。
系统从分层思路上讲从上至下共分为:插件层,框架层,协议适配层,数据源服务层,原子服务层,基础服务层,上层通过 SDK 依赖下层的服务(接口),各层次职责为:
至此,相信大家都知晓了,针对于个性化的推荐,我们的开发工作最终将聚焦于开发场景插件,不需要再额外开发每一个业务流程了。
应用推荐系统架构
在完成第三步整体逻辑框图设计之后,我们从场景参数定义,服务设计原则,设计模式使用,场景热插拔等方面进行了相关的方案研究并最终实现了方案的落地。
为实现推荐场景足够通用,我们将数据源层,原子服务层,基础服务层的内容进行了服务配置的映射,通过在配置中定义对应的配置项来实现服务的映射和组合,针对于差异性的内容在插件层进行实现。以如下的配置项示意图来说明:
实现服务原子化与服务唯一化对本系统至关重要,在实现过程中是严格遵照如下两点来:
应用推荐依赖的三方RPC服务及内部的一些过滤逻辑都封装成了细粒度的原子服务(方法)的SDK。SDK中的内容不包含个性化推荐场景的具体业务性的能力,体现的重点是基础功能项,业务内容需要在场景插件中进行实现,统一类型的服务尽可能支持组合。
服务唯一化在对于实现系统的收敛和代码规模可控至关重要,我们也是不断的在朝着这个努力。各服务层都是以SDK的形式对外提供相关的功能,在SDK中实现服务调用入口的唯一性。
系统中使用了较多的设计模式来优化整体架构,如下重点来介绍使用的模板设计模式、策略模式及组合模式:
在获取推荐原始队列中使用了模板设计模式和策略模式来实现此过程。
使用模板设计模式的好处显而易见,能够容易促进此部分处理逻辑流程化。
针对不同的数据源,需要使用不同的数据源服务和方法,使用策略模式的好处是便于定义在不同场景下对不同的接口的调用。
同类型的原子服务或者方法尽可能支持组合模式,这种会为后续的扩展提供很大的便利性。
以实际的实现方法来说明,在我们定义过滤类型的时候,支持传入多个过滤类型,上层业务在使用的时候按需传入即可。使用组合的设计模式在提升扩展性方面起到了巨大的作用。
系统中为实现场景之间的隔离和互不干扰,笔者使用了Java SPI的方式,在框架层定义了场景接口,接口实现类则在各个场景在独自的Jar中实现。这种方式有助于插件程序对框架层和基础服务层的侵入性降到最低。
以前商店服务端在各个接口的service层写完整的推荐队列获取、融合、组装、过滤逻辑,有大量的重复内容,且随着版本的不断迭代,有很多版本不同的处理逻辑夹杂在一起,导致改造难升级难,牵一发动全身。目前应用推荐系统在两个方向带来较大改善:
通过上述相关的方案落地,针对于各个推荐场景,我们大概减少了75%的开发工作量,同时bug率也得到大幅度的降低。
作者:vivo-Huang Xiaoqun