万字前端效率大提速系列 🚀 :十三、系统设计专题

193 阅读6分钟

学习系统设计能很好的提升我们的工程能力,让我们“俯瞰”项目,自顶向下的思考,合理规划和分配资源,帮助我们把控大局。

设计之前

在开始设计前,先确认系统的目标用户、应用场景、约束等条件。需要具体考虑的问题有:

  1. 用户画像
    • 系统的目标用户是怎样的群体
    • 他们是怎样使用系统的
  2. 系统应用场景
    • 使用系统的设备和地点
    • 能够向系统输入什么数据
    • 系统会反馈什么数据
  3. 约束
    • 目标群体的潜在数量有多大
    • 系统能承载的输入输出数据量应该有多大

...

因为讨论的是 Web 应用,那么目标用户主要分 客户端(toC)、企业端(toB)和toG三大类,设备则以PC、手机为主。用户在浏览器上使用应用,通过服务器提供应用服务。

业务模型设计

在设计业务模型之前,开发同学要确保完全理解消化了系统的需求。根据需求理清核心业务模块及它们之间的关系。例如在业务中,哪些用户角色参与使用;使用业务模块时需要保留哪些业务状态;状态改变对其他模块和角色的影响;这些模块具体在代码的实现位置划分等等。

技术架构设计

技术的最终目的是为了保证系统长期平稳运行。因此在设计架构的时候,主要考虑可扩展性、可用性、一致性和稳定性。

  1. 什么时候考虑可扩展性?
    • 当系统处理少量数据比较快,但是大量数据比较慢的时候,系统应该具备可扩展性
    • 在开发程序前考虑可扩展性,成本比较低
    • 应该能够根据数据量,横向扩展、收缩节点资源
  2. 为了达到可扩展性,我们会选择分布式系统,根据 CAP 理论,会有怎样的限制?
    • CAP 理论即:分布式系统中,一致性、可用性、分区容错性只能同时满足两项
    • 为了保证系统的稳定性,我们必须选择分区容错性,那么对于一致性和可用性,我们只能选择其中一项
  3. 可用性、一致性应该怎么选择?
    • 其实分布式系统在大多数时候能够同时满足可用性、一致性,但在出现临界条件时,系统会放弃其中一项
    • 如果系统选择可用性,那么每次响应不一定能获取到最新数据,例如刚在B服务器写入,A服务器仍返回的旧数据
    • 如果系统选择一致性,那么每次都能拿到最新数据,但是可能会返回错误。例如A会向B获取最新写入数据,但B服务器可能因为网络原因不响应。

前端应用如何设计技术架构

根据不同场景的特性,会考虑不一样的架构设计。例如面向客户端,可以通过 CDN 进行内容分发,可以用SSR、资源预加载加速首页渲染;对于业务复杂的管理后台,我们可以用微前端架构剥离出独立的模块,分而治之。

对于单个项目,成熟的项目结构可以是这样的:

- src
  - assets // 静态资源
  - components // 模块化公共组件
  - router // 路由配置
  - store // 全局状态管理
  - views // 页面
  - uitils // 公共方法
  main.js // 应用主入口
package.json // 依赖管理
xx.config.js // 各类插件配置

后端应用如何设计技术架构

一个可扩展的后端应用可以包含以下部分:

请求
↓
负载均衡 (负责根据服务器状态,将流量分配到合适的服务器)
↓
Web服务器集群 (负责处理请求)
↓
缓存、数据库、消息队列 (负责储存应用状态及数据)
↑
离线处理服务器 (负责处理离线应用)
  1. 负载均衡
    • 实现负载均衡通常有三种方式,通过客户端判断应该连接哪个服务器;通过服务器应用判断;通过专业的硬件设备判断
    • 多数时候我们会先从服务器应用开始实现负载均衡
    • 比较简单好用的方法有:使用 nginx 的加权轮询法,根据服务器的配置和负载设置权重,根据权重来分配流量。
    • 在负载均衡的帮助下,我们能够水平扩展服务器集群,即通过添加服务器数量,线性的增加系统的容量
  2. 数据库
    • 关系型数据库可以通过主从复制、主主复制、联合、分片来水平扩展负载量
    • 主从复制即主库可读写,从库只读。主库挂掉应该有从库升为主库的逻辑
    • 主主复制即多个主库都负责读写,会有写入冲突或同步延迟(不建议)
    • 联合,一个应用使用多个数据库,每个负责不同的功能模块
    • 分片,例如按地区将用户信息分到不同的数据库
    • 非关系型数据库,根据不同的应用设置集群如 MongoDB 和 ES
  3. 缓存
    • 用好缓存能大大提升当前服务器资源的利用率,一般分为应用缓存和数据库缓存
    • 应用缓存需要在应用中主动写入、移除缓存,并根据当前缓存返回结果
    • 数据库缓存不需要修改应用程序代码,例如设置了 MySQL 的查询缓存后,MySQL 会将查询结果存在一张哈希表中,以提高查询效率。
  4. 消息队列
    • 消息队列能够高效处理离线任务
    • 离线任务处理服务器独立于应用服务器
    • 消息队列的消费结果既可以异步通知应用,也可以同步挂起在线应用等待通知

一般来说,先设计能够满足少量用户使用的系统,再根据不同的瓶颈,对系统进行缩放设计。同时应该把工作重点放在最需要优化的地方,提升关键点1%的性能比提升非关键点100%的性能,收益要大得多。

参考

The System Design Primer

构建之法