|
| 1 | +# 02-软件架构权衡-架构特性 |
| 2 | + |
| 3 | +- [00-软件架构权衡-我们为什么以及如何进行权衡?](http://www.javaedge.cn/md/design/00-%E8%BD%AF%E4%BB%B6%E6%9E%B6%E6%9E%84%E6%9D%83%E8%A1%A1-%E6%88%91%E4%BB%AC%E4%B8%BA%E4%BB%80%E4%B9%88%E4%BB%A5%E5%8F%8A%E5%A6%82%E4%BD%95%E8%BF%9B%E8%A1%8C%E6%9D%83%E8%A1%A1%EF%BC%9F.html) |
| 4 | + |
| 5 | +- [01-软件架构权衡-无意识决策的问题](http://www.javaedge.cn/md/design/01-%E8%BD%AF%E4%BB%B6%E6%9E%B6%E6%9E%84%E6%9D%83%E8%A1%A1-%E6%97%A0%E6%84%8F%E8%AF%86%E5%86%B3%E7%AD%96%E7%9A%84%E9%97%AE%E9%A2%98.html) |
| 6 | + |
| 7 | +本系列00文重点讨论啥是架构权衡以及为什么它们很重要。01文,阐述了在软件架构中进行有意识决策过程的必要性。 |
| 8 | + |
| 9 | +本文总结讨论具体的架构特性及为啥理解这些特性对系统至关重要。为做到这点,需理解: |
| 10 | + |
| 11 | +- 啥是架构特性 |
| 12 | +- 选择关注某一特性有时会迫使我们在另一特性上做出权衡 |
| 13 | + |
| 14 | +换句话说,架构特性是指你的系统总体能力的具体方面,这就是架构的“ability”(属性),你很快就会明白为什么。 |
| 15 | + |
| 16 | +以下是一些常见的架构特性。这绝不是一个详尽的列表——只是一些你在设计和构建应用程序时最常遇到的特性,也是我在全职职业生涯和咨询实践中最常见的特性。 |
| 17 | + |
| 18 | +我试图在这里做的是识别特性,指出它在何时最有可能重要,关注它时我们在做什么权衡,以及一些实现该特性的方法。当然,这一切都是非常高层次的,每一个具体点上还有更多内容可以讨论。 |
| 19 | + |
| 20 | +## 1 架构特性 |
| 21 | + |
| 22 | +### 1.1 可审计性(Auditability) |
| 23 | + |
| 24 | +#### 这是啥? |
| 25 | + |
| 26 | +你的系统在运行,事件在流转。用户启动动作并导致事情发生。许多情况下,你希望有一个明确记录,记录这些事情的发生。即你希望记录某个用户在特定时间发起了某个特定的动作,以及该动作的具体情况。 |
| 27 | + |
| 28 | +#### 何时重要? |
| 29 | + |
| 30 | +- 金融等受监管行业 |
| 31 | +- 遵守内部和外部标准(ISO、PCI、SOC、SOX) |
| 32 | +- 出于安全原因,以便你可以识别和调查未经授权的活动 |
| 33 | + |
| 34 | +#### 我们在做啥权衡 |
| 35 | + |
| 36 | +- 系统的复杂性增加 |
| 37 | +- 测试变得更复杂,因为你需要测试审计事件在整个系统中是否被正确记录 |
| 38 | + |
| 39 | +#### 一些实现方式 |
| 40 | + |
| 41 | +- 将审计事件日志与应用日志分开记录。这可以存入数据库、独立日志流、第三方日志聚合系统或另一种永久云存储(例如AWS的S3)。 |
| 42 | +- 制定明确的数据保留和清除策略(金融数据通常需要保留7年)。 |
| 43 | +- 注意成本——大部分数据可以移到较少频繁使用的存储层(冷存储),因为这些数据可能不需要即时检索。 |
| 44 | + |
| 45 | +### 1.2 可观测性(Observability) |
| 46 | + |
| 47 | +**这是什么?** 通常,人们对监控和可观测性之间的区别感到困惑。确实,这两个概念有很多重叠之处,常常被互换使用。一个常见的区分方式是,可观测性指的是在问题发生前识别系统潜在问题的能力。监控是通过日志、指标、跟踪、警报和仪表板查看系统内部发生的情况的能力。监控是实现可观测性的一部分。 |
| 48 | + |
| 49 | +在本文中,我将监控和可观测性都纳入可观测性的范畴。 |
| 50 | + |
| 51 | +#### 何时重要? |
| 52 | + |
| 53 | +任何实际系统都需要一定程度的可观测性。系统越复杂,使用频率越高,可观测机制就越复杂。 |
| 54 | + |
| 55 | +#### 我们在做啥权衡 |
| 56 | + |
| 57 | +- 可观测性增加系统复杂性 |
| 58 | +- 根据可观测性的程度和部署的机制,它也可能影响应用性能 |
| 59 | +- 成本——实现第三方/现成的解决方案 or 内部构建 |
| 60 | +- 可扩展性——可观测系统需要随业务应用扩展。这是一个常见但易被忽视的问题,特别是内部/本地部署的可观测系统 |
| 61 | + |
| 62 | +#### 一些实现方式 |
| 63 | + |
| 64 | +- 使用云原生或第三方APM(应用性能监控)和可观测工具,如Datadog、New Relic、Dynatrace、Honeycomb等 |
| 65 | +- 确保你的应用记录重要信息 |
| 66 | +- 对分布式系统(如微服务)——使用跟踪来保持跨多个系统的请求的可见性 |
| 67 | +- 使用AIOps工具(通常作为上述第三方解决方案的一部分)发现和预测应用行为 |
| 68 | +- 使用OpenTelemetry等开放标准 |
| 69 | +- 确保根据暴露的指标、日志和跟踪配置警报、仪表板、报告。换句话说,确保有可监控和实时操作的有意义的工件。 |
| 70 | + |
| 71 | +### 1.3 可伸缩性/弹性(Scalability/Elasticity) |
| 72 | + |
| 73 | +#### 这是什么? |
| 74 | + |
| 75 | +应用程序支持增加的流量/更多请求或用户的能力。你的系统如何从每秒10次交易(TPS)调整到每秒1000次交易?当用户群从1万增长到100万时会发生什么? |
| 76 | + |
| 77 | +在某些情况下,可伸缩性与弹性的概念可以互换使用,而在其他情况下,这两个概念表示系统的不同方面。准确地说: |
| 78 | + |
| 79 | +- 可伸缩性,指系统整体在较长时间内适应不断增长的负载的能力 |
| 80 | +- 弹性,指系统在特定时间内适应波动负载/流量的能力 |
| 81 | + |
| 82 | +为简化,这里将这两个概念合并。 |
| 83 | + |
| 84 | +#### 何时重要? |
| 85 | + |
| 86 | +- 如果你的应用预期会随着时间的推移而增长,需考虑可扩展性。这适用于大多数部署在生产环境中面向外部用户的系统 |
| 87 | +- 唯一不需要担心可扩展性的情况是,如果你知道系统使用会被限制在一定范围内(例如内部公司应用)。这包括用户群已知非常小且不需要增长的生产应用 |
| 88 | + |
| 89 | +#### 我们在做啥权衡 |
| 90 | + |
| 91 | +- 系统的复杂性增加 |
| 92 | +- **可部署性**变得更困难。处理一个容器的部署要比处理1000个容器容易得多——即使你在使用容器编排框架。 |
| 93 | +- 系统越可扩展,维护**可观测性**就越困难,因为需要观察、跟踪和调查的组件更多。 |
| 94 | +- **可测试性**可能更具挑战性,测试用例需要覆盖应用的分布式特性 |
| 95 | + |
| 96 | +#### 一些实现方式 |
| 97 | + |
| 98 | +- 使用容器编排系统(如Kubernetes)动态调整所需计算水平(容器数量) |
| 99 | +- 利用云环境中的无服务器服务,大部分扩展由云本身负责 |
| 100 | +- 开发应用使其易于扩展。对于“单线程”运行时(如Node),意味着不要阻塞事件循环超过必要时间。对于多线程应用,意味着以最佳方式利用并发(避免死锁,使用非阻塞锁等) |
| 101 | +- 尽可能采用无状态范式,只有在必要时才维护和传递状态。 |
| 102 | +- 通过频繁运行负载、压力和流量测试来识别瓶颈。 |
| 103 | +- 优先扩展横向伸缩而非纵向伸缩 |
| 104 | + |
| 105 | +### 1.4 响应性(Responsiveness) |
| 106 | + |
| 107 | +#### 这是什么? |
| 108 | + |
| 109 | +应用程序的响应能力是其在合理时间内对调用方提供某种响应的能力——即使在应用程序负载显著增加时。换句话说,应用能够处理并响应用户操作,而用户不会感到延迟。这适用于前端和后端应用。 |
| 110 | + |
| 111 | +#### 何时重要? |
| 112 | + |
| 113 | +- 当应用是面向用户时——即实际有用户在等待某些事情发生。 |
| 114 | +- 当你的用例对应用的响应性没有太多容忍度时。例如,某人在银行网站申请贷款,可能对延迟的容忍度更高(因为别无选择),而某人使用服务获取实时股票报价进行日间交易时,容忍度则较低。 |
| 115 | + |
| 116 | +#### 做啥权衡 |
| 117 | + |
| 118 | +- 通常,响应性与可扩展性直接相关。系统越可扩展,响应性越可能越好。然而,应用程序还需要通过设计确保响应性。 |
| 119 | +- 应用程序的内部设计复杂性增加。 |
| 120 | +- **可测试性**变得更复杂,以覆盖可能限制响应性的潜在用例。 |
| 121 | +- 在某些情况下,我们需要在响应性和正确性之间进行权衡。例如,考虑从服务A和服务B获取数据的情况,但服务B不可达。你可能希望返回服务A的数据,而不是无限期等待服务B,因为这会影响系统的响应性。这样做的结果是,你快速返回数据(来自服务A),但由于没有服务B的数据,响应不完整。 |
| 122 | + |
| 123 | +#### 一些实现方式 |
| 124 | + |
| 125 | +- 优雅降级 |
| 126 | +- 断路器 |
| 127 | +- 异步处理 |
| 128 | + |
| 129 | +### 1.5 容错性(Fault Tolerance) |
| 130 | + |
| 131 | +#### 这是什么? |
| 132 | + |
| 133 | +系统在其一个或多个组件发生故障时继续运行的能力。 |
| 134 | + |
| 135 | +容错性也与应用响应性相关。区别在于,响应性更多是确保终端用户体验,而容错性关注的是系统在仅有部分组件工作的情况下,仍能继续运行并产生结果。 |
| 136 | + |
| 137 | +#### 何时重要? |
| 138 | + |
| 139 | +- 任务关键系统 |
| 140 | + |
| 141 | +#### 做啥权衡 |
| 142 | + |
| 143 | +- 就像响应性一样,我们可能会为了系统整体的运行能力而牺牲系统输出的正确性。 |
| 144 | + |
| 145 | +**一些实现方式** |
| 146 | + |
| 147 | +- 优雅降级 |
| 148 | +- 备用副本(计算和存储) |
| 149 | +- 主动/主动或主动/被动部署 |
| 150 | +- 通过架构设计尽量减少关键组件的中断 |
| 151 | +- 监控故障 |
| 152 | +- 使用BASE事务代替ACID事务 |
| 153 | + |
| 154 | +### 1.6 可扩展性(Extensibility) |
| 155 | + |
| 156 | +#### 这是什么? |
| 157 | + |
| 158 | +轻松向系统添加新功能和新集成的能力。 |
| 159 | + |
| 160 | +#### 何时重要? |
| 161 | + |
| 162 | +- 当我们知道应用可能会以意想不到的方式增长和转变时 |
| 163 | +- 当需要添加新组件时 |
| 164 | + |
| 165 | +#### 做什么权衡 |
| 166 | + |
| 167 | +- 当可扩展性是我们的目标时,需要提前进行大量规划,使系统架构易于扩展。 |
| 168 | + |
| 169 | +#### 一些实现方式 |
| 170 | + |
| 171 | +- 模块化软件——创建解耦、高内聚、封装的服务,彼此之间没有依赖(包括代码和架构) |
| 172 | +- 使用开放标准和最佳实践。例如,利用REST或流行的消息队列框架连接微服务 |
| 173 | +- 使用清晰的API契约 |
| 174 | +- 解耦数据存储/数据库,不同的领域数据使用不同的数据存储 |
| 175 | + |
| 176 | +### 1.7 可测试性(Testability) |
| 177 | + |
| 178 | +#### 这是什么? |
| 179 | + |
| 180 | +系统能够轻松地进行手动和自动测试,无论是整体测试还是组件测试。 |
| 181 | + |
| 182 | +#### 何时重要? |
| 183 | + |
| 184 | +- 当我们希望测试覆盖尽可能多的应用流程时 |
| 185 | + |
| 186 | +#### 做什么权衡 |
| 187 | + |
| 188 | +- 系统复杂性增加。尤其是对于分布式系统和异步流程,通常很难甚至不可能实现。 |
| 189 | +- 系统需要设计和架构为允许组件进行隔离测试(包括代码和基础设施)。 |
| 190 | + |
| 191 | +#### 一些实现方式 |
| 192 | + |
| 193 | +- 可测试的代码(即模块化代码,便于单元测试、集成测试) |
| 194 | +- 模块化,使应用功能可以模拟 |
| 195 | +- 确保尽可能多的测试可以自动化 |
| 196 | + |
| 197 | +### 1.8 性能(Performance) |
| 198 | + |
| 199 | +#### 这是什么? |
| 200 | + |
| 201 | +性能与可扩展性和弹性密切相关,之间有很多重叠之处。区别在于,可扩展性和弹性通常指系统适应不断增长的负载的能力。而性能则讨论系统在合理时间内处理所有负载的能力。 |
| 202 | + |
| 203 | +#### 何时重要? |
| 204 | + |
| 205 | +- 大多数面向用户的系统需要足够高的性能以提供积极的用户体验。什么是“足够高的性能”取决于应用的上下文。例如,对于大多数网站来说,超过几秒钟的响应时间通常被认为是糟糕的用户体验(尽管这取决于网站的功能)。 |
| 206 | + |
| 207 | +#### 做什么权衡 |
| 208 | + |
| 209 | +确保性能需要系统在设计上能够预见并消除瓶颈。这还需要在关键点进行细致优化。这意味着需要投入足够的精力来设计系统,持续监控并识别问题区域。 |
| 210 | + |
| 211 | +#### 一些实现方式 |
| 212 | + |
| 213 | +- 非阻塞I/O |
| 214 | +- 异步编程和架构(消息传递、事件流) |
| 215 | +- 解耦系统 |
| 216 | +- 优化长时间运行的过程和CPU密集型操作 |
| 217 | +- 优化应用代码 |
| 218 | +- 选择具有低网络开销的技术(即那些在堆栈较低协议上通信的技术,而不是HTTP) |
| 219 | +- 利用具有已知性能保证的云原生服务(只要这些服务按规定使用) |
0 commit comments