QCon 演讲火热征集中,快来分享技术实践与洞见! 了解详情
写点什么

构建高可伸缩性的 WEB 交互式系统(中)

  • 2014-09-17
  • 本文字数:3686 字

    阅读完需:约 12 分钟

《构建高可伸缩性的WEB 交互式系统》的第一篇,我们介绍了Web 交互式系统中平台的可伸缩性。本文将描述模块的可伸缩性。

模块的可伸缩性

WEB 交互式系统对模块的可伸缩性同样表现为:

  • 可扩展性:对于系统新增的功能需求能够快速响应支持
  • 可缩减性:对于系统退化的模块能够以最小的修改方式剔除

这里我们提供一套模块调度的系统架构模式,用于支持单页富应用系统的设计架构、模块拆分、模块重组、调度管理等功能。

模块

我们定义的模块是指:从系统中拆分出来的、可与用户进行交互完成一部分完整功能的独立单元。

模块组成

因为这里描述的模块可独立与用户完成交互功能,因此模块会包含以下元素:

  • 样式:定义模块的效果
  • 结构:定义模块的结构
  • 逻辑:实现模块的功能

以上元素对于一个 WEB 系统开发者来说并不陌生,而我们只需要寻求一种形式将这些内容封装起来即可。

模块封装

从模块的组成我们可以看到系统中分离出来的模块可能会长成这个样子,比如 module.html 就是我们分离出来的一个模块。

当然这里也可以用脚本文件封装,样式和结构采用注入形式。下面以 html 文件封装举例:

复制代码
<!-- 模块样式 -->
<style>
.m-mdl-1 .a{color:#aaa;}
.m-mdl-1 .b{color:#bbb;}
/* 此处省略若干内容 */
</style>
<!-- 模块结构 -->
<div class="m-mdl-1">
<p class="a">aaaaaaaaaaaaaaaaaaa</p>
<p class="b">bbbbbbbbbbbbbbbbbbb</p>
<!-- 此处省略若干内容 -->
</div>
<!-- 模块逻辑 -->
<script>
(function(){
var a = 'aaa';
var b = 'bbb';
// 此处省略若干内容
})();
</script>

这个模块在用户需要时加载到客户端,并展现出来跟用户进行交互,完成功能。但是我们会发现,如果系统预加载了此模块或者模块在 parse 时,这些内容会被直接执行,而这个结果并不是我们需要的,因此我们需要将模块的各元素文本化处理。文本化处理有多种方式,如作为文本 script、textarea 等标签内容,因此 module.html 里的模块我们可以封装成如下样子,以 textarea 举例:

复制代码
<!-- 模块样式 -->
<textarea name="css">
.m-mdl-1 .a{color:#aaa;}
.m-mdl-1 .b{color:#bbb;}
/* 此处省略若干内容 */
</textarea>
<!-- 模块结构 -->
<textarea name="html">
<div class="m-mdl-1">
<p class="a">aaaaaaaaaaaaaaaaaaa</p>
<p class="b">bbbbbbbbbbbbbbbbbbb</p>
<!-- 此处省略若干内容 -->
</div>
</textarea>
<!-- 模块逻辑 -->
<textarea name="js">
(function(){
var a = 'aaa';
var b = 'bbb';
// 此处省略若干内容
})();
</textarea>

管理依赖

从系统中拆分出来的模块之间是存在有一定关系的,如一个模块的呈现必须依赖另外一个模块的呈现。下面我们会以一个简单的例子来讲解模块之间的依赖管理,如下图是我们的一个单页应用系统:

从上图不难看出整个系统包含以下几部分内容:

  • 日志管理

    • 日志:日志列表,可切换收件箱 / 草稿箱 / 回收站 / 标签
    • 标签:标签列表,可转至日志按标签查看列表
  • 博客设置

    • 账号管理

      • 基本资料:用户基本资料设置表单
      • 个人经历:个人经历填写表单
    • 权限设置:权限设置表单

而这些模块之间的层级关系则如下所示:

针对交互式系统的这种层级架构典型的模式可以参阅:

然而在 WEB 交互式系统的实践过程中我们发现这种模式会存在一些缺陷:

  • 由于每个父模块自己维护了所有的子模块,因此父子模块之间耦合性过强,父模块必须耦合所有子模块
  • 由于模块之间不能直接越级调用,因此子模块需要其他模块协助时必须层层向上传递事件,如果层级过深则会影响到系统效率
  • 模块的增删等变化导致的变更涉及的影响较大,删除中间节点上的模块可能导致相邻的若干模块的变更
  • 多人协作开发系统时存在依赖关系的模块会导致开发人员之间的紧密耦合

在这里,我们给出了一种基于模块标识的依赖管理配置方案,可以彻底的将模块进行解耦,每个模块可以独立完整的完成自己的交互功能,而系统的整合则可以通过配置的方式灵活的重组各模块,模块的增删操作只需修改配置即可完成,而无需影响到具体业务逻辑。

下文我们会通过以上例子来讲解此方案的原理和实际操作方式。

模块标识

因为本方案会基于模块标识做配置,因此在介绍方案之前我们先介绍一下模块标识,这里我们给模块标识取名为UMI(Uniform Module Identifier)统一模块标识,下文简称 UMI,遵循以下规则约定:

  • 格式同 URI 的 Path 部分,如 /m/m0/
  • 必须以“/”符开始
  • 私有模块必须以“/?”开始
  • 承载模块的依赖关系,如 /m/m0/ 和 /m/m1/ 表明这两个标识对应模块的父模块标识均为 /m

每个 UMI 均可唯一标识一个模块及模块在系统中的依赖关系,在模块章节我们介绍了一个模块可以用一个 html 进行封装,因此我们可以得到以下结果:

每个 UMI 均可映射到一个模块实现文件,这样我们就可以将模块从具体实现中解耦出来,对模块的增删修改操作只需调整 UMI 和模块文件的映射关系即可,而无需涉及具体业务逻辑的修改。

模块依赖

在解决了模块与实现分离的问题后,我们接下来需要将层级式的模块扁平化来解耦模块之间的依赖关系。回到前面的例子,模块之间的层级关系如下图所示:

如果我们将图中的依赖关系进行抽象分离后,可以发现所有的模块即可呈现扁平的状态:

而对于模块之前的依赖关系的管理,在所有系统中都是一致的,但是每个模块的具体功能实现是由系统来决定的,不同的系统是截然不同的,因此本方案提供的解决方案主要是用来维护模块之间的依赖关系的。

从上图我们可以比较清楚的看到模块之间的依赖关系呈现树状结构,因此我们会以树的结构来组织维护模块之间的依赖关系,我们称之为依赖关系树。而当我们将这棵树上的任意节点与根节点之间的路径用“/”分隔序列化后,发现刚好与我们提供的 UMI 是匹配的,因此组成系统的模块的 UMI 可以跟依赖关系树的节点一一对应起来,如下图所示:

在模块标识章节我们介绍了 UMI 与模块封装文件可以相互映射,因此依赖关系树上的节点可以直接与模块的实现文件做一一对应,如下图所示:

至此,我们将垂直层级依赖的模块通过依赖关系树分解成了无任何关系的扁平模块结构。

模块组合

模块只需要有个呈现容器即可渲染出来,因此模块如果需要能够做任意组合,只需将模块分成两种类型:提供容器的模块,和使用容器的模块即可。当然,一个模块可同时兼具提供容器和使用容器的功能,提供容器的模块和使用容器的模块可任意组合。

对于模块组合的配置代码范例:

复制代码
'/m/blog/list/':{
module:'module/layout/blog.list/index.html',
composite:{
box:'/?/blog/box/',
tag:'/?/blog/tag/',
list:'/?/blog/list/',
clazz:'/?/blog/class/'
}
}

调度策略

在将模块扁平化后,各模块就可以安排给不同的开发人员进行功能实现和测试了,各模块完成后根据依赖关系树进行系统整合,系统整合后各模块会遵循一定的调度策略进行调度。

模块状态

根据模块调度的阶段划分,模块的状态可以分为以下四种:

  • 模块构建:构建模块结构
  • 模块显示:将模块渲染到指定的容器中
  • 模块刷新:根据外界输入的参数信息获取数据并展示(这里主要做数据处理)
  • 模块隐藏:模块放至内存中,回收由显示和刷新阶段产生的额外数据及结构

调度策略主要控制模块在这几个阶段之间的转换规则。

模块显示

当用户请求显示一个模块时各模块会遵循以下步骤进行调度,假设请求显示 /m/blog/list/ 模块:

  1. 检查目标节点到根节点路径上注册的模块,如果注册的是模块的实现文件地址,则请求载入模块实现文件
  2. 如果节点所在的模块的所有祖先节点已显示,则当前模块可被显示出来,否则等待祖先模块的显示调度
  3. 模块载入后根据第二步骤原则尝试调度目标模块的显示

模块切换

当用户从一个模块切换到另外一个模块时各模块遵循以下步骤调度,假设从 /m/blog/list/ 切换到 /m/setting/account/edu/ 模块:

  1. 查找源模块与目标模块的公共父节点


2. 从源节点到公共节点之间的模块调度隐藏操作


3. 从根节点到公共节点之间的模块调度刷新操作


4. 从公共节点到目标节点之间的模块调度显示操作

消息通道

大部分时候我们不建议使用模块之前的消息通信,实践中也存在一些特殊情况会需要模块之前的消息通信,这里提供两种方式的消息通讯:

  • 点对点的消息:一个模块发送消息时明确指定目标模块的 UMI
  • 观察订阅消息:一个模块可以对外申明发布了什么样的消息,有需要的模块可以订阅该模块 UMI 上的消息

上面介绍了模块可伸缩性的一些原理。在本系列的最后一篇文章中,我们将以网易的 NEJ 框架为例,对上述原则进行说明。敬请期待!

本作品采用知识共享署名 4.0 国际许可协议进行许可。

作者介绍

蔡剑飞,网易杭州研究院前端高级技术专家,2005 年加入网易,先后参与过网易邮箱、网易博客、网易相册、网易云音乐等产品的开发,从2010 年开始开发NEJ 框架,现在负责NEJ 框架的维护及培训。他的邮箱是 genify@163.com ,微博是 genify ,欢迎大家来信交流!


感谢张云龙对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ )或者腾讯微博( @InfoQ )关注我们,并与我们的编辑和其他读者朋友交流。

2014-09-17 17:056888

评论

发布
暂无评论
发现更多内容

地平线轨迹预测 QCNet 参考算法-V1.0

地平线开发者

自动驾驶 算法

TDengine 与 SCADA 系统无缝连接,点击查看全面操作指南

TDengine

数据库 tdengine 时序数据库

磁盘诊断工具SMART Utility for mac

Mac相关知识分享

软件架构:问题起源和应对

FunTester

【YashanDB知识库】YAS-02025 no free space in virtual memory pool

YashanDB

yashandb 崖山数据库 yashandb知识库

探索端智能,加速大模型应用,火山引擎边缘智能 x 扣子技术沙龙等你来

火山引擎边缘云

机器人 智能IoT边缘服务 大模型

聊聊职场务实和务虚的事

老张

认知提升 职场成长

Java 应用程序性能分析工具JProfiler for Mac

Mac相关知识分享

公司的电脑性能差有什么解决办法?

上海锐起科技

替换 Oracle ,江河信息用 TDengine 解决高基数查询写入问题

TDengine

数据库 tdengine

移动云参编全球首个大模型供应链安全国际标准

科技热闻

【YashanDB知识库】archivelog磁盘满导致数据库abnormal

YashanDB

yashandb 崖山数据库 yashandb知识库

Gyges Labs引领行业变革,全球首款隐形显示AI眼镜亮相光博会

江湖老铁

开源 TTS 模型「Fish Speech」1.4 发布;GameGen-O :生成开放世界游戏视频模型丨 RTE 开发者日报

声网

一张图精通多种排序算法的选择策略

肖哥弹架构

Java 算法

【YashanDB知识库】数据库获取时间和服务器时间不一致

YashanDB

yashandb 崖山数据库 yashandb知识库

降本增效、极简体验!828就选华为云Flexus云数据库RDS

轶天下事

中小企业引入MES系统:提升生产效能

万界星空科技

数字化转型 工业互联网 制造业 生产管理系统 mes

AI生成文档?代码有用,领域知识为王

Bruce Talk

AI 敏捷开发 DDD

TDengine 与飞腾腾锐 D2000 完成兼容互认证,推动国产软硬件深度融合

TDengine

数据库 tdengine

面试官:线程池遇到未处理的异常会崩溃吗?

EquatorCoco

Python 面试 线程池

和鲸科技聘任上海交通大学医学院张维拓老师为公司医学行业专家顾问

ModelWhale

人工智能 大数据‘’ 医学研究

万界星空科技塑料制品行业MES解决方案

万界星空科技

制造业 mes 万界星空科技 塑料制品 塑料

3D动画制作软件NewTek LightWave 3D for mac

Mac相关知识分享

多任务 BT 种子下载工具BitComet for Mac

Mac相关知识分享

以 Buycbuy为蓝本:淘宝微店代购集运系统搭建秘籍

tbapi

淘宝代购系统 淘宝代购集运系统 华人代购系统 逆向海淘系统 代购商城系统

淘宝API:淘宝商品评论数据接口(Taobao.item_review)

tbapi

淘宝商品评论接口 淘宝评论API 淘宝商品评论采集 淘宝店铺评论采集

重庆飞亚实业:二维码革新,提升企业安全巡检效率

草料二维码

无代码 低代码平台 无代码平台 草料二维码 无代码低代码

高可用与低成本兼得:深入了解 TDengine 的双副本与双活方案

TDengine

数据库 tdengine

IPQ6010 IPQ6018 | Complete Guide to Captive Portal Configuration on Wallys DR6018 Router

wallyslilly

IPQ6010 ipq6018 Captive Portal

内幕!smardaten无代码平台全方位测评,这些细节你绝对想不到!

中杯可乐多加冰

低代码 无代码开发 无代码 无代码平台

构建高可伸缩性的WEB交互式系统(中)_语言 & 开发_蔡剑飞_InfoQ精选文章