软件架构的出现是为了解决系统规模增加后出现了系统耦合严重,开发效率低,逻辑复杂,扩展困难等问题。所以架构设计是为了解决软件复杂度而存在的,所以架构设计的目地是识别出需求的复杂点然后针对性的解决。系统设计时需要考虑:性能,系统的可扩展性,安全性,高可用,成本。
架构师的工作并不神秘,成熟的架构师需要对已经存在的技术非常熟悉,对已经经过验证的架构模式烂熟于心,然后根据自己对业务的理解,挑选合适的架构模式进行组合,再对组合后的方案进行修改和调整。
系统复杂性来源
- 高性能:为了提高系统每秒处理业务的能力,我们会采用集群与将业务进行分解为子系统,系统间通过网络进行调用。这都增加了系统的复杂度。
- 高可用:解决系统的高可用采用的都是冗余设备来进行处理。而冗余的设备需要考虑数据的一致性,状态的决策(什么情况下将机器下线)。
- 可扩展性:业务需求本身具有不可预测性,为了实现业务上的灵活性往往需要抽像出更多的层来应用业务需求的变化。
- 低成本:与增加机器实现系统的高性能与高可用冲突。
- 系统安全性。
架构设计的原则
真正优秀的架构都是在企业当前人力、条件、业务等各种约束下设计出来的,能够合理地将资源整合在一起并发挥出最大功效,并且能够快速落地。有下面几个原则:
- 合适原则:合适优于业界领先。
- 简单原则:“简单优于复杂。
- 演化原则宣言:“演化优于一步到位”。
架构设计的步骤
- 识别系统的复杂性:将主要的复杂度问题列出来,然后根据业务、技术、团队等综合情况进行排序,优先解决当前面临的最主要的复杂度问题。可以采用排查法来识别系统的复杂性。其中1:高性能需要有TPS/QPS的数据支撑,分别考虑读写性能。2:高可用需要根据业务的影响程度进行判断,假设系统宕机后的影响有多大,包括(读,写,存储)3:可扩展性需要结合业务的特点进行分析。4:安全性同样是需要根据业务特点进行判断。
- 设计备选方案:备选方案只关注技术选型,而不是技术细节,技术选型的差异化要比较明显。
- 评估和选择备选方案:通过对方案质量属性点进行360度环评,质量属性点包括:性能,可用性,硬件成本,项目投入,复杂度,安全性,可扩展性。
- 详细方案设计:详细方案设计就是将方案涉及的关键技术细节给确定下来。
架构模式
在具体的实践过程中,为了更快、更好地设计出优秀的架构需要掌握业界已经成熟的各种架构模式。大部分情况下,我们做架构设计主要都是基于已有的成熟模式,结合业务和团队的具体情况,进行一定的优化或者调整;即使少部分情况我们需要进行较大的创新,前提也是需要对已有的各种架构模式和技术非常熟悉。
高性能架构模式
-
数据库读写分离
读写分离的基本原理是将数据库读写操作分散到不同的节点上,下面是其基本架构图
数据采用读写分离后需要考虑复制延迟的情况。解决办法有:
- 写操作后的读操作指定发给数据库主服务器。
- 读从机失败后再读一次主机。
- 关键业务读写操作全部指向主机,非关键业务采用读写分离。
读写分离后还需要考虑分配机制 将读写操作区分开来,然后访问不同的数据库服务器,一般有两种方式:
- 程序代码封装
- 中间件封装。
- 数据库分库分表
当数据量达到千万甚至上亿条的时候,单台数据库服务器的存储能力会成为系统的瓶颈,分库分表用于分散存储压力同时也解决了访问压力。分库可以分为:
1:业务分库,将不同的业务数据存储到不同的数据库服务器中。
2:将相同的表数据分散存储在不同的数据库服务器中,用于处理数据量特别大的表。
分表拆分同样有两种方式,分别为:
1:垂直拆分,将一张表分成两张表或者多张表,不同的表包含不同的列。
2:水平拆分,将一张表的记录分成多张表,不同的表包含不同的记录数。
分库分表需要考虑:
1:数据路由规则,是采用范围路由还是hash路由的方式。
2:数据库的事务,由于分库后数据不在同一个库里,原来数据库提供的事务不起作用了。
3:在单库里表的join,count,order by 等操作都会变的比单库单表复杂的多。
在实现方式上,分库分表与读写分享一样。都有两种方式,程序封装或者采用中间件封装。 - 高性能的NoSQL
1:K-V 存储:解决关系数据库无法存储数据结构的问题,以 Redis 为代表。
2:文档数据库:解决关系数据库强 schema 约束的问题,以 MongoDB 为代表。
3:列式数据库:解决关系数据库大数据场景下的 I/O 问题,以 HBase 为代表。
4:全文搜索引擎:解决关系数据库的全文搜索性能问题,以 Elasticsearch 为代表。 - 缓存
能够应用缓存的场景为:1:需要经过复杂运算后得出的数据。2:读多写少的数据,比如微博。的架构设计阶段,缓存需要考虑如下问题:
1:缓存数据不存在,造成缓存穿透,最终拖跨存储服务器(可以对不存在的数据存一个默认值)。
2:缓存数据生成需要耗费太多的资源,有可能拖跨整个系统。
3:缓存失效,同时有大量请求失效的缓存数据造成缓存雪崩。解决办法可有采用更新锁或者后台更新的方式。
4:热点数据的缓存,需要对热点数据保存多个缓存副本,这样可以将请求分散在不同的缓存服务器上。对于多个副本的缓存,内容一样,可以设置成不同的过期时间,这样可以避免引起缓存雪崩。 -
高性能负载均衡(任务分配器)
负载均衡可以分为DNS负载均衡,硬件负载均衡器与软件负载均衡。其中DNS用于处理地理位置上的负载均衡。硬件负载均衡(F5,A10)具有功能强大,性能强悍但是伤钱但扩展性差,而软件负载均衡(Nginx, LVS)具有简单,便宜且扩展性容易但是性能一般。在实际的系统中可以综合使用三种负载均衡器,以便发挥他们各自的优点,如下图所示:
负载均衡算法有以下几类:
1:轮询:将任务平均分配到服务器上,具有实现简单的特点。
2:加权轮询:可以根据硬件机器的特性给不同的服务器配置不同的权重。
3:负载最低优先:需要感知服务器的性能指标,可以充分利用用服务器的性能便增加了分配算法的复杂度。
4:性能最优优先:优先将任务分配给处理速度最快的服务器,通过这种方式达到最快响应。需要收集服务器返回任务的时间。
5:Hash算法,根据请求的某些关键信息进行Hash运算,将相同的hash值分配到同一台服务器,可以满足特定的业务需求。
高可用架构模式
高可用的本质是通过冗余的机器来做到服务的高可用。
-
存储高可用架构
1:主备复制:这种方式的优点是简单,备机只做为数据备份,不提供服务,主机出现问题后需要人工参与的方式进行恢复。架构图如下:
存储高可用需要考虑复制延迟和中断导致的数据不一致问题。常见的高可用存储架构有主备、主从、主主。
2:主从复制:这种方式从机会提供相应的读能力,可以避免硬件的浪费。缺点是客户端需要感知到主从关系统,将写操作分配给主机,将读操作分配给备机,增加了复杂性。同时会有数据延时,某些业务场景可能有问题。架构图如下:
3:主主复制:这种模式两台机器都做为主机,客户端的请求可以发送到任意一台机器。这种方式的架构只有对特定业务情况下才会使用,因为两台主机之间需要双向复制,比如日志数据可以采用这种方式处理而库存数据就不能采用这种方式进行架构设计。架构图如下:
无论是主从模式还是主备模式都需要人工参与的方式进行数据恢复。而人工参与的不确定性就增加了,比如主机在凌晨挂了。双机自动切换能够在无需人工参与的情况下或只需人工半参与的情况下快速的进行双机切换。自动切换需要考虑以下三个点:
1:如何判断主机是不可用状态。
2:切换时机与切换策略是什么,主机恢复后是继续做主机还是做从机。
3:如何解决数据冲突。
根据状态传递渠道的不同,常见的主备(主从)切换架构有三种形式:互连式、中介式和模拟式。
1)互连式:主备机直接建立状态传递的渠道,缺点是当传递状态的渠道本身就有问题,可能出现双主的情况。架构图请注意与主备复制架构对比
2)中介式:主备两者之外引入第三方中介,主备机之间不直接连接,而都去连接中介,并且通过中介来传递状态信息,其架构图如下:
3)模拟式:主备机之间并不传递任何状态数据,而是备机模拟成一个客户端,向主机发起模拟的读写操作,根据读写操作的响应情况来判断主机的状态。其基本架构如下:
4:数据分散集群
数据分散集群用于解决单机存储不下所有数据的业务,通过多台机器形成集群进行分散存储。其中分散集群需要考虑数据存储的均衡性:不能有一部分机器存储了大部分的数据;同时还需要考虑数据的容错性,一旦有一台机器损坏了,如何保证数据不丢失;再有就是集群的伸缩能力了,当集群的机器不够,如何保证数据数据的迁移与新机器的平衡性。 -
计算高可用架构
计算高可用相比存储高可用来说就简单的多了,作为任务分配器(负载均衡器)需要知道有那些机器可有用来做计算与相应的任务分配策略。计算高可能同样可以分为主备,主从,集群。其中集群模式用的相对来说是比较多的,主备对硬件有一定的浪费。集群模式架构如下图所示: