当前位置: 首页 > news >正文

架构篇06-复杂度来源:可扩展性

文章目录

    • 预测变化
    • 应对变化
    • 小结

复杂度来源前面已经讲了高性能和高可用,今天来聊聊可扩展性。

可扩展性指系统为了应对将来需求变化而提供的一种扩展能力,当有新的需求出现时,系统不需要或者仅需要少量修改就可以支持,无须整个系统重构或者重建。

由于软件系统固有的多变性,新的需求总会不断提出来,因此可扩展性显得尤其重要。在软件开发领域,面向对象思想的提出,就是为了解决可扩展性带来的问题;后来的设计模式,更是将可扩展性做到了极致。得益于设计模式的巨大影响力,几乎所有的技术人员对于可扩展性都特别重视。

设计具备良好可扩展性的系统,有两个基本条件:正确预测变化完美封装变化。但要达成这两个条件,本身也是一件复杂的事情,我来具体分析一下。

预测变化

软件系统与硬件或者建筑相比,有一个很大的差异:软件系统在发布后还可以不断地修改和演进,这就意味着不断有新的需求需要实现。如果新需求能够不改代码甚至少改代码就可以实现,那当然是皆大欢喜的,否则来一个需求就要求系统大改一次,成本会非常高,程序员心里也不爽(改来改去),产品经理也不爽(做得那么慢),老板也不爽(那么多人就只能干这么点事)。因此作为架构师,我们总是试图去预测所有的变化,然后设计完美的方案来应对,当下一次需求真正来临时,架构师可以自豪地说:这个我当时已经预测到了,架构已经完美地支持,只需要一两天工作量就可以了!

然而理想是美好的,现实却是复杂的。有一句谚语,“唯一不变的是变化”,如果按照这个标准去衡量,架构师每个设计方案都要考虑可扩展性。例如,架构师准备设计一个简单的后台管理系统,当架构师考虑用 MySQL 存储数据时,是否要考虑后续需要用 Oracle 来存储?当架构师设计用 HTTP 做接口协议时,是否要考虑要不要支持 ProtocolBuffer?甚至更离谱一点,架构师是否要考虑 VR 技术对架构的影响从而提前做好可扩展性?如果每个点都考虑可扩展性,架构师会不堪重负,架构设计也会异常庞大且最终无法落地。但架构师也不能完全不做预测,否则可能系统刚上线,马上来新的需求就需要重构,这同样意味着前期很多投入的工作量也白费了。

同时,“预测”这个词,本身就暗示了不可能每次预测都是准确的,如果预测的事情出错,我们期望中的需求迟迟不来,甚至被明确否定,那么基于预测做的架构设计就没什么作用,投入的工作量也就白费了。

综合分析,预测变化的复杂性在于:

  • 不能每个设计点都考虑可扩展性。
  • 不能完全不考虑可扩展性。
  • 所有的预测都存在出错的可能性。

对于架构师来说,如何把握预测的程度和提升预测结果的准确性,是一件很复杂的事情,而且没有通用的标准可以简单套上去,更多是靠自己的经验、直觉,所以架构设计评审的时候经常会出现两个设计师对某个判断争得面红耳赤的情况,原因就在于没有明确标准,不同的人理解和判断有偏差,而最终又只能选择一个判断。

应对变化

假设架构师经验非常丰富,目光非常敏锐,看问题非常准,所有的变化都能准确预测,是否意味着可扩展性就很容易实现了呢?也没那么理想!因为预测变化是一回事,采取什么方案来应对变化,又是另外一个复杂的事情。即使预测很准确,如果方案不合适,则系统扩展一样很麻烦。

img

第一种应对变化的常见方案是:将“变化”封装在一个“变化层”,将不变的部分封装在一个独立的“稳定层”

无论是变化层依赖稳定层,还是稳定层依赖变化层都是可以的,需要根据具体业务情况来设计。例如,如果系统需要支持 XML、JSON、ProtocolBuffer 三种接入方式,那么最终的架构就是上面图中的“形式 1”架构,也就是下面这样。

img

如果系统需要支持 MySQL、Oracle、DB2 数据库存储,那么最终的架构就变成了“形式 2”的架构了,你可以看下面这张图。

img

无论采取哪种形式,通过剥离变化层和稳定层的方式应对变化,都会带来两个主要的复杂性相关的问题。

  1. 系统需要拆分出变化层和稳定层

对于哪些属于变化层,哪些属于稳定层,很多时候并不是像前面的示例(不同接口协议或者不同数据库)那样明确,不同的人有不同的理解,导致架构设计评审的时候可能吵翻天。

  1. 需要设计变化层和稳定层之间的接口

接口设计同样至关重要,对于稳定层来说,接口肯定是越稳定越好;但对于变化层来说,在有差异的多个实现方式中找出共同点,并且还要保证当加入新的功能时原有的接口设计不需要太大修改,这是一件很复杂的事情。例如,MySQL 的 REPLACE INTO 和 Oracle 的 MERGE INTO 语法和功能有一些差异,那存储层如何向稳定层提供数据访问接口呢?是采取 MySQL 的方式,还是采取 Oracle 的方式,还是自适应判断?如果再考虑 DB2 的情况呢?相信你看到这里就已经能够大致体会到接口设计的复杂性了。

第二种常见的应对变化的方案是提炼出一个“抽象层”和一个“实现层”。抽象层是稳定的,实现层可以根据具体业务需要定制开发,当加入新的功能时,只需要增加新的实现,无须修改抽象层。这种方案典型的实践就是设计模式和规则引擎。考虑到绝大部分技术人员对设计模式都非常熟悉,我以设计模式为例来说明这种方案的复杂性。

以设计模式的“装饰者”模式来分析,下面是装饰者模式的类关系图。

img

图中的 Component 和 Decorator 就是抽象出来的规则,这个规则包括几部分:

  1. Component 和 Decorator 类

  2. Decorator 类继承 Component 类

  3. Decorator 类聚合了 Component 类

这个规则一旦抽象出来后就固定了,不能轻易修改。例如,把规则 3 去掉,就无法实现装饰者模式的目的了。

装饰者模式相比传统的继承来实现功能,确实灵活很多。例如,《设计模式》中装饰者模式的样例“TextView”类的实现,用了装饰者之后,能够灵活地给 TextView 增加额外更多功能,比如可以增加边框、滚动条、背景图片等,这些功能上的组合不影响规则,只需要按照规则实现即可。但装饰者模式相对普通的类实现模式,明显要复杂多了。本来一个函数或者一个类就能搞定的事情,现在要拆分成多个类,而且多个类之间必须按照装饰者模式来设计和调用。

规则引擎和设计模式类似,都是通过灵活的设计来达到可扩展的目的,但“灵活的设计”本身就是一件复杂的事情,不说别的,光是把 23 种设计模式全部理解和备注,都是一件很困难的事情。

小结

今天我们从预测变化和应对变化这两个设计可扩展性系统的条件,以及它们实现起来本身的复杂性,聊了复杂度来源之一的可扩展性,希望对你有所帮助。


【星猿杂谈】:在这里我们共同探索科技新趋势,分享积累的点滴,从编程语言到系统架构,从人工智能到高性能计算,我们追求技术的进步,同时珍视分享的力量。欢迎关注我们,在技术的精彩世界中一起遨游,发现更多未知!

相关文章:

架构篇06-复杂度来源:可扩展性

文章目录 预测变化应对变化小结 复杂度来源前面已经讲了高性能和高可用,今天来聊聊可扩展性。 可扩展性指系统为了应对将来需求变化而提供的一种扩展能力,当有新的需求出现时,系统不需要或者仅需要少量修改就可以支持,无须整个系…...

flowable流程结束触发监听器 flowable获取结束节点 flowable流程结束事件响应监听器

flowable流程结束触发监听器 | flowable流程结束获取结束节点 | flowable流程结束事件响应监听器 下面代码是该监听器是对每个到达结束事件后执行的。 原本的流程定义是如果其中任意某个节点进行了驳回,则直接结束流程。 所以在每个节点的驳回对应的排他网关都设…...

【Python3】【力扣题】389. 找不同

【力扣题】题目描述: 【Python3】代码: 1、解题思路:使用计数器分别统计字符串中的元素和出现次数,两个计数器相减,结果就是新添加的元素。 知识点:collections.Counter(...):字典子类&#x…...

【从0上手cornerstone3D】如何加载nifti格式的文件

在线演示 支持加载的文件格式 .nii .nii.gz 代码实现 npm install cornerstonejs/nifti-volume-loader// ------------- 核心代码 Start------------------- // 注册一个nifti格式的加载器 volumeLoader.registerVolumeLoader("nifti",cornerstoneNiftiImageVolu…...

c# 学习笔记 - 异步编程

文章目录 1. 异步编程介绍1.1 简单介绍1.2 async/await 使用1.3 Task/Task<TResult> 对象 2. 样例2.1 迅速启动所有任务&#xff0c;仅当需要结果才等待任务执行2.2 使用 await 调用异步方法&#xff0c;即使这个异步方法内有 await 也不会同时执行回调和向下执行操作(必…...

设置了uni.chooseLocation,小程序中打不开

设置了uni.chooseLocation&#xff0c;在小程序打不开&#xff0c;点击没反应&#xff0c;地图显现不出来&#xff1b; 解决方案&#xff1a; 1.Hbuilder——微信开发者工具路径没有配置 打开工具——>设置 2.微信小程序服务端口没有开 解决方法&#xff1a;打开微信开发…...

spring retry 配置及使用

spring retry 配置及使用 接口或功能因外界异常导致失败后进行重推机制 依赖 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.1.RELEASE</version></p…...

uni-app的组件(二)

多项选择器checkbox-group 多项选择器&#xff0c;内部由多个 checkbox 组成。 <checkbox-group><checkbox checked color"red" value"1"></checkbox> 篮球<!-- disabled:是否禁用 --><checkbox disabled color"rgba(0,0…...

项目开发中安全问题以及解决办法——客户传进来的数据不可信

用户传进来的数据是不可信的&#xff0c;比如下面这种情况下&#xff1a; PostMapping("/order") public void wrong(RequestBody Order order) { this.createOrder(order); } Data public class Order { private long itemId; //商品ID private BigDecimal ite…...

解决springboot启动报Failed to start bean ‘subProtocolWebSocketHandler‘;

解决springboot启动报 Failed to start bean subProtocolWebSocketHandler; nested exception is java.lang.IllegalArgumentException: No handlers 问题发现问题解决 问题发现 使用springboot整合websocket&#xff0c;启动时报错&#xff0c;示例代码&#xff1a; EnableW…...

什么是技术架构?架构和框架之间的区别是什么?怎样去做好架构设计?(一)

什么是技术架构?架构和框架之间的区别是什么?怎样去做好架构设计?(一)。 在软件行业,对于什么是架构,都有很多的争论,每个人都有自己的理解。在不同的书籍上, 不同的作者, 对于架构的定义也不统一, 角度不同, 定义不同。 一、架构是什么 Linux 有架构,MySQL 有架构,J…...

【多线程】认识Thread类及其常用方法

&#x1f4c4;前言&#xff1a; 本文是对以往多线程学习中 Thread类 的介绍&#xff0c;以及对其中的部分细节问题进行总结。 文章目录 一. 线程的 创建和启动&#x1f346;1. 通过继承 Thread 类创建线程&#x1f345;2. 通过实现 Runnable 接口创建线程&#x1f966;3. 其他方…...

多用户商业版 whisper 2.1在线搭建教程

1. 准备工作 购买许可证&#xff1a;确保你已经购买了足够数量的用户许可证&#xff0c;以便所有员工或客户都能使用软件。系统要求&#xff1a;检查你的服务器和客户端计算机是否满足软件的最低系统要求。网络配置&#xff1a;确保你的网络环境&#xff08;如防火墙、路由器等…...

HEXO搭建个人博客

Hexo是一款基于Node.js的静态博客框架&#xff0c;可以生成静态网页托管在GitHub上。中文文档见HEXO 配置环境 安装Git&#xff1a;下载并安装Git 检查git是否正确安装&#xff1a; git --version 安装Node.js&#xff1a;Node.js 为大多数平台提供了官方的安装程序。注意安装…...

Spring MVC学习之——RequestMapping注解

RequestMapping注解 作用 用于建立请求URL和处理请求方法之间的对应关系。 属性 value&#xff1a;指定请求的实际地址&#xff0c;可以是一个字符串或者一个字符串列表。 value可以不写&#xff0c;直接在括号中写&#xff0c;默认就是value值 RequestMapping(value“/hel…...

鸿蒙原生应用/元服务开发-延迟任务开发实现(二)

一、接口说明 接口名接口描述startWork(work: WorkInfo): void;申请延迟任务stopWork(work: WorkInfo, needCancel?: boolean): void;取消延迟任务getWorkStatus(workId: number, callback: AsyncCallback>): void;获取延迟任务状态&#xff08;Callback形式&#xff09;g…...

机器学习在什么场景下最常用-九五小庞

机器学习在多个场景中都有广泛的应用&#xff0c;下面是一些常见的应用场景&#xff1a; 自然语言处理&#xff08;NLP&#xff09;&#xff1a;如语音识别、自动翻译、情感分析、垃圾邮件过滤等。数据挖掘和分析&#xff1a;如市场分析、用户画像、推荐系统、欺诈检测等。智能…...

利用IP应用场景API识别真实用户

引言 在当今数字化时代&#xff0c;随着互联网的普及和应用的广泛&#xff0c;验证用户身份的重要性变得越来越突出。在许多场景中&#xff0c;特别是在涉及安全性、用户体验以及个人隐私保护方面&#xff0c;确定用户的真实身份至关重要。而IP应用场景API则是一种强大的工具&…...

Hugging Face怎么通过国内镜像去进行模型下载(hf-mirror.com)

一、引言 Hugging Face &#x1f917;是一家专注于自然语言处理&#xff08;NLP&#xff09;技术的公司&#xff0c;以其开源贡献和先进的机器学习模型而闻名。该公司最著名的产品是 Transformers 库&#xff0c;这是一个广泛使用的 Python 库&#xff0c;它提供了大量预训练模…...

POKT Network 开启周期性通缩,该计划将持续至 2025 年

POKT Network&#xff08;也被称为 Pocket Network&#xff09;在通证经济模型上完成了重大的改进&#xff0c;不仅将通货膨胀率降至 5% 以下&#xff0c;并使 POKT 通证在 2025 年走向通缩的轨迹上&#xff0c;预计到2024 年年底通货膨胀率将降至 2% 以下。POKT Network 的 “…...

MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例

一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力

引言&#xff1a; 在人工智能快速发展的浪潮中&#xff0c;快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型&#xff08;LLM&#xff09;。该模型代表着该领域的重大突破&#xff0c;通过独特方式融合思考与非思考…...

【论文笔记】若干矿井粉尘检测算法概述

总的来说&#xff0c;传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度&#xff0c;通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?

uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件&#xff0c;用于在原生应用中加载 HTML 页面&#xff1a; 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf

FTP 客服管理系统 实现kefu123登录&#xff0c;不允许匿名访问&#xff0c;kefu只能访问/data/kefu目录&#xff0c;不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...

【JavaSE】多线程基础学习笔记

多线程基础 -线程相关概念 程序&#xff08;Program&#xff09; 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序&#xff0c;比如我们使用QQ&#xff0c;就启动了一个进程&#xff0c;操作系统就会为该进程分配内存…...

快刀集(1): 一刀斩断视频片头广告

一刀流&#xff1a;用一个简单脚本&#xff0c;秒杀视频片头广告&#xff0c;还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农&#xff0c;平时写代码之余看看电影、补补片&#xff0c;是再正常不过的事。 电影嘛&#xff0c;要沉浸&#xff0c;…...

4. TypeScript 类型推断与类型组合

一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式&#xff0c;自动确定它们的类型。 这一特性减少了显式类型注解的需要&#xff0c;在保持类型安全的同时简化了代码。通过分析上下文和初始值&#xff0c;TypeSc…...