通用详情页的打造

背景介绍

图片

大家都知道,详情页承载了站内的核心流量。它的量级到底有多大呢?

我们来看一下,日均播放次数数亿次,这么大的流量,其重要程度可想而知。

在这样一个页面,每一个功能都是大量业务的汇总点。

作为用户核心消费场景,详情页不仅需要承接各种业务的转化,还要负责展示各业务在播放页的功能。

可以说,播放页的代码复杂度属于客户端最高的代码之一,这不仅因为播放页本身的功能复杂,还因为它需要融合大量外部业务功能。

复杂的功能自然会带来较高的代码复杂度,而高代码复杂度往往意味着高代码维护成本。

明确需求

图片

我们来看一下没有做这个项目之前的状态。如图所示,他们分别为三个业务团队各自维护。页面间相互独立。能力无法复用。

图片

通过这个项目,我们要将他们融合成了一个页面。产品的诉求就是将他们融合为一个,来达到多业务形态统一的目标。

图片

但是,这三个详情页并不像产品想象的那么简单。

每个业务都有自己的特殊形态,如大型活动态、主客态、播单态、PUGV/OGV态等一系列业务形态。

每种形态都有自己的特殊逻辑,而且这些业务形态间还可以互相切换。

需求分析

为了更好地达成目标,我们需要进行如下思考:

  • 从业务角度:

要解决多业务形态不统一的问题。例如,产品既想要UGC大型活动的能力,又想要OGV的多视角功能。

但这两个能力在之前分别是两个业务团队各自开发的,无法复用,产品在业务选择上无法兼得。

  • 从效率角度:

要解决迭代方式不统一的问题。例如,进度条体验优化需求,产品在给UGC团队提需求的同时,还要复制一份给OGV团队。

两个业务方的开发和测试都需要进入这个项目,并且双方的开发进度和排期可能不一致。如果产品强烈要求同一版本上线,还需要协调各方资源。

  • 从质量角度:

要解决如何保障稳定性的问题。例如,多团队协作,之前都是组内同事协作开发,现在融入了两个新的业务团队,我们该如何保障稳定性。

  • 从团队角度:

要解决如何让新人快速上手的问题。正常情况下,新人想要进入开发必须对这个系统足够了解后才行。

更何况现在变成了三个业务融合的页面。有没有一种手段,让新人无需关心复杂的业务形态和业务逻辑,只需要关注自己的需求?

具体方案

针对以上问题,我们可以总结出通用详情页框架必须满足以上三点,分别为:复用性,灵活性,稳定性

图片

接下来我们继续对多业务形态进行分析。

首先我们从横向上进行拆解,通过对比,我们可以发现

多业务形态间其实有很多的相同模块。如互动,弹幕发送框,相关推荐等。

从纵向上进行拆解,我们也可以发现很多相同模块,如弹窗管理器,主题组件,转场组件等。

那么从横向和纵向上我们发现,多种业务形态间其实有很多可以复用的能力。

图片

基于前面的思考,我们设计了一套通用详情页的框架。将其分为三层:

  1. 业务层:将业务模块分为两类,能够在多业务间复用的模块抽象到通用业务,业务独有模块则由各业务自行负责。

  2. 组件层:抽象出各种通用组件,业务方可自由选取和组装。

  3. 框架层:抽象生命周期管理、数据管理等核心逻辑,以此来保证整个详情页的稳定性。

这样我们就初步解决了复用性的问题,但是随之而来的就是灵活性问题。

图片

我们以实际场景为例,相关推荐模块在课堂态不展示,但是在ugc和ogv下需要展示,另外他的点击事件在ugc和ogv下还会出现差异。

同时相关推荐模块还强依赖简介模块。因为简介模块也是一个通用组件,业务方可以自由替换。

如果哪天业务方替换了了简介模块,那相关推荐模块将无法正常运行。

从相关推荐这个例子我们可以得出如果想让业务模块复用,必须满足两个条件。

  1. 支持业务异化,即允许业务能插入自定义逻辑,否则现在抽象的通用模块在迭代的过程一定会变成非通用,或者里面掺杂各种if else逻辑来支持异化。

  2. 必须保证模块间相互独立,因为所有业务逻辑在此框架下都变成了模块,模块是可以由业务方自由选择的。

图片

引入依赖注入

因此,我们需要在流程和模块中加入依赖注入的能力,用于业务方实现差异化逻辑。

业务方可自行插入自己的业务逻辑,并选择或替换业务模块。来解决模块间的耦合。

定义依赖注入容器

public class BlocStore {
    typealias StoreLock = RecursiveLock
    typealias StoreTable = [String: BlocTable]
 
    private let lock: StoreLock = StoreLock()
    private lazy var storeTable: StoreTable = [:]
}
 
extension BlocStore {
    public func register<Service>(service: Service.Type = Service.self, to: Bloc.Type) {
        let key = "\(service)"
        lock.lock()
        defer { lock.unlock() }
        serviceTable[key] = to
    }
 
    @discardableResult
    public func optional<Service>(service: Service.Type = Service.self) -> Service? {
        let key = "\(service)"
        lock.lock()
        defer { lock.unlock() }
        let service = resolve(bloc)
        return s
    }
}
 
// Bind and unbind
extension BlocStore {
    public func bindBloc(bloc: Bloc) {
 
    }
 
    public func unbindBloc<T: Bloc>(_ blocType: T.Type) {
 
    }
}
 
// BlocLifeCycle
extension BlocStore {
    func onStart(bloc: Bloc?) {
        bloc?.onStart()
    }
 
    func onPause(bloc: Bloc?) {
        bloc?.onPause()
    }
 
    func onResume(bloc: Bloc?) {
        bloc?.onResume()
    }
 
    func onStop(bloc: Bloc?) {
        bloc?.onStop()
    }
}

组件注册

// 业务方根据业务逻辑可以注入不同的实现
register(service: XXXProtocol.self, to: ABloc.self) // A业务形态
register(service: XXXProtocol.self, to: BBloc.self) // B业务形态

组件解析

let s: XXXProtocol = store.optional()

引入scope

scope分为页面级和业务级两种scope:

class VDScope {
    public static let core = "store.core.scope"
    public static let biz = "store.biz.scope"
}

定义 Scope 管理来管理模块的生命周期:

  • Page scope的生命周期与页面保持一致,Biz scope与业务形态的生命周期保持一致。

  • 即在页面形态发生变化时,框架层会自动将bizscope下的所有模块进行销毁。

public class BlocStore {
    typealias ScopeTable = [String: String]
    ...
 
    func bizTypeDidChanged() {
        // 销毁上一个bizscope下所有模块
        xxxx
        // 初始化新bizscope下模块
        xxx
    }
}

这样,新人进入开发时无需关注当前业务形态或业务形态切换的问题,达到快速上手的目的。

如何保障吞吐速度和质量稳定?

在开发资源和测试资源不变的情况下,业务范围扩大了,我们该如何保障吞吐速度和质量的稳定呢?

图片

我们可以将策略分为三个阶段:

1.开发阶段:

对于核心流程添加全链路日志,如果发现不符合预期的数据则直接抛出异常。

同时进行技术埋点上报。如果是对于核心流程的修改,强制添加AB降级方案。

2.测试阶段:

有些bug非常隐蔽,在用户体验上可能没有任何差异,但内部流程或数据可能已经发生异常。

对于类似问题,测试根本无法发现。导致此类问题流入线上的风险。我们可以通过添加监控和告警,让我们及时发现问题。

3.灰度/线上阶段:

我们可以通过添加监控和告警,让我们及时发现问题。

具体实施方案:

首先,我们对通用详情页里核心流程添加了全链路日志,并为日志服务添加了两项额外能力:

如果发现日志类型为Error,内部自动触发DEBUG弹窗提醒,并上报技术埋点,达到对线上稳定性的监控。

图片

同时,搭建离在线数据报表和异常告警,进一步保障稳定性。

至此,搭建了通用详情页从发现问题到定向拉取再到快速定位的闭环。

-End-

作者丨凉茶

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/796077.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Mac M1安装配置Hadoop+Flink SQL环境

Flink 1.18.1 Hadoop 3.4.0 一、准备工作 系统&#xff1a;Mac M1 (MacOS Sonoma 14.3.1) JDK&#xff1a;jdk1.8.0_381 &#xff08;注意&#xff1a;尽量一定要用JDK8&#xff0c;少用高版本&#xff09; Scala&#xff1a;2.12 JDK安装在本机的/opt/jdk1.8.0_381.jdk/C…

认识R155法规(UN Regulation No. 155)-MUNIK

背景 Background 随着汽车新四化&#xff08;电动化、智能化、网联化、共享化&#xff09;政策的提出&#xff0c;大数据和人工智能等技术的发展&#xff0c;以及软件驱动汽车、舱驾一体、行泊一体等新型架构概念的提出&#xff0c;车内外智能传感器采集的大量数据&#xff08…

数据结构4.0——串的定义和基本操作

串的定义(逻辑结构) 串&#xff0c;即字符串(String)是由零个或多个字符组成的有序数列。 一般记为Sa1a2....an(n>0) 其中&#xff0c;S是串名&#xff0c;单引号括起来的字符序列是串的值;ai可以是字母、数字或其他字符&#xff1b;串中字符的个数n称为串的长度。n0时的…

观察者模式的实现

引言&#xff1a;观察者模式——程序中的“通信兵” 在现代战争中&#xff0c;通信是胜利的关键。信息力以网络、数据、算法、算力等为底层支撑&#xff0c;在现代战争中不断推动感知、决策、指控等各环节产生量变与质变。在软件架构中&#xff0c;观察者模式扮演着类似的角色…

SpringBoot实战:枚举类型转换问题

1.在controller层中完成service注入 2.调用业务层进行查询所有房间类型标签 Tag(name "标签管理") RestController RequestMapping("/admin/label") public class LabelController {Autowiredprivate LabelInfoService service;Operation(summary &qu…

dm-verity hashtree的结构

参考了&#xff1a;实现 dm-verity | Android Open Source Project (google.cn)。基于这个添加了一层原始数据&#xff0c;便于理解。 结构图如下&#xff1a; 对hashtree结构图的解释&#xff1a; dev data&#xff1a;表示我们的分区数据。这里我们将dev data按照指定的大…

【JAVA poi-tl-ext 富文本转word】

富文本转word 环境使用poi-tl-ext的原因富文本转word代码 环境 jdk 1.8 <dependency><groupId>io.github.draco1023</groupId><artifactId>poi-tl-ext</artifactId><version>0.4.16</version> </dependency>poi-tl-ext已经包…

学习C++,应该循序渐进的看哪些书?

学习C是一个循序渐进的过程&#xff0c;需要根据自己的基础和目标来选择合适的书籍。以下是一个推荐的学习路径&#xff0c;包含了从入门到进阶的书籍&#xff1a; 1. 入门阶段 《C Primer Plus 第6版 中文版》 推荐理由&#xff1a;这本书同样适合C零基础的学习者&#xff0…

中国蚁剑的安装(附文件)

1.下载压包&#xff08;注意两个都要下载&#xff09; 我先挂几个月&#xff0c;后续怕找不到的话&#xff0c;就自己保存网盘 链接:https://pan.baidu.com/s/1bhBe6a1_5VFYUY35XG1N9Q?pwdx9gp 提取码:x9gp 链接:https://pan.baidu.com/s/1u8CMkOORRVpCI2zJysJPQQ?pwdx9gp …

java Web学习笔记(三)

文章目录 1. 前置知识2. Vue使用vite构建项目SFC入门使用ref和.value体会响应式数据&#xff08;使用ES6和setup&#xff09; 3. Vue视图渲染技术及其语法模板语法&#xff1a;命令插值表达式渲染双标><中的文本&#xff08;还挺可爱&#xff09;属性渲染命令事件渲染命令…

内网对抗-基石框架篇域树林域森林架构信任关系多域成员层级信息收集环境搭建

知识点&#xff1a; 1、基石框架篇-域树&域林架构-权限控制-用户和网络 2、基石框架篇-域树&域林架构-环境搭建-准备和加入 3、基石框架篇-域树&域林架构-信息收集-手工和工具1、工作组(局域网) 将不同的计算机按照功能分别列入不同的工作组。想要访问某个部门的…

24/7/12总结

axios Axios 是一个基于 promise 网络请求库&#xff0c;作用于node.js 和浏览器中。 它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使用原生 node.js http 模块, 而在客户端 (浏览端) 则使用 XMLHttpRequests。 get请求: <script>function…

WordPress 主题技巧:给文章页增加“谁来过”模块。

模块功能&#xff1a; 我个人目前在做一个电影类的网站&#xff0c;在开发文章页的模版时候&#xff0c;突然觉得给文章页增加一个“谁对本电影感兴趣”的功能模块可能会比较有趣&#xff0c;这个功能有点类似于‘足迹’的感觉&#xff0c;用户可以通过这个功能&#xff0c;发…

C# Winform 系统方案目录的管理开发

在做一个中等复杂程度项目时&#xff0c;我们通常有系统全局配置&#xff0c;还要有对应的方案目录的管理和更新。 比如我们有如下需求&#xff1a;开发一个方案管理&#xff0c;可以新建、打开和保存方案&#xff0c;同时还需要保存方案中的各种文件。我设计的采用目录管理和…

短视频SEO矩阵系统:源码开发与部署全攻略

在数字化时代&#xff0c;短视频已成为人们获取信息、娱乐休闲的重要方式。随着短视频平台的兴起&#xff0c;如何让自己的内容在众多视频中脱颖而出&#xff0c;成为每个创作者和内容运营者关注的焦点。本文将为您深入解析短视频SEO矩阵系统的源码开发与部署&#xff0c;助您在…

Flink异常:org/apache/hadoop/hive/ql/parse/SemanticException

在flink项目中跑 上面这段代码出现如下这个异常&#xff0c; java.lang.NoClassDefFoundError: org/apache/thrift/TException 加上下面这个依赖后不报错 <dependency> <groupId>org.apache.thrift</groupId> <artifactId>libthrift</artifactId…

PostgreSQL(二十二)缓冲区管理器

目录 一、缓冲区概述 1、缓冲区结构 2、buffer_tag结构 3、Backend进程读取操作 4、写脏块 二、缓冲区管理器结构 1、第一层&#xff1a;Buffer Table layer&#xff08;缓冲区表层&#xff09; 2、第二层&#xff1a;Buffer Descriptor Layer&#xff08;缓冲区描述层…

有必要把共享服务器升级到VPS吗?

根据自己的需求来选择是否升级&#xff0c;虚拟专用服务器 (VPS) 是一种托管解决方案&#xff0c;它以低得多的成本提供专用服务器的大部分功能。使用 VPS&#xff0c;您的虚拟服务器将与在其上运行的其他虚拟服务器共享硬件服务器的资源。但是&#xff0c;与传统的共享托管&am…

用python识别二维码(python实例二十三)

目录 1.认识Python 2.环境与工具 2.1 python环境 2.2 Visual Studio Code编译 3.识别二维码 3.1 代码构思 3.2 代码实例 3.3 运行结果 4.总结 1.认识Python Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。 Python 的设计具有很强的可读性&…

Hadoop3:HDFS-通过配置黑白名单对集群进行扩缩容,并实现数据均衡(实用)

一、集群情况介绍 我的本地虚拟机&#xff0c;一共有三个节点&#xff0c;hadoop102、hadoop103、hadoop104 二、白名单 创建白名单文件whitelist&#xff0c;通过白名单的配置&#xff0c;只允许集群包含102和103两台机器可以存储数据&#xff0c;104无法存储数据。 需求 …