如何有效的进行架构设计?

2021-11-24 14:49:47 浏览数 (1)

1.前言

最近描述产品或者架构解决方案的经验总结写的相对较多,这篇暂时不谈具体问题场景了,想聊一下关于架构设计的一点方法论和经验总结。之前的很长一段时间都在实践和学习架构等相关的内容,回想了一下工作以来接触到的系统:广告系统、营销活动系统、权益系统、支付&账务系统、资金决策系统,然后还有那些看起来规模庞大的重点项目,也算是有了一点自己的总结和思考,在这里表述出来分享给大家。

架构设计的理论很多,每个人的理解程度也不尽相同,思维习惯也差异很大,最重要的是能够根据场景灵活选择和应用来解决问题,能解决问题就好,并且无招胜有招。另外,丰富的技术硬储备、实践经验 是驾驭这些理论的关键。

2.关于架构设计的前因后果

2.1架构设计的定义

架构设计不仅是计算机范畴的内容,建筑行业、制造行业都有着大量的架构设计内容,架构设计这个词最早也是建筑行业发明的,先看相对笼统的概念: “架构设计是一种对于架构内部元素及元素间关系的一种主观映射的的产物,架构设计是一系列相关的抽象模式,用于指导大型软件系统各个方面的设计。” -- 源自百度百科的一点解释 再具象一点: “计算系统的软件架构师解释该系统所需的结构体的集合,其中包括:软件元素、元素之间的相互关系、以及二者各自的属性” -- 卡内基·梅隆大学软件工程研究所定义,业界比较认可的观点 整体来说比较认可上述的的观点,就从上述的概念来描述计算机软件架构设计吧。

2.2为什么需要架构设计

我们面临各种各样的现实问题,为了解决这些现实问题,我们需要做一些东西来应对,比如盖一栋楼、研发一个手机、研发一个系统等。 在做这些事情之前,我们需要知道我们做出来的东西需要具备哪些功能,而这些功能应该具备比如可靠性、安全性、延展性等哪些特性。 在动手开发之前,我们需要对于要做的事情进行设计,这份设计最核心的目的是: 1、骨架的角色:如果一堆现有的原材料放在面前是无从动手的,我们需要一个思路或者说“骨架”把要做的东西描绘出来。 2、方案的合理性:我们需要提前考虑到某些东西落地过程中可能存在的问题,避免实施时走进死胡同,或者未来而不断返工。

3、降低方案的复杂度:盖一栋楼是相当复杂的,但是在框架之上设计一栋楼的排水系统似乎就没有那么费劲了,确定管道间的连接方案似乎更简单了 4、促成概念的完整性,并为后人留一份说明书。

2.3什么时候需要进行架构设计

什么时机开始架构设计是十分重要的,业界也对这方面始终争论不休,有的主导预先设计,有的反对,有的人相对喜欢先做架构设计,有的人则懒得做,想到哪写到哪。 日常工作相对繁琐,面对的问题及系统现状也是千差万别,通常来说是在面对复杂问题和重要功能时需要进行架构设计,何为“复杂、重要”? 个人认为可以先从内容上做一些区分,比如下面是常见的需要架构设计的内容: 1、是否为全新的领域:如果是首次构建,架构的重要性是十分强的,而对于现有的系统增加个接口,架构考虑就会小很多了。 2、解空间小,或者压根没有一下子能想到的解决方案。 3、问题本身对于系统的要求特别高,相对难以实现,比如:可用性9个9,千万qps,未来变化频率十分高需要高扩展、灵活可配。 4、面临多方合作、协作时,需要一个从全局出发的指导。 5、需要精确衡量ROI的场景。 6、所开发的功能可能会引发相对较大的问题时。 然后再从时机的角度辅助判断: 1、在某项功能被重视起来,或者小步试错成功后准备大资源投入时。 2、系统的质量属性已经烂到了一定的地步,比如已经无法扩展、线上问题频发、研发质量下降、可用性已经无法良好提升等,忍无可忍时。 3、整体定位发生变化时

判断好时机、内容,是架构设计工作最重要的开始的第一步,只有这一步走好了,才能真正的好钢用到刀刃上。

2.4什么时候停止架构设计

结束同开始一样重要,我们需要把整架构设计工作做到一点都不浪费适时停止是很必要的。我们可以把工作分为:问题剖析、架构设计、详细设计、研发过程、测试过程、使用过程等n个顺序阶段,问题剖析结束时,架构设计开始。

2.4.1停止的时机

当系统所面临的主要问题已经不会影响到系统的整体骨架,并且有足够的框架和思路展开详细设计时,并且可预见的工作不会使我们调过头来修改架构时,我们的架构设计工作就应该停止了。

2.4.2哪些内容需要进行架构设计

同架构设计容易混淆的内容是详细设计,说个小技巧,但凡会影响到系统质量属性的设计内容都属于架构设计的范畴,哪怕是一行代码的改动、一个索引的添加。即使是大规模代码的重新组织重构,没有影响到系统质量属性就属于详细设计的范畴。 ps 系统质量属性:可用性、安全性、扩展性、平均响应时间、数据一致性等,每个系统由于所处业务场景不同,所关注的质量属性都是不同的。

3.细谈架构设计的具体过程

像前面提到的,一个好的架构设计是在合适时机出发,对于真正需要做架构设计的内容进行设计,并且能在合适的时机停止开启下一个阶段的工作。 当我们面对一个系统的设计诉求或者有些问题亟待解决 面临重构时:

3.1具体过程

1.甄别架构设计的时机和内容,首先判断是否需要进行架构设计,没必要的话直接进入系统分析或者详细设计阶段,需要的话继续。

2.要对这个系统要落地的功能或者要解决的问题进行剖析,比如”要实现的功能是什么“、”核心要解决的问题是什么“、”可预期的风险“、”要解决的窘境“,这是可以借鉴一些架构设计的指导思考来进行问题分析及系统架构设计,比如以风险驱动、质量驱动、领域驱动、业务&需求驱动、演进式的设计思路。确定我们分析、设计的整体思路,这里并不是说必须选择某一种,而是把这些知识吸收,看清面临的场景,多方位考虑。

3.根据我们面临的场景及需要解决的核心问题,根据当前现场及未来可预见的发展情况(包括当前公司的大环境),对于系统整体的概貌、系统每个节点处理、节点关系之间的关系处理 选择一些既定的思想或者脑爆一些适合系统的架构思路,比如“事件驱动的架构思路”、“六边形架构”、“分层设计”又或是不需要额外设计的思路。

到这里为止,脑海中应该已经反复的推敲出一个架构的概貌及一些节点的大致处理思路了,另外,系统构建不是炫技,恰到好处解决问题,留足扩展就足够了。

4.根据当前架构的特点,选择合适的架构表达语言,将思路落地,落地的过程反复的推敲,确定最终架构方案。这时可以使用一些系统视图表达,比如结合逻辑视图、开发视图、数据视图、运行视图、物理视图来整体的描述要设计系统的各个视角的样子,多角度描述。

5.进入详细设计阶段

6.迎来新的问题、新的需求时,对于系统进行持续性架构迭代。

3.2举个例子

3.2.1业务需求

要设计一个活动中用来记账的系统,提供用户活动中的“权益发放能力”、”余额维护能力”、“流水查询能力“。 除了常规小型活动,后续可能会在一些大型的营销活动中使用。 需求不是很急,属于日常能力建设。 (经典的三句话需求)

3.2.2需求分析&拆解

我们需要在用户活动中权益发放的基础之上对发放情况进行记账。 功能上:要知道余额的现场、已收、已支、流水,要能发权益。 系统质量上:系统的数据一致性要求较高、会有大流量情况存在。 需求变动上:往往是新增权益的情况,功能相对简单,前期需求可能较多、但后期通常会稳定下来。

3.2.3选择指导一种或者多种设计上的指导思想

这个需求场景中一些系统风险凸显,风险驱动来作为设计时的思路是没问题的。 记账很显然属于一种非常具象的业务领域,领域驱动也是相对合适的,一次就可以设计的相对完整,并且很轻松思考到未来会有哪些需求场景和变化。 需求不是很急,但是业务预期相对较高,实现70%与100%差异不大,所以没必要慢慢演进,一次到位就好。 如果做过类似的系统,并且经验丰富,并且对之前的经验十分满意,因地制宜“经验驱动”也相当合适了。

3.2.4确定内部的结构

对于记账,主要就一个核心的实体:“用户余额”,然后确定一下具体的领域模型,根据现实场景确定这个领域模型的领域服务,根据这些就能设计出底层的数据变化流转和输入输出了。 最大的功能问题解决了,然后当前风险列表排在第二的是,数据一致性,这个貌似直接mysql的本地事务就能解决了。 好了,下一个问题:性能问题。 某些场景会有流量很大的情况,貌似读写的量都不会太小,读写先分离,然后读流量挂一下缓存就解了,上面的binlog写redis构建就挺好,写场景都是用户收热,异步一下、微批处理就可以解,也不麻烦。如果都是些小活动,仅用mysql就搞定了,读写不用分离、缓存也用不到。 整体思路出来了,再check下系统风险列表,没有引入新的问题,但是这个结构如果被后来的开发者破坏就比较麻烦了。 执行下约束,确定下开发架构,在接口层面限制下动作,增加一层适配接入,然后再把领域对象隔离一下,对于不同的场景基于领域对象做一下专用的多态实现,被破坏的风险就小不少了。 其他的问题,外部系统的交互,领域能力就sqpi对外暴露吧,然后一些关联动作就事件驱动来驱动外部其他系统的动作吧。 整体思路有了,开干。

3.2.5要开始表达了

先画一下逻辑视图,确定下概貌,然后稍待描述下领域模型。 确定下具体数据结构,数据流交互情况。 确定下运行视图。 部署架构啥的按照公司规范来就可以,这个也不需要额外处理什么,就省略掉吧,上面3块已经足够表述清楚整体架构了。 然后关于数据一致性再进行可行性论述和分析,确定最终的方案,对于整体架构的影响可以参照CAP、BASE理论及本地事物的特征。 然后关于整个系统再考虑下需要核心关注的点,确立技术监控及业务功能监控。 按照各自的职责进行内部结构拆分,适当画一下类图表达下结构。 可以开始具体的详细设计了。 所以差不多长这样子:(就只放了几个核心的) 领域设计

逻辑架构

运行视图(适当揉了下数据)

4.架构设计的武器库

这一章节来看看,关于架构设计我们的“武器库”应该有哪些储备,让我们更加从容的进行架构设计和分析工作,这部分能力同我们的技术硬实力储备是同等重要的,虽然看起来十分的虚。

4.1什么驱动了架构设计?指导设计的准则或思想

4.1.1领域驱动设计

领域驱动设计是基于一种“围绕某一领域建模”的设计思路,通过一种研发、设计人员都能理解的语言来来确定模型明确边界,以此为核心构建软件服务,整个模型会穿插到软件研发流程中的各个阶段。 整体的指导思想分为:战略设计和战术设计,由战略设计推导出战术设计。 战略设计:域、子域、限界上下文等 战术设计:实体、值对象、领域服务、领域事件、聚合、工厂、资源库等 在这些基础之上围绕我们的软件架构思路可以设计出各样的表现形态,但是围绕领域建设的架构,通常具有边界明确易扩展、架构思路及各节点逻辑清晰易懂、问题拆解更加干净等特点。

4.1.2风险驱动的架构设计

风险驱动的架构设计,是主要以“风险清单”为核心,然后指导我们不断的解决风险、梳理风险。到风险都被合理的解决时,架构设计停止。 风险的定义相对宽泛,主要从一个系统的质量属性出发,比如说可用性、性能、数据一致性、适配性、扩展性等等,我们所承接的功能需求也可以理解为风险的一种,系统将来面临的组织调整导致交接、业务发展不下去也都是风险。 对于风险清单,通常分为三步: 1.识别风险并确定风险的优先级 2.选择并运用一组技术 3.评估风险降低的程度 使用风险驱动为主导的架构设计思路,通常能将最多的精力用于核心问题的解决,效率比较高。并且对于风险的把控更强,能最小成本解决问题。 但是风险驱动的架构设计往往适合经验丰富,或者说对于风险感知能力及整体把控能力更强的架构师。 除指导我们的设计过程以外,风险驱动的架构设计还提出了一些对于架构特点的描述,比如说“架构无关的设计”(不关心架构设计或者自然发展)、“专注于架构的设计”(约定好规范)、“架构提升的设计”(架构约束我们后续的行为)进而保证后续对于风险的应对情况。

4.1.3演进式架构设计

演进是一种能力,任何架构都应该具备的能力,这里的“演进式设计”主要是不做提前量的架构设计,让架构在后续的迭代过程中合理的演变,从而构成真正适合的架构设计。 这种驱动力主要是为了保证在系统构建之后,如何尽可能的避免“架构比特衰减”,然后指导我们如何合理的进行增量变更,并且随着环境的变化,应该如何对于架构进行引导性变更。 最核心的就是“增量”、“引导”,我们落地的时候需要保护架构的一些特征,并且我们要不断的判断环境对架构进行引导,持续构建,不为架构而架构。

4.1.4需求驱动架构设计

这个算是最大的来源了,架构设计最核心的就是落地功能来实现需求,需求是我们架构设计的最大驱动力,一切的设计都是为了更好的支持业务,而需求就是业务的一种具像化场景。

4.1.5质量属性驱动架构设计

一个系统没有大的问题和风险,也能相对较好的支撑业务,但是系统一些质量属性确不太高,问题不大,但是不少,一定程度上影响到系统的使用了。 这时候我们通常会对于系统的一些属性展开分析,比如说:易用性、安全性、交互性、鲁棒性等。然后一些架构升级、架构微调、适用性补丁等也都属于出于质量属性驱动的提升之一。 这些看作是风险驱动也是可以的,这些架构设计思路其实边界并没有很清晰,有些时候都是站在不同的角度来论述架构设计而已。

4.2主流架构

4.2.1单体架构

单体架构是一种最简单的架构思路,功能代码都在一起,一起部署一起运行,适用于一些简单系统,直接打包运行,一个脚本、写个职责单调的服务其实都是可以的。 在很多人印象里就是古老的不合理的架构,其实架构没有什么本质上的好与不好,只有适不适合。很多场景单体架构其实更能高效的解决问题。

4.2.2分布式架构

分布式架构是当前最主流的架构。概念相对宽泛,只要是为分散系统压力,对于服务进行拆分部署运维的,本质上都算的上分布式架构。 而拆分的过程中出于其它考虑,又可以再粗暴的划分一下,比如两大核心的分布式服务架构 微服务 MSA:分散系统能力,按照原子能力职责拆分,更重视拓展、维护成本、敏捷特点等 面向服务架构 SOA:分散系统能力,按照业务服务的角度拆分,通过服务总线考虑系统架构和系统治理。 有些观点也认为其实微服务属于SOA的一种变形,粒度和侧重点不同而已,不用纠结,大致了解是怎么回事儿和各自的特点就足够了,概念和边界都是认定的,使用的时候这些边界也根本不妨碍我们的认知。

4.2.3无服务器架构

serverlees 最近这十年比较火的一个概念,被称为无服务架构,感觉翻译成叫做“无服务器架构”更为合适。 这里并不是去分布式,而是让架构设计者根本不用关心服务分布式部署相关的事宜,面对能力(function)、后端设施(backend)进行开发和设计。 目前还没有一个比较权威的官方定义,就目前能力来看适用的场景非常局限,我现在也不确定把它摆着这个主流架构的位置是否合理。

4.3常见架构设计的思想

这里来看一下常见的架构设计思路,可以结合来用,不同的场景选择不同的思路。

4.3.1烟囱式架构

主要是指一种完全隔离的架构设计思路,系统完全不和外界交互,自己能把能力实现即可,所以又被称为信息孤岛,一个又一个的信息孤岛,互相之间互不交互,但是这种场景通常来说是不存在的。

4.3.2微内核架构

微内核架构通常用于处理那种 系统都相对对立,但是系统之间有共享,但是共享的内容相对比较薄,这些能力或者服务通常以核心的形式存在,所有的服务依赖于他们,比如说一些账户能力、权限管控能力,然后业务系统围绕这部分能力进行业务的能力的构建。

4.3.3分层架构

分层架构主要是对于一个系统内部结构的描述,这应该是大家最常见的模式了,我们按照能力职责对于系统、服务、能力又或者代码进行分层管理,那一个web项目来说,大致就分成了:展示层、业务层、持久层。按照我们系统的能力又大致分为:接入层、业务层、数据访问层。 好处是每层都仅需要关注自己的职责就好,定义好输入输出,能力维护相对清晰单纯、好维护、伸缩性好、易测试、好运维。

4.3.4六边形架构

六边形架构本质上也属于一种分层架构,只不过分层的是按照内部和外部来分,内部内聚了服务的业务逻辑,外部表示服务的驱动逻辑、基础设施等,内部通过端口和外部进行通信,端口能够对应多个外部系统,不同的场景使用不同的适配器,所以六边形架构又被称为端口-适配器架构。 重点主要体现在:关注点(业务逻辑收敛,考虑业务功能的稳定,使用的不确定性)、外部可替换、依赖倒置等。

4.3.5SOA架构

SOA即前面提到的面向服务架构,它根据需求通过网络对松散耦合的粗粒度应用组件进行分布式部署、组合和使用,服务之间通过简单但又精确的接口进行通信。 SOA服务主要是有以下几个基本特征: 可从企业外部访问、随时可用、粗粒度的服务接口分级、松散耦合、可重用的服务、服务接口设计管理、标准化的服务接口、支持各种消息模式、精确定义的服务契约等。 对于业务合作友好,出于商业流程协作等目的形成的一种架构技术。

4.3.6微服务架构

微服务架构是一种通过多个原子能力的“小型服务”来组成更上层单个应用的架构方式,然后各个原子服务各自维护,可以使用不同的语言、不同的存储、也可能是同机部署也可能不是。 主要专注于单一职责、语言无关、细粒度服务的构建(这些算是和SOA的差异),这里借鉴一段话:”微服务是一种通过多个小型服务组合来构建单个应用的架构风格,这些服务围绕业务能力而非特定的技术标准来构建。各个服务可以采用不同的编程语言,不同的数据存储技术,运行在不同的进程之中。服务采取轻量级的通信机制和自动化的部署机制实现通信与运维。“ -- 《Microservices: A Definition of This New Architectural Term》

4.3.7事件驱动的架构

事件驱动顾名思义以事件为核心,通过规范事件、通过处理事件生成、发现和处理来进行节点驱动的架构设计思路。 核心思路就是在系统间建立一套事件队列管道 ,然后系统外部的动作以事件的形式放到管道中,然后各个子系统从管道中获取自己感兴趣的消息,可以对消息进行处理也可以对消息进行附赠信息。 这样每个子系统都是高度解耦的,一定程度上,耦合程度从m*n*c下降到了m n,通常使用事件驱动的架构已经到了相当复杂的场景。

4.4一些定律及经验

4.4.1 康威定律

康威定律核心的原文是”设计系统的架构受制于产生这些设计的组的沟通架构“,跟人感觉粗暴点来理解就是,组织结构决定系统架构。 在我们进行架构设计时必定要人认真考虑组织结构对于系统架构的可能产生的影响,顺从这些沟通合作关系构建出来的系统往往是跟好的,这样设计并表达出来的系统往往是更有助于合作的。 至于为什么要关注这个,可以参照《人月神话》中的一个沟通成本公式:(n-1)/2,n为人数,随着人员的上涨对于系统的沟通成本维护成本是非常之大的。 遵从康威定律设计出的系统,往往子系统能更好的内聚、协作沟通成本更低、更有助于系统的演进,对于团队而言更有助于稳定。

4.4.2 SOLID面向对象原则

本着高内聚低耦合的目标进行抽象设计。

单一职责原则

某一个类仅有一个职责,修改某个类的理由应该只有一个理由,如果涉及多个则需要拆分,这里描述的是类,其实对于一个实体,比如一个类、一个服务、一个系统 都应该只具有一个职责。

开闭原则

面向新增开放,面向修改关闭,好的系统设计的应该以增量的方式更新,而非在原有的基础上进行修改变更。

里氏替换原则

满足is-a的关系,一个超类的使用场景,所有超类的子类的实例均能被正常使用,并可随意替换。

依赖倒置原则

高层模块实现不能直接依赖于底层模块实现,应该都依赖于抽象。

接口分离

不能强迫使用方依赖他们根本不需要的能力,这就要求我们能力设计的时候尽可能的职责单一并且专用,大而全的能力是不太合适的。

4.4.3AKF架构准则

这块是在《架构即未来》一书中提炼出来的原则。

N 1 设计

永远不要少于两个,通常为3个。比如无状态服务的部署、机房电力供给至少有两个。

回滚设计

要能够应对犯错和回退场景,应该保留历史版本,并能立马回到历史版本。

禁用设计

对于服务和能力,尤其是新服务和新能力,我们要能灵活开关。

监控设计

对系统和服务的死活、存活情况绝对是要知道的,必要性是很强的,如果一个系统可以没有监控,就应该考虑可以没有这个系统。监控的设计必须是架构设计中的一个重要组成部分。

具备多活数据中心

如果当前阶段允许、ROI划得来,那就尝试摆脱一下单数据中心,体验、容灾都有很好的提升。

使用成熟的技术

对于架构的构建要使用已经经过考验的技术和方案,避免吃螃蟹,不要用beta版本。

异步设计

能异步就异步,成本不高,体验还好。

无状态系统

我们的系统一定要尽可能无状态,有状态的系统扩展、部署、数据同步都存在大量的问题,如果避免不了状态型功能,那就把他们尽可能收到一起。

水平扩展而非垂直升级

永远不要依赖于单点性能的提升,比如更快的数据库、更快的机器、更牛逼的系统。

走一想三

设计一步,再往下想两步,要让自己尽可能有对于未来发展的洞察能力,这就要求我们需要熟悉业务、熟悉当前趋势、合理预期未来。

非核心则购买

这块指的是不要重复造轮子,有现成的,请直接使用。

使用商品化硬件
小步迭代、快试错
故障隔离

系统设计要有断路机制,也要尽可能建设隔离机制,故障单元不能影响其他单元的工作,分区容错十分重要。

自动化

一个高效迭代的系统,非常重要的一点就是自动化。一切人可以重复的过程,都可以自动化。

4.4.4CAP理论

一个分布式系统最多同时满足一致性、可用性、分区容忍性这三项中的两项,要根据面临的场景进行合理选择。

一致性

计算机系统中的数据变化,应该同现实世界数据变化预期一致,不会出现节点数据不一致的情况。

可用性

正常提供服务,服务没挂、服务没抖

分区容错

部分节点或者网络分区故障时,仍能提供可用和一致的服务。

4.4.5BASE理论

BASE指基本可用、软状态和最终一致性。

基本可用

出现故障时,允许牺牲部分可用性,来保证核心功能,比如转账后的短信通知不能影响转账的成功。

软状态

允许系统存在中间状态,允许节点之间发生不一致的情况,这种情况不会影响系统的整体可用性。比如转账成功,扣款方已经成功,收款方因合并入账抖动还未入账。

最终一致性

软状态的都是临时的,在经过一定时间之后,某些节点的数据最终会走向一致,比如转账成功,最终双方预期都是正确的,并且都会收到通知。

4.4.6GRASP职责分配
信息专家

职责的分配要按照信息能力来确定,也就是说我们要把一个职责发配给具有足够信息处理的专家。

创建者

对象A能够赋予职责给B,至少B包含A/B记录A的实例/B密切的使用A/B拥有A的初始化数据。

低耦合

在职责赋予的过程中,要让对象间的影响和依赖尽可能的小,最大化重用。

高内聚

对象的职责尽可能的单调,职责相关的逻辑要收敛到对象内部,不能外泄。

控制器

一个职责的执行,得有一个控制器来规范上层的使用及协调服务内部的运作。

多态

将某一类职责分给一个抽象下的不同实现,在运行时动态选择,开发时灵活插拔。

间接

两个或多个对象存在交互时,尽可能创建一个中间类或者中间系统,来处理交互关系,避免耦合。

纯虚构

可以利用虚构来创建现实中压根不存在的实体,赋予这个实体职责时应该是符合大众认知的。

封装变化

对于变化的影响范围要尽可能的变小,一个对象内部的逻辑变更不应该影响到其他实现,更不应该影响到上游使用及整个服务或系统,最好在内部消化掉。

4.5架构设计的表达语言

这块儿主要是各大公司,或者一些研究院整理出来的大家相对容易接受的架构表达语言,大家看看就好,最常用的还是五视图模式,讲真,感觉只要能把思路描述清楚,别人能看懂就足够了,核心的精力还是放到对于问题的分析、架构的思考上比较划得来。

4.5.1多视图模式

多视图模式常见的有:RUP的4 1视图、SEI3视图、西门子4视图

4.5.2五视图模式

逻辑视图、开发视图、数据视图、运行视图、物理视图

5.是时候思考一下了

这块后续单独出两篇剖析一下对于常见经典系统的剖析,本篇已经够长了,先到这里了,感谢大家。

0 人点赞