推荐系统上线后,饿了么么的业务线越来越多——外卖、跑腿、超市、药品、鲜花...系统越来越庞大。小明发现每加一个业务线就得改一堆模块,改了东墙塌西墙。他意识到:单个模块设计再好也没用,如果整体架构没有"布局谋篇"。
就像写作文——字词句再优美,段落组织不好,整篇文章就是一盘散沙。软件架构设计就是软件系统的"布局谋篇",是架构设计师的真正主场。
🏛️ 9.1 软件架构概述:从子程序到中间件
软件抽象的发展史:
- 1960s 子程序年代:程序间调用为连接关系
- 1970s 模块化年代:数据流分析、E-R图、信息隐藏,抽象到模块级
- 1980s 面向对象年代:继承增加了新元素连接关系
- 1990s 框架年代:可互换的黑箱对象相互嵌入
- 当前 中间件和IT架构:可购买可复用元素构建系统
软件架构定义(采用定义1):系统的一个(或多个)结构,由软件元素、元素的外部可见属性、元素之间的关系组成。关键理解:架构是抽象,强调"外部可见"属性,内部实现细节不属于架构。
架构重要性四方面:① 项目干系人交流的平台(共同语言)② 早期设计决策(约束实现、影响质量属性、预测质量、维护依据、原型开发、成本进度估计)③ 较高层面上实现复用(产品线共享架构)④ 对开发的指导与规范。
架构5种模型:结构模型(最直观,构件+连接件)、框架模型(侧重整体结构)、动态模型(系统大颗粒行为)、过程模型(构造步骤)、功能模型(功能构件分层)。最常用=结构模型和动态模型。
"4+1"视图模型(Kruchten 1995):
| 视图 | 关注 | 谁看 |
|---|---|---|
| 逻辑视图 | 功能需求,对象模型 | 最终用户 |
| 开发视图 | 模块组织管理 | 程序员 |
| 进程视图 | 运行特性,并发/分布/容错 | 系统集成人员 |
| 物理视图 | 软件到硬件映射 | 系统工程师 |
| 场景(+) | 重要活动抽象,串联四视图 | 所有人 |
逻辑+开发视图=静态结构,进程+物理视图=动态结构。
⚖️ 9.2 软件质量属性:架构师关注的焦点
架构设计师重点关注质量属性而非功能属性——因为不同结构可实现同样功能,功能性独立于结构。架构设计师面临的是对结构选择的决策。
运行期质量属性:性能、安全性、易用性、可伸缩性、互操作性、可靠性、持续可用性、鲁棒性(健壮性/容错性)。
开发期质量属性:易理解性、可扩展性(灵活性)、可重用性、可测试性、可维护性、可移植性。
6个质量属性+战术(战术=实现质量属性的基本设计决策,战术集合=架构策略):
① 可用性——阻止错误发展成故障。战术三类:
- 🔍 错误检测:命令/响应、心跳(计时器)、异常
- 🔄 错误恢复:表决、主动冗余(热备份)、被动冗余(暖重启)、备件、状态再同步、检查点/回滚
- 🛡️ 错误预防:从服务中删除、事务、进程监视器
② 可修改性——局部化修改、防止连锁反应、推迟绑定时间。战术:维持语义一致性、预期变更、泛化、限制选择;信息隐藏、维持现有接口、限制通信路径、仲裁者使用;运行时注册、配置文件、多态、构件更换。
③ 性能——影响响应时间=资源消耗+闭锁时间。战术:减少资源需求(提高效率/减少计算/控制事件率/限制执行时间)、资源管理(引入并发/多副本/增加资源)、资源仲裁(FIFO/固定优先级/动态优先级/静态调度)。
④ 安全性——抵抗攻击(身份验证/授权/加密/完整性/限制暴露/限制访问)、检测攻击(入侵检测)、从攻击中恢复(恢复+识别攻击者)。
⑤ 可测试性——输入/输出(记录回放/接口与实现分离/优化访问线路) + 内部监控。
⑥ 易用性——运行时(任务模型/用户模型/系统模型) + 设计时(分离用户接口) + 支持用户主动操作(取消/撤销/聚合/多视图)。
🎨 9.3 软件架构风格:5大家族
架构风格=描述某一特定应用领域中系统组织方式的惯用模式。定义词汇表(构件/连接件类型)+约束(如何组合)。Garlan和Shaw分类5大家族:
| 风格家族 | 子风格 | 核心思想 |
|---|---|---|
| 📊 数据流 | 批处理序列、管道/过滤器 | 数据像流水线一样流动加工 |
| 📞 调用/返回 | 主程序/子程序、OO、层次结构 | 分而治之,调用返回 |
| 🔗 独立构件 | 进程通信、事件系统 | 构件独立不直接通信 |
| 🖥️ 虚拟机 | 解释器、规则为中心 | 构建运行环境解析自定义语言 |
| 🏪 仓库 | 数据库、超文本、黑板 | 中央数据结构+独立构件操作 |
管道/过滤器:每个构件读输入→内部处理→产生输出,构件=过滤器,连接=管道。UNIX shell和编译器是经典例子。优点:隐蔽性好、支持重用、可并行。缺点:不适合交互应用、解析合成数据增加复杂性。批处理vs管道过滤器:批处理数据整体传递、高潜伏性、无交互;管道过滤器递增、延迟小、可交互。
层次结构:每层为上层服务并作为下层客户。优点:抽象递增设计、功能改变最多影响相邻两层、支持重用。缺点:不是所有系统都能分层、找到合适层次抽象难。最广应用=分层通信协议。
事件系统(隐式调用):构件不直接调用,而是触发/广播事件,其他构件在事件中注册。触发者不知道谁响应。优点:强大重用支持、改进方便。缺点:放弃计算控制、数据交换问题、正确性推理困难。
黑板系统三部分:知识源(不直接通信,通过黑板交互)、黑板数据结构(按层次组织解决问题数据)、控制(黑板状态驱动)。传统应用=信号处理(语音/模式识别)。
🏗️ 9.4 层次架构:C/S、B/S、MVC、MVP
二层C/S缺点:单一服务器难以扩展到广域网、服务器负荷太重、数据安全性不好(客户端可直接访问数据库)。
三层C/S:表示层(用户接口) + 功能层(业务处理逻辑) + 数据层(DBMS)。关键=表示层和功能层分离成独立程序。功能层和数据层放不同服务器=灵活性最高,系统越大优势越显著。
B/S(浏览器/服务器):三层C/S的实现方式,浏览器→Web服务器→数据库服务器。零客户端——只需浏览器。优势:维护全在服务端、异种机异种网开放性。不足:缺乏动态页面支持、响应速度低于C/S、数据动态交互性差、不利于OLTP。
MVC(1979年Reenskau提出):
- 📊 Model:应用状态和业务功能的封装(数据+行为),接受Controller请求完成业务处理,状态改变时通知View
- 👁️ View:可视化界面呈现+捕捉用户交互操作
- 🎮 Controller:完成UI逻辑。View捕获操作→转发Controller→需要业务调用Controller调Model→控制原View或创建新View响应
MVC解决的核心问题=关注点分离——业务逻辑、UI处理逻辑、界面呈现三者分离。避免自治视图的问题:业务逻辑丧失重用、不同稳定性元素混在一起、UI组件难测试。
MVP(Model-View-Presenter):从MVC演变,关键区别=View不直接使用Model。所有交互在Presenter内部。Presenter依赖抽象化View(IView接口),使UI处理逻辑易于测试。优点:模型视图完全分离、一个Presenter可用于多视图、脱离UI测试逻辑。缺点:View和Presenter交互频繁,过度渲染会与特定视图联系过紧。
🌐 9.5 面向服务的架构(SOA)与微服务
SOA核心:所有功能定义为独立的服务,通过交互协调完成业务逻辑,服务间松散耦合。
5个设计原则:明确定义的接口、自包含和模块化、粗粒度(消息交互而非RPC,频度低)、松耦合、互操作性/兼容/策略声明。
关键技术(以XML为基础):
- 📋 UDDI:服务发布、查找、定位。三部分:数据模型(XML Schema)、API(基于SOAP)、注册服务
- 📄 WSDL:服务描述语言。服务接口定义(抽象可重用) + 服务实现定义(端口/部署细节)
- ✉️ SOAP:消息传输规范。用XML格式化消息,HTTP承载。四部分:封装(Envelope)、编码规则、RPC表示、绑定。消息三部分:Envelope(必须)、Header(可选)、Body(必须)
- 🔄 REST:只使用HTTP和XML。所有事物抽象为资源,每资源唯一标识,通用接口操作,操作不改变资源标识,无状态。支持POST/GET/PUT/DELETE
SOA三种实现方法:
- 🔌 Web Service:三角色(提供者/请求者/注册中心),三操作(发布/查找/绑定)。6层架构:传输层→通信协议层→描述层→服务层→业务流程层→服务注册层
- 📖 服务注册表:设计时使用,服务注册/服务位置/服务绑定
- 🚌 ESB(企业服务总线):标准化通信基础结构。消除服务提供者与请求者间直接连接,进一步解耦。功能:异构环境支持、非侵入式服务接口、缓冲器、服务代理/协议转换、消息转换路由、安全认证
微服务——很小的服务,属于SOA的一种。像活字印刷术——每个服务独立组件,通过编排组合使用。核心特点:小且专注做一件事、轻量级通信(RESTful)、松耦合、独立部署。
微服务7大优势:技术异构性(每服务可选技术)、弹性(局部故障不整体崩)、扩展(针对单服务)、简化部署、与组织结构匹配、可组合性、可替代性优化。
微服务6大挑战:分布式复杂度、运维成本、部署自动化、DevOps与组织结构、服务间依赖测试、服务间依赖管理。
微服务 vs SOA主要差异:微服务更细粒度、更独立部署、更强调去中心化数据管理、更轻量通信。
📐 9.6 架构设计:ADD方法
属性驱动设计法(ADD)——将分解建立在质量属性之上。输入=功能需求(用例)+限制条件+质量需求(质量场景)。
ADD步骤:① 选择要分解的模块 → ② 选择架构驱动因素(优先级最高的需求) → ③ 选择满足驱动因素的架构模式(基于战术) → ④ 实例化模块并分配功能(多视图表示) → ⑤ 定义子模块接口 → ⑥ 验证用例和质量场景 → ⑦ 对需进一步分解的模块重复。
演变交付生命周期模型两个循环:① 迭代开发架构 ② 在架构基础上迭代开发最终版本。架构由架构驱动因素(少数关键的高优先级业务目标质量需求)塑造。开发骨架系统是第二个循环的第一步。
📝 9.7 软件架构文档化
架构文档使用者=项目关系人。7条编档规则:从读者角度编写、避免不必要重复、避免歧义、使用标准结构、记录基本原理、保持更新但不过频、针对适宜性评审。
视图编档7部分:视图概述、元素目录(元素/关系/接口/行为)、上下文图、可变性指南、架构背景、术语表、其他信息。
架构重构(反向工程之一):系统已存在但架构未知(文档缺失/失效)。四活动迭代:信息提取→数据库构造→视图融合→重构(可视化和交互+模式定义和识别→生成架构文档)。
🔍 9.8 软件架构评估
三种评估方法:
- 📋 基于调查问卷/检查表:利用经验知识,缺点=依赖主观推断
- 🎭 基于场景:SEI提出,分析架构对场景的支持程度。ATAM和SAAM使用
- 📊 基于度量:建立质量属性-度量映射→获取度量信息→推导质量属性。更客观量化
ATAM(架构权衡分析法)——9步:
- ATAM方法表述
- 商业动机表述
- 架构表述
- 架构方法分类
- 生成质量属性效用树(根→质量属性→属性求精→场景(叶),修剪保留≤50个,按重要度和难易度各H/M/L排序)
- 分析架构方法(探查实现场景的架构方法,找风险/无风险/敏感点/权衡点)
- 集体讨论并确定场景优先级(投票)
- 分析架构方法(类似第6步,针对第7步的高优先级场景)
- 结果表述
ATAM产生结果:简洁架构表述、业务目标、场景集合、架构决策到质量需求映射、敏感点和权衡点、有/无风险决策、风险主题。
CBAM(成本效益分析法)——在ATAM上构建,从经济角度建模。8步:整理场景→求精(最坏/当前/期望/最好)→确定优先级(投票)→分配效用→策略-场景-响应级别对应→内插法确定期望效用→计算总收益→按ROI排序选策略。
🧩 9.9 构件及复用
三大商用构件标准:
| 标准 | 组织 | 核心 |
|---|---|---|
| CORBA | OMG | 3层:ORB(软总线)→公共服务→公共设施。CCM构件模型 |
| J2EE | SUN | 支持RMI+IIOP。Servlet/JSP/EJB。EJB=会话Bean+实体Bean |
| DNA 2000 | Microsoft | COM/DCOM/COM+。ASP+COM+Cluster。负载均衡/内存数据库/对象池 |
基于复用开发的组织结构:三类职能部门——构件系统开发部(开发可复用资产)、应用系统项目开发部(复用资产)、支持部门(确认/编目/分发/反馈)。上有复用经理协调,平衡长期目标与近期项目压力。
🏭 9.10 产品线与系统演化
软件产品线=一组共享公共可管理特性集的软件密集型系统,用公共核心资产集成开发。核心资产库包括架构+可剪裁元素+设计方案+文档+测试计划+历史记录等。
可复用资产:需求、架构设计、元素、建模与分析、测试、项目规划、过程/方法/工具、人员、样本系统、缺陷消除。
产品线架构3点特别之处:① 必须考虑一系列明确许可的变化 ② 一定要文档化 ③ 必须提供"产品创建者指南"描述实例化过程。
两种开发模型:前瞻性产品线(自上而下,基于战略决策预先设计) vs 反应性模型(自下而上,根据已证明为共有的元素构建)。
DSSA(特定领域软件架构)——开发产品线的方法。必备特征:严格定义的问题域/解决域、普遍性、合适抽象程度、固定可复用元素。垂直域(特定系统族通用架构) vs 水平域(多系统族共有功能)。活动阶段:领域分析(获得领域模型)→领域设计(获得DSSA)→领域实现(开发组织可复用信息)。中等复杂度项目约50-100个类。
架构演化7步:需求变动归类→制订演化计划→修改/增加/删除构件→更新构件相互作用→构件组装与测试→技术评审→产生演化后架构。
👁️ 9.11 软件架构视图
视图分三类:
- 📦 模块视图类型:主要模块实现单元。4种风格——分解(分配责任)、使用(模块依赖)、分层(虚拟机"允许使用"关系)、泛化(继承关系)
- ⚙️ 构件和连接件(C&C)视图类型:运行时元素。6种风格——管道和过滤器、共享数据、发布-订阅、客户机-服务器、对等连接、通信-进程
- 🗺️ 分配视图类型:软件到环境的映射。3种风格——部署(硬件映射)、实现(文件系统映射)、工作任务(团队映射)
各视图间映射关系:模块视图→C&C视图(模块实现单元映射到运行时构件,但关系复杂——同一代码模块可由多个C&C元素执行,单一构件可执行多模块代码);分配视图=辅助性,将其他视图映射到环境。
饿了么么最终选了微服务+分层架构:每个业务线(外卖/跑腿/超市)是独立微服务,通过RESTful API通信。表示层用B/S,业务逻辑层用微服务,数据层用分库分表。用ATAM评估发现缓存策略是关键权衡点,用4+1视图编档架构。
老王感慨:"架构设计就是选和妥协——不是选最好的,是选最合适的。"但小明知道:架构设计还要落地到代码层面——第10章的设计模式,是架构思想的微观体现。