哈啰动态化容器架构实践

字节探险家说
• 阅读 827

背景介绍

哈啰动态化容器架构实践

哈啰的业务的多样性体现到APP页面上,我们会发现整个APP的页面设计呈现的方式发生了很大的变化。早些年哈啰APP页面的功能较为单一,上图是近几年哈啰APP的页面,可以看出页面更加多样化,很多业务的功能和信息都在这些页面上展示出来,也有很多交互的能力。

哈啰动态化容器架构实践

这样的业务发展趋势及APP的页面设计方式,给我们的技术团队带来了两个痛点和挑战。一是交付效率,复合型的页面往往会涉及到多个业务团队的需求,也会涉及到多个技术团队去合作开发,效率就会下降。同时,这些页面都属于流量曝光型页面,产品侧需要做产品的AB测试,尽快地去上线并回收数据。如果发现功能需要调整,产品就希望尽快变更,所以用户触达效率及交付速度都对产品的迭代有很高的要求。二是用户体验,哈啰的首页及各个业务的一级频道页,都是一个业务最核心的流量页面,对用户的体验要求很高,如多端一致性、稳定性和交互流畅度这些指标,相对于其他三四级页面来说,对稳定性的要求也会更高。

技术方案

技术选型的两点思考

哈啰动态化容器架构实践

面临这些痛点和挑战,我们想到了一些解决方案,首先介绍一下技术选型的思考。一是我们要做一个架构,需要知道架构的能力边界在哪里,要解决怎样的困难和实现怎样的目标。这里列出了现在成熟的客户端APP的技术体系。从服务端来看,主要包括几个能力,最底端的是中间件的能力抽象,上面是业务微服务的能力抽象,再上面是面向端的能力抽象,比如我们通常会做一层BFF来面向端的接口封装,一层CDN来面向端的数据高效访问的能力抽象。从客户端来看,现在成熟的APP基本都有一套自己的组件化框架,做一些通用能力的沉淀,我们想要设计和实现的容器框架就是在组件化框架的上面,它的主要目的是承载各个核心页面的展现和相关的业务逻辑,这是整个容器的能力边界的大致界定。我们希望这个容器首先是标准化的,通过标准化的手段来尽量抹平业务的复杂性带来的问题。二是隔离的虚拟化技术,尽可能提高自身运行的稳定性。这是容器的两个特点,也是我们选定了在这个范围做容器封装的技术。

哈啰动态化容器架构实践

我们的容器既然要承载整个页面的渲染,应该用什么技术来实现动态化渲染,这是第二个技术选型的思考。其实业内也有很多成熟的技术方案。我们可以从三个维度对这些技术进行归类,一是动态化能力,刚才也提到这是很重要的考虑点。二是原生体验,因为我们对整个页面的流畅度、稳定性都有极高的要求,如果在原生体验上做不到,那这个技术选型就不合适。三是开发成本,我们的成本投入也是有上限的。比如说大家比较熟悉的Web容器的方案,是比较容易引入到整个移动端里的,它的优势就是动态能力,相比整个原生的开发,它天然就具备动态更新的优势。还有像React Native、Weex前几年也是非常火,它的技术选型是在整个原生体验和动态能力做了取舍。DSL方案相对于前面两种又各有优势。我们怎么去做技术选型主要有两个考虑点,包括投入成本和关键收益。一是投入成本,要结合自己业务的状态和团队的人力投入来考量,它需要考虑整个技术研发项目的成本,从头开发还是在已有的架构上做升级,成本是完全不一样的。其次现有系统的升级风险,对现有技术架构兼容性,对业务的影响都需要考虑进去。二是关键收益,任何技术项目都有很多收益,我们要考虑做这个系统,哪些技术能力是主要的,哪些是次要的,哪些是可有可无的。这样分析后我们可以看到为了保证用户体验,性能优先一定是最先考量的关键收益。其次我们要提升效率,尽可能降低团队合作的边界成本。同时我们希望架构可持续地去演进,来满足业务不断增长的诉求。基于这两个考虑,我们最后选择了DSL方案。

架构模型

哈啰动态化容器架构实践

移动端的核心框架有渲染层、协议层和逻辑层。渲染层是解决静态渲染和动态渲染如何实现的,协议层是提供一些标准化的接口封装,方便模块的运行、通信以及扩展。逻辑层是整个容器框架自己运行的时候,它的状态管理、数据管理、日志管理各方面的一些能力的封装。监控层是整个容器框架自己内建的一套监控体系,我们在这个体系上也搭建了很多可视化的工具和用户触达的能力。模块层更多的就是模块的管理。

动态模块的实现和优化

哈啰动态化容器架构实践

上图是容器渲染层动态模块的渲染过程。首先还是获取配置,也是刚才整个页面配置里单一模块的配置信息。这个信息里的主要格式有两部分,一是节点信息,决定了这一个渲染素里面每个节点的元素类型,它可能是一个空的容器、一个文本,也可能是一个图片。二是布局信息,我们用Flexbox的语法来描述所有节点之间相互的关系。我们把配置信息解析出来生成了配置树,就是把配置文件变成数据模型。接着是动态绑定,最后会生成一个渲染树。在这个过程中会遇到一些性能问题,如中低端的安卓机,渲染性能较iOS机型会出现明显下降。

哈啰动态化容器架构实践

我们从多个维度进行了优化工作,一是数据预取;第二是渲染优化,将前置的渲染操作步骤放到后台线程来处理,并铺平页面元素的层级;第三个是数据更新策略的控制,包括版本管理、虚拟节点、动态绑定和Diff算法。

哈啰动态化容器架构实践

我们把这个卡片上屏之后,用户需要交互能力,于是我们设计了一套action指令的方式来实现,通过预埋在配置信息里的路由命令来实现手势识别和相应的页面跳转操作。action指令能解决80%的场景需求,但在某些场景下不太好用。

哈啰动态化容器架构实践

比如这样的页面,有一个优惠券可以领,当点击红色去领取按钮的时候,卡片就会发生变化,比如按钮会置灰,卡片的样式也会发生变化。这种场景我们通过实现一套jsruntime来进行支持,通过动态获取脚本来实现类似的操作。

容器逻辑层

哈啰动态化容器架构实践

这张图是容器逻辑层的模型图,可以从几个维度理解容器逻辑层的运作方式。第一是初始化,容器在启动时进行初始化,整个模块首先要进行自注册,我们在适当时会获取相应的配置数据,接下来会走前面介绍的流程,根据模块的ID做模块的初创建,根据算法来判断是不是要上首屏,然后进行模块上屏。第二是运行时的状态,一个模块运行时主要有四个能力。一是它的布局和样式信息要维持,二是它的业务数据要维持,三是生命周期的状态,包括它的显示、隐藏、创建、销毁,四是事件监听,我们基于一个模块通信的总线来实现整个模块状态的变更和相互关系的关联。此外容器有状态管理的机制。

容器协议层

哈啰动态化容器架构实践

第三层是容器协议层,我们设计这一层的意义是想让模块开发更加标准化,无论是平台方还是业务方,能降低业务开发的复杂度。我们主要抽象了四层容器协议层,首先容器的布局协议和数据协议相对比较好理解,布局协议解决了布局的一些信息,如模块单元的大小、位置。容器的数据协议就是容器配置数据的填充,如单元格数量和内容;生命周期协议,包括创建、加载、显示、消失和销毁。生命周期协议有很多应用的场景,比如性能监控和曝光计算。事件协议,事件总线的运行是依托事件协议的,比如像模块事件、定时器事件。

容器运行

哈啰动态化容器架构实践

我们从全局看一下容器运行是怎么做的,这里会看一下前端和服务端做的事情。前端会有一个配置平台。策略中心由算法驱动决定整个模块排序。业务微服务提供显示模块具体业务数据的来源,所有的信息都会融合成一个BFF层。端启动后,比如首页用了容器框架,启动之后它会做容器的初始化、模块的初始化流程,模块运行时又基于事件状态这样的管线来做一些状态的变更和模块的通信。

研发流程的改进

哈啰动态化容器架构实践

我们设计了整个框架,新的系统对研发、测试、运维的工作方式都会带来很多的变化,很多环节上进行了生产提效。绿色标注的是移动端常规的发版流程,我们看到涉及到的节点和团队都非常多,跨团队的合作会遇到很多问题,发版速度并没有想象那么快。在新的系统下,我们通过在线系统做的变更,这个环节的节点就会少很多,而且更新效率会快很多。

辅助工具

哈啰动态化容器架构实践

在整个开发过程中,我们也做了很多工具来提升开发的体验和效率。刚才也讲到开发可以去配置平台做配置,我们本地客户端也集成了一个调试工具,会预览渲染的动态卡片的样式。

发布和监控

哈啰动态化容器架构实践

发布上线的流程不同于传统的客户端发版,它变成了一个在线系统的发布流程。除了发布的变更,我们也做了一些监控分析的能力为它保驾护航,我们会采集性能的数据、稳定性的数据和异常的数据,在这些数据上建立可视化的分析能力和告警触达能力。

项目实践

哈啰动态化容器架构实践

2020年我们做了首页大改版,整个页面会变成右侧这样。我们希望首页能够承载流量曝光、业务分发、广告营销各方面的能力,首屏的模块会变得非常多,所以整个页面信息的多样化和逻辑的复杂度会高很多。基于这样的背景,我们在2020年做了容器化架构的设计和升级。容器化架构升级主要分为两个阶段,2020年做了跨端统一的客户端的框架,2021年做了容器动态化相关的能力,进一步提升研发效率。我们计划在前后端各个平台,包括运营平台、算法平台等各方面去完善,包括低代码的开发工具,把这个系统做得更好用更高效。

哈啰动态化容器架构实践

在1.0阶段,我们主要为了支持业务的升级,实现两端统一的容器框架,可以把所有业务都呈现在整个框架上面,同时具备一些基本的标准,比如协议层的协议,还有整个模块容器运行的状态。基于这个事件驱动的模型,2.0阶段我们做了模块化,再之后我们增加了动态化能力,进一步加快交付效率和降低研发成本。3.0阶段我们去完善前后端各系统。

哈啰动态化容器架构实践

回顾整个项目,我们在复盘的时候总结了两个经验。一是要做取舍,业内也有很多现成的方案可以考虑,但并不是每一个都可以拿过来用,所以做技术取舍非常重要。要结合自己团队的能力和目标来做取舍,并不是越复杂越好。二是我们要持续去做架构的升级,要看长远些,我们的目标是持续推动业务的增长。我们整个首页支持的业务线有10多个,上线之后一年多的时间里迭代了100多次,保持了极高的稳定性,同时团队的研发效率提升30%,技术改造取得了很好的效果。

未来展望

哈啰动态化容器架构实践

最后介绍一下未来的展望,一是原生SDK可以做的工作,不断提升稳定性,降低崩溃率和异常率,同时逻辑动态化上还有很多可以做的工作。二是完善工具箱,比如IDE、数据可视化等。三是建设生产力平台,让搭建平台更加简单易用,同时要提高系统自动化能力。

(本文作者:秦阳)

哈啰动态化容器架构实践

本文系哈啰技术团队出品,未经许可,不得进行商业性转载或者使用。非商业目的转载或使用本文内容,敬请注明“内容转载自哈啰技术团队”。
点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
4年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
梦
5年前
微信小程序new Date()转换时间异常问题
微信小程序苹果手机页面上显示时间异常,安卓机正常问题image(https://imghelloworld.osscnbeijing.aliyuncs.com/imgs/b691e1230e2f15efbd81fe11ef734d4f.png)错误代码vardate'2021030617:00:00'vardateT
Wesley13 Wesley13
4年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
4年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
4年前
Unity横屏
Android下发现Unity里面的Player设置,并不能完全有效,比如打开了自动旋转,启动的时候还是会横屏,修改XML添加以下代码<applicationandroid:icon"@drawable/ic\_launcher"                    android:label"@string/app\_name"
Stella981 Stella981
4年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
2年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这