架构篇10:架构设计流程-识别复杂度

文章目录

    • 架构设计第 1 步:识别复杂度
    • 识别复杂度实战
    • 识别复杂度心得
    • 小结

从今天开始,我们分4期,结合复杂度来源和架构设计原则,通过一个模拟的设计场景“前浪微博”,一起看看在实践中究竟如何进行架构设计。今天先来看架构设计流程第 1 步:识别复杂度。

架构设计第 1 步:识别复杂度

我在前面讲过,架构设计的本质目的是为了解决软件系统的复杂性,所以在我们设计架构时,首先就要分析系统的复杂性。只有正确分析出了系统的复杂性,后续的架构设计方案才不会偏离方向;否则,如果对系统的复杂性判断错误,即使后续的架构设计方案再完美再先进,都是南辕北辙,做的越好,错的越多、越离谱。

例如,如果一个系统的复杂度本来是业务逻辑太复杂,功能耦合严重,架构师却设计了一个 TPS 达到 50000/ 秒的高性能架构,即使这个架构最终的性能再优秀也没有任何意义,因为架构没有解决正确的复杂性问题。

架构的复杂度主要来源于“高性能”“高可用”“可扩展”等几个方面,但架构师在具体判断复杂性的时候,不能生搬硬套,认为任何时候架构都必须同时满足这三方面的要求。实际上大部分场景下,复杂度只是其中的某一个,少数情况下包含其中两个,如果真的出现同时需要解决三个或者三个以上的复杂度,要么说明这个系统之前设计的有问题,要么可能就是架构师的判断出现了失误,即使真的认为要同时满足这三方面的要求,也必须要进行优先级排序。

例如,专栏前面提到过的“亿级用户平台”失败的案例,设计对标腾讯的 QQ,按照腾讯 QQ 的用户量级和功能复杂度进行设计,高性能、高可用、可扩展、安全等技术一应俱全,一开始就设计出了 40 多个子系统,然后投入大量人力开发了将近 1 年时间才跌跌撞撞地正式上线。上线后发现之前的过度设计完全是多此一举,而且带来很多问题:

  • 系统复杂无比,运维效率低下,每次业务版本升级都需要十几个子系统同步升级,操作步骤复杂,容易出错,出错后回滚还可能带来二次问题。
  • 每次版本开发和升级都需要十几个子系统配合,开发效率低下。
  • 子系统数量太多,关系复杂,小问题不断,而且出问题后定位困难。
  • 开始设计的号称 TPS 50000/ 秒的系统,实际 TPS 连 500 都不到。

由于业务没有发展,最初的设计人员陆续离开,后来接手的团队,无奈又花了 2 年时间将系统重构,合并很多子系统,将原来 40 多个子系统合并成不到 20 个子系统,整个系统才逐步稳定下来。

如果运气真的不好,接手了一个每个复杂度都存在问题的系统,那应该怎么办呢?答案是一个个来解决问题,不要幻想一次架构重构解决所有问题。例如这个“亿级用户平台”的案例,后来接手的团队其实面临几个主要的问题:系统稳定性不高,经常出各种莫名的小问题;系统子系统数量太多,系统关系复杂,开发效率低;不支持异地多活,机房级别的故障会导致业务整体不可用。如果同时要解决这些问题,就可能会面临这些困境:

  • 要做的事情太多,反而感觉无从下手。
  • 设计方案本身太复杂,落地时间遥遥无期。
  • 同一个方案要解决不同的复杂性,有的设计点是互相矛盾的。例如,要提升系统可用性,就需要将数据及时存储到硬盘上,而硬盘刷盘反过来又会影响系统性能。

因此,正确的做法是将主要的复杂度问题列出来,然后根据业务、技术、团队等综合情况进行排序,优先解决当前面临的最主要的复杂度问题。“亿级用户平台”这个案例,团队就优先选择将子系统的数量降下来,后来发现子系统数量降下来后,不但开发效率提升了,原来经常发生的小问题也基本消失了,于是团队再在这个基础上做了异地多活方案,也取得了非常好的效果。

对于按照复杂度优先级解决的方式,存在一个普遍的担忧:如果按照优先级来解决复杂度,可能会出现解决了优先级排在前面的复杂度后,解决后续复杂度的方案需要将已经落地的方案推倒重来。这个担忧理论上是可能的,但现实中几乎是不可能出现的,原因在于软件系统的可塑性和易变性。对于同一个复杂度问题,软件系统的方案可以有多个,总是可以挑出综合来看性价比最高的方案。

即使架构师决定要推倒重来,这个新的方案也必须能够同时解决已经被解决的复杂度问题,一般来说能够达到这种理想状态的方案基本都是依靠新技术的引入。例如,Hadoop 能够将高可用、高性能、大容量三个大数据处理的复杂度问题同时解决。

识别复杂度对架构师来说是一项挑战,因为原始的需求中并没有哪个地方会明确地说明复杂度在哪里,需要架构师在理解需求的基础上进行分析。有经验的架构师可能一看需求就知道复杂度大概在哪里;如果经验不足,那只能采取“排查法”,从不同的角度逐一进行分析。

识别复杂度实战

我们假想一个创业公司,名称叫作“前浪微博”。前浪微博的业务发展很快,系统也越来越多,系统间协作的效率很低,例如:

  • 用户发一条微博后,微博子系统需要通知审核子系统进行审核,然后通知统计子系统进行统计,再通知广告子系统进行广告预测,接着通知消息子系统进行消息推送……一条微博有十几个通知,目前都是系统间通过接口调用的。每通知一个新系统,微博子系统就要设计接口、进行测试,效率很低,问题定位很麻烦,经常和其他子系统的技术人员产生分岐,微博子系统的开发人员不胜其烦。
  • 用户等级达到 VIP 后,等级子系统要通知福利子系统进行奖品发放,要通知客服子系统安排专属服务人员,要通知商品子系统进行商品打折处理……等级子系统的开发人员也是不胜其烦。

新来的架构师在梳理这些问题时,结合自己的经验,敏锐地发现了这些问题背后的根源在于架构上各业务子系统强耦合,而消息队列系统正好可以完成子系统的解耦,于是提议要引入消息队列系统。经过一分析二讨论三开会四汇报五审批等一系列操作后,消息队列系统终于立项了。其他背景信息还有:

  • 中间件团队规模不大,大约 6 人左右。
  • 中间件团队熟悉 Java 语言,但有一个新同事 C/C++ 很牛。
  • 开发平台是 Linux,数据库是 MySQL。
  • 目前整个业务系统是单机房部署,没有双机房。

针对前浪微博的消息队列系统,采用“排查法”来分析复杂度,具体分析过程是:

  • 这个消息队列是否需要高性能

我们假设前浪微博系统用户每天发送 1000 万条微博,那么微博子系统一天会产生 1000 万条消息,我们再假设平均一条消息有 10 个子系统读取,那么其他子系统读取的消息大约是 1 亿次。

1000 万和 1 亿看起来很吓人,但对于架构师来说,关注的不是一天的数据,而是 1 秒的数据,即 TPS 和 QPS。我们将数据按照秒来计算,一天内平均每秒写入消息数为 115 条,每秒读取的消息数是 1150 条;再考虑系统的读写并不是完全平均的,设计的目标应该以峰值来计算。峰值一般取平均值的 3 倍,那么消息队列系统的 TPS 是 345,QPS 是 3450,这个量级的数据意味着并不要求高性能。

虽然根据当前业务规模计算的性能要求并不高,但业务会增长,因此系统设计需要考虑一定的性能余量。由于现在的基数较低,为了预留一定的系统容量应对后续业务的发展,我们将设计目标设定为峰值的 4 倍,因此最终的性能要求是:TPS 为 1380,QPS 为 13800。TPS 为 1380 并不高,但 QPS 为 13800 已经比较高了,因此高性能读取是复杂度之一。注意,这里的设计目标设定为峰值的 4 倍是根据业务发展速度来预估的,不是固定为 4 倍,不同的业务可以是 2 倍,也可以是 8 倍,但一般不要设定在 10 倍以上,更不要一上来就按照 100 倍预估。

  • 这个消息队列是否需要高可用性

对于微博子系统来说,如果消息丢了,导致没有审核,然后触犯了国家法律法规,则是非常严重的事情;对于等级子系统来说,如果用户达到相应等级后,系统没有给他奖品和专属服务,则 VIP 用户会很不满意,导致用户流失从而损失收入,虽然也比较关键,但没有审核子系统丢消息那么严重。

综合来看,消息队列需要高可用性,包括消息写入、消息存储、消息读取都需要保证高可用性。

  • 这个消息队列是否需要高可扩展性

消息队列的功能很明确,基本无须扩展,因此可扩展性不是这个消息队列的复杂度关键。

为了方便理解,这里我只排查“高性能”“高可用”“扩展性”这 3 个复杂度,在实际应用中,不同的公司或者团队,可能还有一些其他方面的复杂度分析。例如,金融系统可能需要考虑安全性,有的公司会考虑成本等。

综合分析下来,消息队列的复杂性主要体现在这几个方面:高性能消息读取、高可用消息写入、高可用消息存储、高可用消息读取。

“前浪微博”的消息队列设计才刚完成第 1 步,专栏下一期会根据今天识别的复杂度设计备选方案,前面提到的场景在下一期还会用到哦。

识别复杂度心得

架构设计由需求所驱动,本质目的是为了解决软件系统的复杂性;为此,我们在进行架构设计时,需要以理解需求为前提,首要进行系统复杂性的分析。具体做法是:

  1. 构建复杂度的来源清单——高性能、可用性、扩展性、安全、低成本、规模等。
  2. 结合需求、技术、团队、资源等对上述复杂度逐一分析是否需要?是否关键?
  • “高性能”主要从软件系统未来的TPS、响应时间、服务器资源利用率等客观指标,也可以从用户的主观感受方面去考虑。

  • “可用性”主要从服务不中断等质量属性,符合行业政策、国家法规等方面去考虑。

  • “扩展性”则主要从功能需求的未来变更幅度等方面去考虑。

  1. 按照上述的分析结论,得到复杂度按照优先级的排序清单,越是排在前面的复杂度,就越关键,就越优先解决。

需要特别注意的是:随着所处的业务阶段不同、外部的技术条件和环境的不同,得到的复杂度问题的优先级排序就会有所不同。一切皆变化。

小结

今天我们聊了架构设计流程的第一个步骤“识别复杂度”,并且通过一个模拟的场景讲述了“排查法”的具体分析方式,希望对你有所帮助。


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

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Zookeeper+Kafka集群

1 Zookeeper 1.1 Zookeeper概述 Zookeeper是一个开源的分布式的,为分布式框架提供协调服务的Apache项目。 分布式系统管理框架,主要用来解决分布式应用集群中应用系统的一致性问题,想大于各种分布式应用的注册中心文件系统通知机制 本质 …

element plus表格的表头和内容居中

文章目录 需求分析 需求 对于 element-plus 中的 table 进行表头和内容的居中显示 分析 单列的表头和内容居中 &#xff1a; 在对应的那一列加上align“center” 即可 <el-table-column prop"name" label"商品名称" align"center" />…

0004.电脑开机提示按F1

常用的电脑主板不知道什么原因&#xff0c;莫名其妙的启动不了了。尝试了很多方法&#xff0c;没有奏效。没有办法我就只能把硬盘拆了下来&#xff0c;装到了另一台电脑上面。但是开机以后却提示F1&#xff0c;如下图&#xff1a; 根据上面的提示&#xff0c;应该是驱动有问题…

『论文阅读|2024 WACV 多目标跟踪Deep-EloU|纯中文版』

论文题目&#xff1a; Iterative Scale-Up ExpansionIoU and Deep Features Association for Multi-Object Tracking in Sports 论文特点&#xff1a; 作者提出了一种迭代扩展的 ExpansionIoU 和深度特征关联方法Deep-EIoU&#xff0c;用于体育场景中的多目标跟踪&#xff0c;旨…

JAVA的双亲委派机制

目录 双亲委派的介绍破坏双亲委派模型OSGI 双亲委派&#xff08;Parent Delegation&#xff09;是一种软件设计模式&#xff0c;常用于Java类加载器的实现。它通过继承关系来实现类的加载&#xff0c;即每个类加载器都有一个父加载器&#xff0c;当一个类加载器需要加载一个类时…

测试工程师必看!测试用例设计全解析,让你彻底掌握

测试工程师在入行时&#xff0c;都会接触到一个名词——测试用例&#xff0c;都知道测试用例是干什么用的&#xff0c;提到设计测试用例的方法&#xff0c;大部分测试工程师都会侃侃而谈&#xff1a;等价类法、边界值法、判定表法、正交分解法……这些方法说起来都如数家珍&…

一、认识 JVM 规范(JVM 概述、字节码指令集、Class文件解析、ASM)

1. JVM 概述 JVM&#xff1a;Java Virtual Machine&#xff0c;也就是 Java 虚拟机 所谓虚拟机是指&#xff1a;通过软件模拟的具有完整硬件系统功能的、运行在一个完全隔离环境中的计算机系统。 即&#xff1a;虚拟机是一个计算机系统。这种计算机系统运行在完全隔离的环境中…

Linux网络编程(二-套接字)

目录 一、背景知识 1.1 端口号 1.2 网络字节序 1.3 地址转换函数 二、Socket简介 三、套接字相关的函数 3.1 socket() 3.2 bind() 3.3 connect() 3.4 listen() 3.5 accept() 3.6 read()/recv()/recvfrom() 3.7 send()/sendto() 3.8 close() 四、UPD客服/服务端实…

打造专业级ChatGPT风格聊天界面:SpringBoot与Vue实现动态打字机效果,附完整前后端源码

大家好&#xff0c;今天用SpringBoot、vue写了一个仿ChatGPT官网聊天的打字机效果。 所有代码地址:gitee代码地址 &#xff0c;包含前端和后端&#xff0c;可以直接运行 使用本技术实现的项目&#xff1a;aicnn.cn&#xff0c;欢迎大家体验 如果文章知识点有错误的地方&#xf…

AEB滤镜再破碎,安全焦虑「解不开」?

不久前&#xff0c;理想L7重大交通事故&#xff0c;再次引发了公众对AEB的热议。 根据理想汽车公布的事故视频显示&#xff0c;碰撞发生前3秒&#xff0c;车速在178km/h时驾驶员采取了制动措施&#xff0c;但车速大幅超出AEB&#xff08;自动紧急刹车系统&#xff09;的工作范…

Java下载FTP服务器上的资源,附带FTP工具类

通过xftp可以看到目标服务器上面的资源如下&#xff1a; 第一步&#xff1a;导入ftp依赖&#xff1a; <dependency><groupId>commons-net</groupId><artifactId>commons-net</artifactId><version>3.7</version> <!-- 使用最新…

PyTorch 内 LibTorch/TorchScript 的使用

PyTorch 内 LibTorch/TorchScript 的使用 1. .pt .pth .bin .onnx 格式1.1 模型的保存与加载到底在做什么&#xff1f;1.2 为什么要约定格式&#xff1f;1.3 格式汇总1.3.1 .pt .pth 格式1.3.2 .bin 格式1.3.3 直接保存完整模型1.3.4 .onnx 格式1.3.5 jit.trace1.3.6 jit.scrip…

了解云工作负载保护:技术和最佳实践

云工作负载是指云环境中的应用程序或存储元素&#xff0c;无论是公共云、私有云还是混合云。每个云工作负载都使用云的资源&#xff0c;包括计算、网络和存储。 云工作负载可以多种多样&#xff0c;例如运行应用程序、数据库或托管网站。它们可以是静态的或动态的&#xff0c;…

mysql 导入数据 1273 - Unknown collation: ‘utf8mb4_0900_ai_ci‘

前言: mysql 导入数据 遇到这个错误 1273 - Unknown collation: utf8mb4_0900_ai_ci 具体原因没有深究 但应该是设计数据库的 字符集类型会出现这个问题 例如: char varchar text..... utf8mb4 类型可以存储表情 在现在这个时代会用很多 以后会用的更多 所以不建议改…

LabVIEW滚动轴承故障在线监测

展示了如何将LabVIEW开发出一种有效的滚动轴承故障在线监测系统。介绍了该系统的开发过程、工作原理及其在实际应用中的效果。该系统成功地应用于对滚动轴承故障的早期诊断&#xff0c;提高了故障检测的准确性和效率。 滚动轴承在工作过程中会产生复杂的振动信号&#xff0c;包…

【C语言进阶】预处理详解

引言 对预处理的相关知识进行详细的介绍 ✨ 猪巴戒&#xff1a;个人主页✨ 所属专栏&#xff1a;《C语言进阶》 &#x1f388;跟着猪巴戒&#xff0c;一起学习C语言&#x1f388; 目录 引言 预定义符号 #define定义常量 #define定义宏 带有副作用的宏参数 宏替换的规则 …

开源的测试平台快2千星了,能带来多少收益呢

最近看了下自己去年初开源的测试平台&#xff0c;star一起算的话也到1.7k了&#xff1a; 做开源的初心一方面是想把自己的理解和思想展示出来&#xff0c;另一方面是想进一步打造个人IP&#xff0c;提升影响力&#xff08;其实这个想法很早之前就有了&#xff0c;计划过无数次但…

【BERT】详解

BERT 简介 BERT 是谷歌在 2018 年时提出的一种基于 Transformer 的双向编码器的表示学习模型&#xff0c;它在多个 NLP 任务上刷新了记录。它利用了大量的无标注文本进行预训练&#xff0c;预训练任务有掩码语言模型和下一句预测&#xff0c;掩码语言模型指的是随机地替换文本中…

「云渲染C4D」C4D如何进行云渲染?

云渲染C4D的过程可现实一键式完成&#xff0c;目前云渲染平台随着技术的发展&#xff0c;平台的使用越发容易操作&#xff0c;无论是渲染文件的传输性、安全性、高效性都有较大的提升&#xff0c;本次为大家简单说明下关于云渲染操作方法。 &#xff08;图源网络&#xff09; …

Android状态栏布局隐藏的方法

1.问题如下&#xff0c;安卓布局很不协调 2.先将ActionBar设置为NoActionBar 先打开styles.xml 3.使用工具类 package com.afison.newfault.utils;import android.annotation.TargetApi; import android.app.Activity; import android.content.Context; import android.graph…