作者|王展,张宇,罗群峰,顾春晖背景抖音Feed容器用于推荐、关注、同城、好友等多种场景,各场景有自己的逻辑和业务,最终归纳在FeedViewController中。随着业务的迭代,代码越来越臃肿,面临以下问题:容器类(FeedViewController)10000+行,业务类有十多个。整体理解容器框架和业务边界不清晰,框架代码修改不收敛,不规则。业务变更可能会导致线上问题,比如数据层不收敛导致的问题:自动删除导致一次滑动多个视频或者自动跳转到第一个视频等问题。容器类承接了推荐、关注、加好友三大场景。详细的业务逻辑有很多不同。目前,多个业务代码耦合在一起。在增加新功能时,需要考虑其他业务方,容易引入问题,开发测试效率低。流入容器和流出容器形状相似但代码分开,主要代码重复。增加新功能时,需要在两个类中重复开发,如:视频预加载优化等,开发维护成本高核心功能监控和代码防退化系统不完善。饲料容器承载多场景业务。Feed容器承载了基础功能、直播、登录、注销、性??能监控、预加载等多种功能。由于之前缺乏管控,导致容器内业务相互耦合严重,业务边界不清晰。开发过程中稍有不慎,就会影响到其他业务。而且随着业务的迭代,有逐渐恶化的趋势,尤其是新的业务接入,无法处理负责任的代码。业务迭代效率低。由于直接在容器类中修改代码,往往一个版本在容器中有多个业务修改,导致冲突。这时候需要多方审核,确保修改没有问题。往往是平台业务同学提供支持,业务整体迭代效率比较低。防劣&监控缺乏业务耦合,没有监控代码变更,导致FeedViewController越来越膨胀。因为没有合理的结构,无法拆分,代码退化越来越严重,以目前的情况来看,根本无法杜绝退化。为解决上述问题,目标计划首先设定目标,然后根据目标提出解决方案,最后实施,以验证目标是否实现。目标架构分层,明确各层职责。容器和服务解耦,多个服务解耦,使容器和服务闭环;业务组件可插拔,不同场景支持业务组件灵活组合和扩展;建立监控系统,实现稳定性、性能、问题定位,并建立看板,实时了解各项指标;集装箱与业务仓库防变质、隔离、维修人员衔接;基于以上目标进行思考和设计,从以下四点出发:明确业务开发痛点、多业务协同开发效率低、模块设计不合理成本高等;自上而下的设计保证了整体业务架构设计的合理性,明确了优化方向;分层开发,在线验证,降低上线风险和全量成本;建筑抗劣化,效益可衡量;该方案针对feed容器内部多场景、多服务耦合带来的整体维护困难,新业务接入成本高等问题。拆分完成后,为了方便各个业务的维护,设计了ControlerKit工具实现生命周期方法的分发,通过Context进行状态管理,实现各个业务之间的通信和状态维护。整体架构基础容器Feed基础容器采用组件化框架,支持基础组件和业务组件的动态组合和扩展,由业务独立、统一的列表形式组成,通过数据驱动的页面展示。同时,生命周期事件对外暴露,方便组件监控。其中,基础容器由平台方统一维护,并提供完善的监控体系,方便问题的定位和排查。基础组件Feed容器的基础组件部分由平台侧统一维护。目前的基础组件主要包括播放控制、播放策略优化、列表预加载、页面管理。其中,全屏feed相关的基础组件为多个业务共享,具有可重用性和可扩展性的优势。业务组件业务组件是与业务密切相关的组件。业务方可以根据自己的需要灵活定制。组件本身是可插拔的,由每个业务方维护。应用场景业务端基于Feed容器构建页面,结合业务组件和基础组件。在构建过程中,可以根据配置文件定制容器,如推荐、关注等。容器化工具将多个服务耦合在同一个容器中,导致容器类越来越臃肿。一方面,各方同时维系的难度越来越大。另一方面,对新服务和新学员很不友好。大量时间熟悉上下文,避免变更对其他业务的影响。为此,设计了ControllerKit库,实现了复杂页面的分发,解决了ViewController臃肿的问题,规范了代码拆分标准,提供了分发方法的能力。每个接入方按照规则注册后,只需要实现自己关心的生命周期方法,并在方法中实现相应的逻辑即可。ContainerViewControllerContainerViewController是一个容器ViewController,实现了ContainerProtocol,保存了context,负责各种生命周期方法的分发。ContainerProtocol声明了容器提供的属性和方法,方便各个SubController访问。ControllerProtocol声明了基本的生命周期和常用方法。ControllerController是一个子模块,拆分了ViewController中的代码。可以接收viewDidLoad、viewWillAppear等分布式生命周期并调用自定义方法,也可以给ViewController添加子View。ControllerManagerControllerManager负责Controller的注册、管理和方法分发。通过classNameArray返回Controller的字符串类名数组即可,可以支持其他仓库Controller的能力。Manager需要声明用于分发的Controller协议。只需要声明,不需要实现。Manager会通过消息转发机制在内部进行分发。角色关系ContainerViewController实现ContainerProtocol并持有ControllerManager。每个子Controller都注册在ControllerManager中。每个Controller都可以通过ContainerProtocol访问容器能力。ControllerManager通过在ControllerProtocol中声明的方法进行分发。例如:当ContainerViewController初始化后调用viewDidLoad时,会通过ControllerManager依次分发给实现该方法的controller,每个Controller可以在自己的viewDidLoad方法中实现自己的逻辑。ControllerPriorityMethod分配优先级是按照数组提供的顺序,所以比较基础的Controller应该排在第一位。优先级是由注册顺序决定的,所以不同方法的优先级不能调整,估计也不会调整。如果不能满足,则使用其他方法实现Feed容器的实现根据ControllerKit,改造Feed容器的类结构如下图。FeedViewController作为容器实现容器能力,通过FeedContainerProtocol对外访问。对应的业务组件FeedControllerManager负责组件注册、管理和事件分发。基于实现各种类和协议的设计和介绍:FeedContainerProtocol容器层通过FeedContainerProtocol对外提供能力,避免业务方直接访问和修改容器类。该协议提供业务层所需的各种能力和接口,由平台侧维护。FeedControllerProtocol业务层协议是通过FeedControllerProtocol声明的,定义了各个生命周期相关的方法。每个业务控制器实现每个业务,只需要在相应的生命周期方法中添加自己的逻辑即可。注入的controller会在相应的时间调用到业务自闭环的Context和ContainerProtocolFeedContainerProtocol的定位和区别是为controller提供实现FeedViewController的能力。FeedContext存储由Controller共享的状态。两者都可以实现通信,但是context更侧重于状态,而ContainerProtocol更侧重于能力,比如页面滚动、数据刷新等业务组件定义和定义业务Controller类实现FeedControllerProtocol协议,并在其中实现相应的业务逻辑相应的生命周期方法。如果FeedControllerProtocol不满足的情况,按照之前的描述在protocol中添加新的生命周期方法,同时添加到FeedContainerProtocol中进行分发和重构。业务后迭代框架由平台业务架构师维护。其他业务的框架扩展需要提交给架构师。架构师开发其他业务提交的解决方案和修改,提交给架构师。查看业务端的代码。业务方的自闭环抗降级建设,将防止饲料容器随着业务的迭代而逐渐变质。需要进行防劣化施工。一、框架与业务分离:代码隔离,修改权限收敛;framework部分,linePipeline访问下,Lint检查是否满足容器规则;业务方修改容器代码,审核通过后才能合并新方案。优势业务解耦,业务与容器职责清晰,边界清晰,降低FeedViewController维护成本,减少新业务接入下面是一个防降级接入的例子,方便投入成本。下面以兴趣选择和服务为例,介绍新旧服务的接入。新功能接入——兴趣选择兴趣选择是一种新型的卡片,需要进行卡片注册和相关逻辑处理。直接修改历史方案FeedViewController,包括以下内容:添加状态管理属性需要在tableviewdelegate、scrolling等多个方法中添加相应的处理逻辑来处理注册卡片逻辑。新方案在生命周期方法中抽取了一个单独的业务控制器和处理兴趣。选择相关逻辑业务相关的属性,在Controller中声明和维护Controller,注册到ControllerManager,在对应的Controller中进行自己的业务处理。您不需要了解容器本身的其他业务逻辑库存功能。很多业务都是在FeedTableVC中处理的,这些逻辑是和其他业务耦合在一起的。网络请求监听和数据处理页面滚动播放处理...使用新的方案拆分先创建一个FeedMonitorController,添加业务相关的属性,在生命周期方法中实现相应的逻辑,然后提取一个单独的业务控制器处理在生命周期法熟识相关逻辑。同时注册到controllerManager中,并设置AB,原代码判断AB。上线验证,满量后删除容器旧码。之后业务会自行关闭,迭代时可以直接修改FeedMonitorController的内容。当前进度&后续规划规划与节奏1234梳理现状;重组方案设计与审查;组件框架设计与开发;Feed能力设计与定义新功能基于新组件开发;库存业务组件拆分及验证业务接口合理化:Feed容器对外暴露能力、业务调用;基础能力接口合理化:Feed容器对组件的暴露能力,广播控制和数据操作等组件框架的水平应用,详情页Feed等收入业务解耦后使用新架构重构,最终容器本身是稳定的,业务方维护自己的业务,提高了整体的稳定性。因为旧容器和新容器的业务耦合,需要了解feed的结构和多个服务的细节。新生熟悉需要2天左右;过程中,由于多个业务同时迭代,相互影响,质量无法保证。只需要在自己的业务控制器中开发,不需要关心容器和其他业务方的结构,大大提高了开发迭代的效率;影响其他业务线的代码保证了代码的稳定性。全业务的自闭环版本在业务组件中实现。版本已映射。新的解决方案是MR。旧的解决方案是MR。-2.0391932.8%1.3-1.6311846.15%0.9-1.2251334.21%0.5-0.8162358.9%0.1-0.4121961.2%
