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

原文转自:http://kb.cnblogs.com/page/503953/

《构建高可伸缩性的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 国际许可协议进行许可。

时间: 2024-11-08 03:59:37

【转发】构建高可伸缩性的WEB交互式系统(中)的相关文章

【转发】构建高可伸缩性的WEB交互式系统(下)

原文转自:http://kb.cnblogs.com/page/504518/ 本文是<构建高可伸缩性的WEB交互式系统>系列文章的第三篇,以网易的NEJ框架为例,对模块的可伸缩性进行分析介绍. 实例分析 NEJ框架根据前两篇的描述对此套架构模式做了实现,下面我们用具体实例讲解如何使用NEJ中的模块调度系统来拆分一个复杂系统.开发测试模块.整合系统等. 系统分解 绘制层级关系图 当我们拿到一个复杂系统时,根据交互稿可以绘制出组成系统的模块的层级关系图,并确定系统对外可访问的模块. 抽象依赖关系

【转发】构建高可伸缩性的WEB交互式系统(上)

原文转自:http://kb.cnblogs.com/page/503460/ 可伸缩性是一种对软件系统处理能力的设计指标,高可伸缩性代表一种弹性,在系统扩展过程中,能够保证旺盛的生命力,通过很少的改动,就能实现整个系统处理能力的增长. 在系统设计的时候,充分地考虑系统的可伸缩性,一方面能够极大地减少日后的维护开销,并帮助决策者对于投资所能获得的回报进行更加精准的估计:另一方面,高可伸缩性的系统往往会具有更好的容灾能力,从而提供更好的用户体验. WEB交互式系统的可伸缩性主要体现在两个方面: 平

用Nginx+Telegraf+Influxb+Grafana构建高逼格Nginx集群监控系统

日常生产环境搭建了Nginx集群后,就需要继续深入研究的就是日常Nginx监控. Nginx如何监控?相信百度就可以找到:nginx-status 通过Nginx-status,实时获取到Nginx监控数据后,如何和现有监控系统集成?一个很好的解决方案: Nginx+Telegraf+Influxdb+Grafana 即通过Telegraf监控插件定时收集Nginx的监控状态,存储到时序数据库Influxdb中,然后通过Grafana展现即可. 一.Nginx启用nginx-status功能 源

[转发]如何在ASP.NET的web.config配置文件中添加MIME类型

常常有一些特殊的MIME类型是IIS中没有的,一般来说要我们自己手动添加.如果网站经常更换服务器或者网站代码是提供给多个用户使用,那么会造成网站中用到的特殊的MIME类型要经常性的在IIS上配置.这里考虑到一个网站配置通用性问题,所以我们可以将MIME类型添加到ASP.NET网站的配置文件中,这样用户就不用一直配置IIS了. 一般来说我们是将MIME的这部份信息配置在system.webServer节点的staticContent节点中,配置的时候只要设置mimeMap节点中的fileExten

交互式系统中采用的调度算法

1.轮转调度 (1)时间片轮转调度算法 CPU的时间分成一段一段的,每一段就是一个时间片,进程轮流使用时间片.示意图如下: (2)虚拟轮转法 时间片轮转调度算法对IO型进程不公平:对于CPU型进程,该进程获得CPU时间片后总是会用完整个时间片:但是对于IO型,它经常是运行一部分时间片后就去等待IO事件,总是用不完自己的时间片.为了解决这个问题,提出了虚拟轮转法,该算法的示意图如下: 虚拟轮转调度算法的思想是:进程用完时间片后,回到就绪队列:而进程因为等待某个事件放弃CPU,该进程等待的事件发生后

基于Web的系统测试方法

基于Web的系统测试与传统的软件测试既有相同之处,也有不同的地方,对软件测试提出了新的挑战.基于Web的系统测试不但需要检查和验证是否按照设计的要求运行,而且还要评价系统在不同用户的浏览器端的显示是否合适.重要的是,还要从最终用户的角度进行安全性和可用性测试. 本文从功能.性能.可用性.客户端兼容性.安全性等方面讨论了基于Web的系统测试方法. 随着Internet和Intranet/Extranet的快速增长,Web已经对商业.工业.银行.财政.教育.政府和娱乐及我们的工作和生活产生了深远的影

构建高可扩Web架构和分布式系统(2)

构建快速可伸缩的数据访问块 在讨论完设计分布式系统的核心考虑因素后,下面让我们再一起讨论难点部分:可扩展的数据访问. 大多数简单的Web应用程序,例如LAMP堆栈应用程序,看起来如图5所示: 图5:简单的Web应用程序 随着系统渐渐扩大,他们将面临两大主要挑战:构建可扩展的应用程序服务器和数据访问机制.在一个高可扩的应用程序设计中,通常最小化的应用程序(或Web)服务往往能体现一种无共享的架构.这就使得应用程序服务器层要进行横向扩展,这种设计的结果就是把繁重的工作转移到堆栈下层的数据库服务和配置

构建高并发高可用的电商平台架构实践

从各个角度总结了电商平台中的架构实践,由于时间仓促,定了个初稿,待补充完善,欢迎大家一起交流. 转载请声明出处:http://blog.csdn.net/yangbutao/article/details/12242441 作者:杨步涛 关注分布式架构.大数据.搜索.开源技术 QQ:306591368 技术Blog:http://blog.csdn.net/yangbutao 一. 设计理念 1.      空间换时间 1)      多级缓存,静态化 客户端页面缓存(http header中包

构建高并发高可用的电商平台架构实践(上)

构建高并发高可用的电商平台架构实践(上) 一. 设计理念 1.      空间换时间 1)      多级缓存,静态化 客户端页面缓存(http header中包含Expires/Cache of Control,last modified(304,server不返回body,客户端可以继续用cache,减少流量),ETag) 反向代理缓存 应用端的缓存(memcache) 内存数据库 Buffer.cache机制(数据库,中间件等) 2)      索引 哈希.B树.倒排.bitmap 哈希索