> 文档中心 > 系统架构设计随想

系统架构设计随想

文章目录

  • 1. 系统架构的定义
    • 1.1 抽象是真正的银弹
    • 1.2 生长才能应对复杂
    • 1.3 系统架构的意义
      • 1.3.1 高性能
      • 1.3.2 高可用
      • 1.3.3 效率与成本
    • 1.4 架构设计的原则
    • 1.5 架构设计的流程
  • 2.架构设计模式
    • 2.1 高性能架构
    • 2.2 高可用架构
    • 2.3 可扩展架构

1. 系统架构的定义

系统是指由个体、个体关系、个体职能组成的一个整体。整体大于个体之和,系统能完成个体不能实现的功能。系统可以形而上也可以形而下,太阳系行星运动是一个系统,细胞的内部组成也是一个系统。系统架构定义了系统的结构以及其个体运作和协作的规则。系统内还有有子系统,系统也可以成为上级系统的子系统。

1.1 抽象是真正的银弹

人类的科技的发展史就是一部抽象史,新的认知和科技不断下沉成为工具供上层调用。比如乐高积木就是不断抽象模块,才能搭建乐高城市。再比如数学的函数就是一个典型的抽象,我们的所有事情都可以用函数的视角去看。计算机的语言发展也是逐层抽象,从二进制语言到汇编再到高级语言,都是屏蔽了下层的复杂性,提供上层抽象、可调用的接口。

1.2 生长才能应对复杂

我们有一个误区,认为只有生物体才有生命。其实所有的事物都有生命周期,都有成住坏空的阶段。对于系统的架构也是如此,好的架构都是长出来的,就好像大象的鼻子、河马的眼睛、长颈鹿的脖子、章鱼的腿都是进化出来的,都是从应对复杂的需求而逐步进化、演化而来的。因此,好的架构都是量体裁衣,根据业务需求进化的。

1.3 系统架构的意义

任何事物都有存在的意义和目的,架构的意义不是把事情弄复杂,为了某些虚无的“高性能、高并发、高可用”的标榜而存在。架构是为了解决系统在发展进化过程中出现的复杂问题,即:解决软件系统复杂度带来的问题。对架构师的要求是能够信手拈来,而不是生搬硬套。

1.3.1 高性能

计算机本身就是一个系统,为了提升计算机系统的性能效率,单机系统从批处理操作系统到分时操作系统系统,CPU调度从单进程到多进程到多线程;同时计算机集群技术也急速发展,通过任务分解、任务分摊,将海量的任务和压力分解为可接受的压力。但是天下没有免费的午餐,集群和分布式同样会带来系统的复杂性。

1.3.2 高可用

冗余是增加系统可靠性的终极办法,生物通过染色体的冗余保证遗传性状的可靠,人是2倍体,土豆是四倍体,甚至有24倍体的生物。冗余服务器除了可以带来高可用,也可以通过分担计算压力来提升性能,因此集群和分布式是提升高性能和高可用的利器。但是天下没有免费的午餐,集群和分布式同样会带来系统的复杂性。比如网络延迟带来主从机的数据不一致等问题。因此在分布式领域提出了CAP定理,即“一致性、可用性、分区容错性”三选二,我们常常会通过妥协一致性来满足可用性和分区容错性。在高可用环境中首先要解决的是可用性的检测。因此有以下几种模式。
独裁式,所有的冗余个体都像独裁者汇报自身状态,但是由于单点问题,不可靠。
协商式,常指主备模式,但由于网络存在不确定性存在可靠性的缺陷。
民主式,如ZooKeeper等模式,通过Paxos或ZAB等选举算法来实现群体决策,为了防止“脑裂”,选举过程需要半数以上,因此当故障机器过半系统就失效了。

1.3.3 效率与成本

灵活 效率的提升需要灵活的设计,可扩展性是对系统适度的设计,通过预测系统未来可能新增的变化,将易变的与不变的部分分离,以达到低成本实现软件维护的目的。然而预测是很难的,需要经验、直觉,还好通过许多解决方案和设计模式能够帮助我们通过抽象的方法应对复杂的情况。
成本,所有的生物的习性都是按最低成本设计的,在系统架构设计时必须将金钱成本、时间成本、试错成本、选择成本进行考量。
安全,既要防范系统安全,如XSS 攻击、CSRF 攻击、SQL 注入、Windows 漏洞、密码破解;也要注意架构安全,通过将网络划分成不同的区域,制定出不同区域之间的访问控制策略防止 DDoS 攻击。
规模,当系统软件功能模块、数据量级、硬件服务器数量增多时,复杂度呈指数级上升,大数据技术就是在这种复杂性下诞生的。

1.4 架构设计的原则

经济原则,首先要算账,架构设计无可避免带来研发的难度和工作量,要综合考虑人力资源、技术积累、业务特点。在考虑成功之前,先考虑失败的几种可能,才能减少失败几率。
简单原则,由于软件的需求是动态变化发展的,如果在一开始就把架构设计的复杂,那么未来可以维护的空间反而会很小,出问题的几率会更大。
演化原则,软件的生命周期中唯一不变的就是“变化”,需求总在不断发展变化,再精美的设计也经不住反复折腾,因此不要妄图一劳永逸,要认识到在软件设计时,先满足当下需求,并不断跟随需求演进并完善架构,当业务持续变化时还可能需要进行重构和重写,但架构却逐步稳定沉淀。适应复杂,需要与变化共舞。淘宝的发展就经历了php+mysql,php+oracle,java+ejb+oracle,java+spring+分库分表+缓存历程,到现在淘宝已经使用了自研的中间件和数据库,从无到有的技术架构是与企业业务发展相匹配的。

1.5 架构设计的流程

分析问题,通过对问题的分析,梳理其功能需求与非功能需求。
进行设计,针对问题结合各类解决方案、工具,进行设计,设计多套有差异性的方案提供对比选择。
方案分析,通过方案的特性与优劣分析,选择最适合的方案。
详细设计,细化到各组件的策略。

2.架构设计模式

2.1 高性能架构

  • 数据库集群
    关系数据库处于业务系统存储层的核心地位,高性能架构最关心的部分就是数据库访问读写压力。常用的策略是读写分离分库分表

读写分离将数据库集群分为主从模式,主机负责写,并在写后将数据同步到从机,从机负责读。然而由于同步时间延迟导致读数据不一致,需要在业务场景中将读指向到主服务器。这类业务场景包括:刚写完的读、读从机失败、关键业务的读。读写分离需要依靠框架、中间件的支持,如TDDL、MySQL Proxy,Atlas等。
分库分表是为了解决数据量过大,减轻存储压力的问题。分库是指将不同业务模块的表分布到对应的服务器上,比如用户数据、订单数据、商品数据分别部署到不同的数据库。带来的问题是一、缺少数据库对事务的支持,需要编程实现事务或人工弥补数据不一致。二、跨库的表连接查询无法通过SQL查询实现。三、设备成本、维护成本、开发成本都提高了不少。分表是指对过大的单张表进行垂直切分或水平切分,垂直切分后将热点字段和不常用字段分离,水平切分后会降低单标数据量。分表带来的问题是一、需要定位表,常根据Range、Hash、或者配置方式定位表;二、join、count、order by、group by需要进行二次处理。

  • NoSQL

关系数据库基于行记录,所以按行存储并按行加载,IO压力比较大;另外,关系数据库基于表结构,缺乏对各类数据结构的支撑;最后关系数据库基于schema,在扩展修改时需要DDL导致性能下降;关系数据库对支撑Like的效率比较低。
为解决这些问题,出现了列式数据库、K-V存储的数据库、文档数据库和全文搜索引擎。
Redis 是 K-V 存储的典型代表,它是一款开源(基于 BSD 许可)的高性能 K-V 缓存和存储系统。Redis 的 Value 是具体的数据结构,包括 string、hash、list、set、sorted set、bitmap 和 hyperloglog,所以常常被称为数据结构服务器。,Redis 的事务只能保证隔离性和一致性无法保证原子性和持久性(A 和 D)。
MongoDB 是文档数据库的代表,由于文档数据库没有schema约束,因此新增字段简单,容易存储如json的复杂结构的数据。但是对于join等复杂查询的支持不好。
列式存储如HBASE,有较高的存储压缩比,能节省空间,但是由于各个列是分散存储的,有时也会成为劣势。
全文检索引擎避免了Like检索时的全表扫描,通过倒排索引进行快速检索。Elastcisearch 是分布式的文档存储方式。它能存储和检索复杂的数据结构以实时的方式序列化成为 JSON 文档。

  • 高性能缓存

缓存穿透缓存击穿都是指缓存没有接住访问直接导致访问压力压在了存储系统如数据库上,穿透是指某些请求数据在数据库中本就存在,缓存内更不会有,解决方案是一、过滤非法请求如id<0的情况,二、将此类查询的key也缓存起来,三、布隆过滤器,对于数据库不存在的数据直接返回空。击穿指由于缓存过期这一瞬间,缓存正在生成,但是访问量剧增,导致请求压在数据库上。解决方案是一、对热点数据的过期时间延长,二、使用并发控制,同一时间由一个线程去生成加载缓存。
缓存雪崩,指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。解决方案是一、缓存数据的过期时间设置随机,二、将热点数据均匀分布在不同缓存翻服务器上,三、缓存有效期为永久,让后台线程去定时更新缓存。四、缓存的更新由分布式锁控制同一时间一个线程来更新。五、系统启动时对缓存进行预加载。

  • IO模型

PPC、TPC在访问达到时启动进程或线程去处理,或者预先创建好进程或线程池减轻临时创建的资源消耗,一般BIO都是这类模式。
Reactor 模式,如NIO及AIO模型,更能够充分利用 DMA 特性,让 I/O操作与计算重叠。目前 Windows下通过 IOCP 实现了真正的异步 I/O,而在 Linux 系统下的 AIO 并不完善,因此在 Linux下实现高并发网络编程时都是以 Reactor 模式为主。

  • 负载均衡

高性能集群的复杂性主要体现在需要增加一个任务分配器,以及为任务选择一个合适的任务分配算法。
DNS负载均衡,通过DNS解析定位到不同的IP,简单方便,但是DNS会被客户端缓存不会及时变更,负载均衡的策略无法通过服务集群能力进行感知和动态分配。可以实现地理区域级的负载均衡。硬件负载均衡,F5或A10等设备实现在TCP协议上的转换实现负载。可以实现集群级别的的负载均衡。软件负载均衡,如LVS 基于四层和Nginx基于七层。可以实现机器级的负载均衡。
负载均衡算法,轮询、加权轮询、负载最低优先、性能最优优先、哈希分组。

2.2 高可用架构

  • 不可不知的CAP

Consistency:All nodes see the same data at the same time;A read is guaranteed to return the most recent write for a given client.
Availability,Every request gets a response on success/failure.A non-failing node will return a reasonable response within a reasonable amount of time (no error or timeout).
Partition Tolerance,System continues to work despite message loss or partial failure;The system will continue to function when network partitions occur.
网络本身无法做到 100% 可靠,有可能出故障,因此,分布式系统理论上不可能选择 CA 架构,只能选择 CP 或者 AP 架构。
CP,当发生分区现象后,出现分区现象节点需要返回 Error,提示发生了错误;AP,为了保证可用性,当发生分区现象后可以将旧数据返回或者暂时接住请求。分区期间放弃 C 或者 A,在分区恢复后通过数据同步、数据合并等手段使得系统能够重新达到 CA 的状态。
BASE是对AP架构的补充
Basically Available,保证核心功能基本可用;
Soft State,允许出现数据不一致情况;
Eventual Consistency,通过数据同步机制最终仍要达到数据一致状态。

  • 故障模式与影响分析

故障模式与影响分析即FMEA(Failure mode and effects analysis),是一套分析和思考的方法。通过给出架构图,列举功能点,假设故障模式和故障现象,评估故障影响以及影响损害程度,并针对故障发生概率与原因的分析,来评估风险程度,并提供解决措施和规避措施,如技术手段和管理手段。来改进系统,使得系统能够满足高可用的指标。

  • 从存储角度看

常用架构模式有主备、主从、主主、集群、分区。对于双机而言,有主备、主从、主主模式。
主备,备机只提供备份,不负责读写任务,架构简单,备机没有发挥负载能力,对于故障需要人工进行切换。
主从,主机负责读写,从机负责读,发挥了从机负载能力。
主主,这种模式主机之间需要保证数据逻辑上的隔离与独立,能够允许数据的双向复制。对于序列增长、库存管控等方式不适用,对于临时性、可丢失的数据是适用的。
切换,一般有互连、中介、模拟等几种组织方式。互连式需要通过主从保持状态通信,在通信出问题后从机或备机升级为主机,在通信恢复后主机重新加入,可以恢复为主机或备机。这种模式需要共享IP。模拟式,备机与主机的状态通信基于备机模拟客户机的访问。中介式,通过增加中介,所有的状态都需要实时通知中介,主机如果与中介断开就自动降级为备机,中介通知备机升级为主机,重新连接后原主机上报状态加入为备机或者重新为主机。该模式要求中介本身就要高可用,一般使用ZooKeeper或Keepalived等方式保障仲裁中介的可高用。
集群,数据的增长速度超出了硬件发展速度,依靠集群实现存储能力的增强,在集中存储架构中,适用于主机能够承担存储压力的情况,但是主机需要将数据复制给多台从机,出现这些问题:复制压力以及复制带来从机数据不一致,备机需要检查主机状态、主机故障后的主机选举问题。在分散存储架构中,适用于存储压力巨大,需要数据分摊存储在各台服务器上,需要考虑存储压力的分摊、容错以及可伸缩。
分区,为容灾考虑需要对数据进行分区,有集中式,通过建立备份中心,将各地的数据统一复制到数据备份中心,互备式,每个分区既负责数据的存储又负责数据的备份,独立式,各地均有自己的独立备份中心,但是成本架构。

  • 从任务角度看

任务分配模式,需要考虑任务是由主机还是从机执行,如果执行失败是否需要重新提交与分配。主备模式,主机执行任务,分为冷备和热备,一般适用于频率不高的企业内部业务。主从模式,主从分别执行不同的任务,当主机宕机将从机升级为主机并继续任务分配。集群模式,分为对称和非对称模式,对称模式中任务的属性差不多;非对称模式中主从任务不同,非对称集群相比负载均衡集群,设计复杂度主要体现在两个方面:任务分配策略更加复杂:需要将任务划分为不同类型并分配给不同角色的集群节点。角色分配策略实现比较复杂:例如,可能需要使用 ZAB、Raft 这类复杂的算法来实现 Leader 的选举。以 ZooKeeper 为例:任务分配器:ZooKeeper 中不存在独立的任务分配器节点,每个 Server 都是任务分配器,Follower 收到请求后会进行判断,如果是写请求就转发给 Leader,如果是读请求就自己处理。角色指定:ZooKeeper 通过 ZAB 算法来选举 Leader,当 Leader 故障后,所有的 Follower 节点会暂停读写操作,开始进行选举,直到新的 Leader 选举出来后才继续对 Client 提供服务。

  • 保重点思维

熔断,通过统一的 API 调用层对接口调用检测统计,如果请求响应超时达到阈值则对依赖的接口进行熔断,凡是访问该API的直接返回错误。
降级,只提供系统部分功能,通过舍弃非重点功能保护重点业务功能的正常使用,一般需设置独立的降级系统。
限流,基于请求或资源进行限流。基于请求的方式:如限定游戏服务器的总在线人数,或者限制固定时间内的访问人数。基于资源的限流方式:基于服务器的性能指标。为了提升用户感受可以使用排队策略,依托队列存储请求,协调服务端消费请求。

  • 构建异地多活

为了防灾需要考虑多活,为防止数据延迟过久或者线路中断,金融相关业务一般考虑同城异区;为防止大规模停电或者极端时间可以考虑跨城市多活;为实现国家文化的个性化服务,可以开展跨国多活。异地多活将带来各种各样的业务复杂性,因此需要优先保障核心业务,数据实现最终一致性即可。为实现最终一致性或提升可用性可以考虑几种方式,一、通过数据库的同步机制实现;二、通过效率队列推送同步消息,或者与数据库同步两种相融合;三、程序访问逻辑上进行控制,对多个中心都进行处理;四、对于非核心业务可以进行取舍,如登录session,可以让用户重新登录而不必同步。四、异常处理,保存日志,并将日志独立存储,对于确实出现故障可以进行事后处理与弥补。

2.3 可扩展架构

  • 分层分模块的思维

首先需要建立一个系统的框架,在框架内逐层实现对应的功能,通过各层之间的调用实现了完整的业务逻辑。
分层思维,比如MVC分为三层,这样当存储层或者视图层的技术换了,可以不影响其他层;又比如TCP/IP协议栈分为了四层,上层协议只需要调用下层协议来完成业务。各层之间的差异足够清晰,边界足够明显,就像许多检查结果如B超提示边界清晰病灶的一般是良性的。
分模块思维是指将任务和流程分成相对独立的段落和模块,通过模块的组合分解,提升系统的灵活性。

  • 面向服务的架构 SOA

SOA 最早的应该是亚马逊,得益于杰弗·贝索斯的远见卓识,亚马逊内部的系统都以服务的方式构造,间接地促使了后来的亚马逊云计算技术的出现。服务,将各类IT系统提供的业务功能抽象为服务,服务由ESB连接组织在一起,ESB 需要实现与各种系统间的协议转换、数据转换、透明的动态路由,达到服务松耦合的目的。SOA 解决了传统 IT 系统重复建设和扩展效率低的问题,但在互联网领域并没有得到大量的应用,反倒是在传统企业落地应用。

  • 微服务架构 microservice

微服务与SOA比较相似,都是基于服务的架构。 微服务的服务粒度更细,关注功能点。 微服务架构没有ESB,主要使用 HTTP RESTful 和 RPC协议来实现服务通讯调用。 SOA主要是解决企业IT系统之间的优化和重构,微服务更适合轻量级的WEB互联网系统。
微服务之拆,需要根据人员规模进行拆分,没有足够的人力不要拆的过细;还要根据业务属性拆分,比如核心服务和非核心服务拆开、性能要求高和低的服务岔开。
微服务之熵,一、服务之间的关系复杂;二、需要维护的服务模块和项目增多,开发测试运维部署的工作量急剧加大;三、调用链路太长,消耗性能、定位问题困难;四、服务治理难度较高,需要对服务状态进行故障监测、服务路由、服务注册和发现。
微服务之器,要实现微服务的顺利落地使用,需要有对应的基础设施实现自动化管理,
一、是要有服务发现、路由、容错的功能。服务发现即向服务注册中心注册,注册中心记录了所有的服务节点的配置和状态,每个微服务启动后都需要将自己的信息注册到服务中心,然后由微服务或者 LOAD BALANCER 系统到服务注册表查询可用服务。服务路由即从多个可用服务中选择一个具体的节点,在负载均衡时使用,有多种策略可选择。服务容错,由于参与系统架构的成员增加,导致出错可能性也增大,需要考虑请求重试、流量控制和服务隔离等情况。
二、要有接口框架和网关。接口规范一般使用Http的Rest风格的协议,数据使用Json规范,或者使用RPC统一接口协议。微服务网关对外提供统一的登录、权限、传输加密、请求与流量控制,屏蔽外部系统与微服务之间的复杂性。
三、要有自动化测试、部署、配置中心提升效率。要实现快速迭代就需要实现快速的自动化测试,对微服务的接口测试要求实现自动化测试。还需要对源码版本、资源、部署实现自动化,通过统一的配置中心配置变更和推送同步。
四、服务监控、跟踪、安全等运维支撑。服务监控实现对机器、进程、接口的实时分析预警,一般是独立系统。服务跟踪收集服务请求链路的参数与响应数据。服务安全对服务请求对象进行鉴权。

  • 微内核架构 Micro kernel Architecture

面向功能进行拆分,将业务功能抽象为插件模块,核心系统实现与业务无关的模块加载通信功能,插件模块负责业务功能逻辑。计算机的设计其实就是一种微内核架构,将CPU、内存为核心,其他IO模块都是插件。操作系统的设计也是一种微内核架构,许多系统调用、硬件驱动都是注册到操作系统,由其他的组件进行调用。
核心系统对插件进行管理,核心系统的职责包括插件管理,插件连接、插件通信。插件连接有几种机制,如OSGi、依赖注入、消息、RPC、Http都可以。

彭州一中