背景
随着闲鱼业务的高速增长,业务的数量和复杂度,都急剧增加,研发侧在业务支撑上出现了两个明显的变化:
一方面,如首页、搜索之类的传统流量型页面,试错和调整变得更加频繁,只有卡片级的动态化能力已经不能满足需求,更多的页面级结构调整扑面而来,仅以首页为例,去年大约有一半的时间,都在做页面结构调整。
另一方面,更多的 DAU 带来的是更多的垂直业务的增长,如同城、会玩、优品、租房、二楼等等,它们也都需要自己的流量型“首页”,而在这个过程中,同类型结构和能力的页面代码大量重复,开发维护成本非常之高。
那么有没有一种方案,能够快速 cover 各个业务场景里的流量型页面,不仅能够适应这种快速多变的大量结构性调整,并且能够快速试错和尝试,尽可能减少重复代码的方案呢?答案是有的,基于流量型页面重展示,轻交互的特点,我们的解法是建立一套端到端的页面级描述协议,加上对接协议的云端搭建平台来对流量型页面结构进行动态调整和渲染。一方面,协议的调整可以直接映射为页面结构的调整,以达到 less code 甚至 no code 对页面结构进行调整的目的。另一方面不同业务场景,可以用通用协议来进行横向扩展,减少开发和维护的成本。
流式场景协议的扩展
闲鱼去年的时候,基于流量型页面,提出过一套三级协议的容器化解决方案,当时的三级协议包括:Page、Section 和 Component,能够较好的描述单页面的情况:
Page 层协议主要包含整个页面 Sections 信息,以及下拉刷新、上拉加载更多等配置信息;
Section 层协议包含当前 Section 的布局信息、初始化 Event、LoadMore Event 及 Components 等信息;
Component 层协议与具体业务相关,对于容器来说是黑盒的,具体如何渲染会交给业务方处理;默认提供 DX 解析渲染 Handler。
容器中,事件中心基于事件传递的方式,数据中心基于 MVVM 的数据驱动进行实现,在 UI 渲染方面,闲鱼将列表容器与动态模板渲染 DXFlutter 相结合,实现页面渲染及数据更新后的页面刷新能力。
为了适应复杂的多页面场景以及容器嵌套的情况,我们在之前提出的三级协议的基础上,扩展了四层协议的设计:Container、Page、Section 和 Component。新设计的 Container 层协议主要包含多个页面 Page 的信息,以及页面 Page 对应的 tab 等配置信息:
|-- Container
|-- Page
|-- Section
|-- Component
由于在 Container 这一级,我们需要对多个 Page 页面进行布局,这里目前能支持三种形式:
单页面,每个页面单 listview 形式
pager+tab+多页面形式,每个页面是单 listview 形式
页面内包含子容器,也就是嵌套容器的形式
我们以最复杂的嵌套容器的情况为例,嵌套的子容器对他的父级的页面而言,实际上是一个 Component(卡片),但是它的内部数据结构是一个完整的 Container 的容器结构,在协议上,我们给它设置的 type="sub_container",这个时候会用默认的 SubContainerRenderhandler(子容器渲染器)对自容器进行解析和渲染,并在过程中处理对应的手势问题,这里我们会给父容器和子容器在协议上分别进类型指定,并提供类型对应手势响应能力。子容器中的页面间的切换消息通过通知对外发送,如果是 tab+pager 的形式,只需要在 tab 内对广播消息设置对应的监听即可,同时子页面也可以通过通知消息接收切换页面的指令,这样的组织形式就能让 tab 和 pager 在解耦的情况下相互通信。
云端平台的设计
整体设计
目前流量平台以二方包的形式对业务服务提供支持,运营和开发可以在流量平台搭建页面和配置对应的资源位数据,在数据修改之后,平台会将修改的部分先写入到 TDDL,然后再主动同步到 cache,而 cache 可以被部署在业务服务的平台 client 二方包读取到,正常的情况下,cache 存在,则业务服务在响应闲鱼客户端请求的时候,只需要读取缓存即可,只有在缓存不存在的时候,才需要主动拉取对应的资源配置数据。
Client 二方包设计
其中部署在业务服务中的 client 二方包具备配置获取、分流计算、动态注册、远程调用等基础能力,除此之外,还对容灾兜底和异常监控都有对应的设计。依赖二方包的形式,业务方服务就能够以解耦的方式来获取流量平台的资源和配置信息。
分流的设计
由于流量型页面需求多变,试错的场景普遍存在 AB 实验的需求,我们在云端平台设计了一套流量分流的体系,来最大限度的降低流式场景下 AB 实验的成本。
一个闲鱼端侧用户在请求容器页面时,会将对应的用户、版本、lbs 等相关信息通过统一的协议上传到云端平台,平台拿到这些信息之后,会经过概念层、分流层、实例层和资源层,最后所有实例都会关联资源和发布计划。这样请求最终能映射到某一个确定的投放方案上,从而让对应的用户看到确定的页面搭配,从而达到云端实验分流的目的,最终让用户看到他应该落到的实验页面。这样新建或者调整页面的 AB 实验,我们只需要在云端配置对应的实验页面需要满足的流量条件即可,很大程度上降低了业务试错的研发成本。
安全容灾
流量型页面一般承载着对应业务的业务入口的角色,是门户,它的稳定性不言而喻,所以整体端云一体的设计中,也少不了对容灾和兜底的考虑。
云端容灾
正常情况下,客户端请求平台,通过业务方服务接口把 userid、城市、设备等信息上传到云端,根据之前的流量设计以及二方包的缓存设计,如果命中的方案,二方包能获取到缓存,则会直接返回缓存资源数据,否则会去流量平台请求资源数据再返回。
在异常情况下,或者没有拿到对应的缓存,那么这时候会用到我们在缓存制备好的的一份兜底数据,兜底数据会在平台操作变更时,通知业务方服务端去制备新的兜底数据以及去除旧的兜底数据。
客户端容灾
客户端容灾主要考虑到网络异常或者接口服务不可用的情况,依然需要保证页面的正常展示,这个时候我们在端侧设计了三级缓存:内存缓存、磁盘缓存以及预置缓存。内存缓存和磁盘缓存都会在接口成功回调之后做保存动作,在失败的情况下会优先去获取内存缓存和磁盘缓存,而当这两部分都获取不到时,就会尝试获取打在 apk 包中的预置缓存数据,这样保证页面不管任何情况下都能正常展示。
客户端与云端平台协议交互
由于端侧容器化协议是基于事件进行交互的,那么在云端平台加入之后,整体事件交互的边界就被进一步拓宽。举个例子,以点击触发关注列表中的某个卡片出现关注标记为例。传统的做法,一般是客户端在端侧写好关注和刷新逻辑,然后在点击的时候去调用写好的刷新方法。但是如果用端到端的协议来实现,则会变成点击将列表接口在当前卡片上绑定的事件回传给平台,这个事件是什么,对端侧来说是透明,在当前场景就是一个关注的事件,那么这个事件会通过点击事件带到云端,云端拿到对应的事件 key 之后,会下发对应的数据,然后端侧只对数据进行刷新,刷新之后,就会让当前卡片出现关注标记。这样的好处在于,减少业务依赖端侧的版本发布,最大程度的使用数据驱动的设计,减少依赖,提高复用性。这样的设计加上容器化协议对页面结构的描述和渲染,在重展示轻交互的流量型页面,将会大大减少端侧逻辑耦合,提高业务开发的效率。
总结和展望
目前,端云一体的容器化搭建方案,已经在闲鱼多业务场景落地。此前的两个问题也得到了解决:
页面结构调整的动态化能力:以首页为例,在容器化协议落地之后,再做页面结构调整,我们只需要简单修改云端平台的配置加上很少的代码改动即可生效,大大减少了业务维护的成本。
横向业务场景的流量型页面支撑:借助云平台配置搭建和端侧容器化渲染协议,现在大约只需要平时一半的开发时间,就能完成一个全新业务场景的搭建,更多的页面骨架型代码,都沉淀在以容器化协议为基础的 SDK 中。
流式场景下端云一体搭建体系,降低了端侧多业务中流量型页面这种特定场景下的冗余代码逻辑。整体云端一体的容器化搭建方案,使得 less code 甚至 no code 快速调整页面结构以及快速搭建新业务场景中以展示为主、轻交互动态化运营流量型页面成为了可能。
本文转载自:闲鱼技术(ID:XYtech_Alibaba)
原文链接:流式场景下端云一体搭建体系
评论