《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 sf.gg/a/1190000039969525 「与昊」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

如何打造一个高可用、高性能、易扩展、可伸缩且安全的应用系统?相信这是困扰着无数开发者的难题,在这里我们以一个网站为例,来讨论一下如何做好大型应用系统的架构设计。

架构演化发展历程

大型网站的技术挑战主要来自于庞大的用户,高并发的访问和海量的数据。

初始阶段

大型网站都是从小型网站发展而来,小型网站最开始时没有太多人访问,只需要一台服务器就绰绰有余,这时的网站架构如图所示。

image.png

应用和数据分离

随着业务的发展,一台服务器逐渐不能满足需求:越来越多的用户访问导致性能越来越差,越来越多的数据导致存储空间不足。这时就需要将应用和数据分离。

应用和数据分离后整个网站使用三台服务器:应用服务器、文件服务器和数据库服务器,如图所示。

image.png

这三台服务器对硬件资源的要求各不相同,应用服务器需要处理大量的业务逻辑,因此需要更快更强大的CPU;数据库服务器需要快速磁盘检索和数据缓存,因此需要更快的硬盘和更大的内存;文件服务器需要存储大量用户上传的文件,因此需要更大的硬盘

使用缓存

随着用户逐渐增多,网站又一次面临挑战:数据库压力太大导致访问延迟,进而影响整个网站的性能,用户体验受到影响。

网站访问遵循二八定律:80%的业务访问集中在20%的数据上。既然大部分的业务访问集中在一小部分数据上,那么如果把这一小部分数据缓存在内存中,是不是就可以减少数据库的访问压力,提高整个网站的数据访问速度,改善数据库的写入性能了呢?

网站使用的缓存可以分为两种:缓存在应用服务器上的本地缓存和缓存在专门的分布式缓存服务器上的远程缓存。本地缓存的访问速度更快一些,但是受应用服务器内存限制,其缓存数据量有限,而且会出现和应用程序争用内存的情况。远程分布式缓存可以使用集群的方式,部署大内存的服务器作为专门的缓存服务器,可以在理论上做到不受内存容量限制的缓存服务,如图所示。

image.png

使用应用服务器集群

使用缓存后,数据访问压力得到有效缓解,但是单一应用服务器能够处理的请求连接有限,在网站访问高峰期,应用服务器成为整个网站的瓶颈。

使用集群是解决高并发、海量数据问题的常用手段。当一台服务器的处理能力、存储空间不足时,不要企图去换更强大的服务器,对大型网站而言,不管多么强大的服务器,都满足不了网站持续增长的业务需求。这种情况下,更恰当的做法是增加一台服务器分担原有服务器的访问及存储压力。

只要能通过增加一台服务器的方式改善负载压力,就可以以同样的方式持续增加服务器不断改善系统性能,从而实现系统的可伸缩性。应用服务器集群是可伸缩集群架构设计中较为简单成熟的一种,如图所示。

image.png

通过负载均衡调度服务器,可将来自用户浏览器的访问请求分发到应用服务器集群中的任何一台服务器上,如果有更多的用户,就在集群中加入更多的应用服务器,使应用服务器的负载压力不再成为整个网站的瓶颈。

读写分离

网站在使用缓存后,使绝大部分数据读操作访问都可以不通过数据库就能完成,但是仍有一部分读操作和全部的写操作需要访问数据库,在网站的用户达到一定规模后,数据库因为负载压力过高而成为网站的瓶颈。

目前大部分的主流数据库都提供主从热备功能,通过配置两台数据库主从关系,可以将一台数据库服务器的数据更新同步到另一台服务器上。网站利用数据库的这一功能,实现数据库读写分离,从而改善数据库负载压力,如图所示。

image.png

应用服务器在写数据的时候,访问主数据库,主数据库通过主从复制机制将数据更新同步到从数据库,这样当应用服务器读数据的时候,就可以通过从数据库获得数据。为了便于应用程序访问读写分离后的数据库,通常在应用服务器端使用专门的数据访问模块,使数据库读写分离对应用透明。

反向代理和CDN

随着业务不断发展,用户规模越来越大,不同地区的用户访问网站时,速度差别也极大。为了提供更好的用户体验,网站需要加速网站访问速度。主要手段有使用CDN和反向代理,如图所示。

image.png

CDN和反向代理的基本原理都是缓存,区别在于CDN部署在网络提供商的机房,使用户在请求网站服务时,可以从距离自己最近的网络提供商机房获取数据;而反向代理则部署在网站的中心机房,当用户请求到达中心机房后,首先访问的服务器是反向代理服务器,如果反向代理服务器中缓存着用户请求的资源,就将其直接返回给用户。

使用分布式文件系统和分布式数据库系统

数据库经过读写分离后,从一台服务器拆分成两台服务器,但是随着网站业务的发展依然不能满足需求,这时需要使用分布式数据库。文件系统也是一样,需要使用分布式文件系统,如图所示。

image.png

分布式数据库是网站数据库拆分的最后手段,只有在单表数据规模非常庞大的时候才使用。不到不得已时,网站更常用的数据库拆分手段是业务分库,将不同业务的数据库部署在不同的物理服务器上。

使用NoSQL和搜索引擎

随着网站业务越来越复杂,对数据存储和检索的需求也越来越复杂,网站需要采用一些非关系数据库技术如NoSQL和非数据库查询技术如搜索引擎,如图所示。

image.png

业务拆分

大型网站为了应对日益复杂的业务场景,通过使用分而治之的手段将整个网站业务分成不同的产品线。具体到技术上,**将一个网站拆分成许多不同的应用,每个应用独立部署维护。应用之间可以通过一个超链接建立关系(在首页上的导航链接每个都指向不同的应用地址),也可以通过消息队列进行数据分发,当然最多的还是通过访问同一个数据存储系统来构成一个关联的完整系image.png

分布式服务

随着业务拆分越来越小,存储系统越来越庞大,应用系统的整体复杂度呈指数级增加,部署维护越来越困难。

既然每一个应用系统都需要执行许多相同的业务操作,比如用户管理、商品管理等,那么可以将这些共用的业务提取出来,独立部署。由这些可复用的业务连接数据库,提供共用业务服务,而应用系统只需要管理用户界面,通过分布式服务调用共用业务服务完成具体业务操作,如图所示。

image.png

大型网站的架构演化到这里,基本上大多数的技术问题都得以解决。

架构模式

为了解决应用系统面临的高并发访问、海量数据处理、高可靠运行等一系列问题与挑战,大型互联网公司在实践中提出了许多解决方案,以实现高性能、高可用、易伸缩、可扩展、安全等各种技术架构目标。这些解决方案又被更多公司重复使用,从而逐渐形成架构模式。

分层

分层是企业应用系统中最常见的一种架构模式,将系统在横向维度上切分成几个部分,每个部分负责一部分相对比较单一的职责,然后通过上层对下层的依赖和调用组成一个完整的系统。

在网站架构中,通常将应用系统分为应用层、服务层、数据层,如下图所示。

image.png

通过分层,可以更好地将一个庞大的软件系统切分成不同的部分,便于分工合作开发和维护。各层之间具有一定的独立性,只要维持调用接口不变,各层可以根据具体问题独立演化发展而不需要其他层必须做出相应调整。

但是分层架构也有一些挑战,就是必须合理规划层次边界和接口,在开发过程中,严格遵循分层架构的约束,禁止跨层调用及逆向调用。在实践中,大的分层结构内部还可以继续分层。

分层架构是逻辑上的,三层结构可以部署在同一个物理机器上。但是随着网站业务的发展,必然需要对已经分层的模块分离部署,使网站拥有更多的计算资源以应对越来越多的用户访问。

分割

分层是将软件在横向方面进行切分,分割则是在纵向方面对软件进行切分。

网站越大,功能越复杂,服务和数据处理的种类也越多。将这些不同的功能和服务分割开来,包装成高内聚低耦合的模块单元,一方面有助于软件的开发和维护;另一方面,便于不同模块的分布式部署,提高网站的并发处理能力和功能扩展能力。

大型网站分割的粒度可能会很小。比如在应用层,将不同业务进行分割,例如将购物、论坛、搜索、广告分割成不同的应用,由独立的团队负责,部署在不同的服务器上。

分布式

对于大型网站,分层和分割的一个主要目的是为了切分后的模块便于分布式部署,即将不同模块部署在不同的服务器上,通过远程调用协同工作。分布式意味着可以使用更多的资源完成同样的功能,能够处理的并发访问和数据量也更大。

但分布式在解决网站高并发问题的同时也带来了其他问题。典型的有下面几点:

  1. 意味着服务调用必须通过网络,这可能会对性能造成比较严重的影响。
  2. 服务器越多,宕机的概率也就越大,造成的服务不可用可能会导致很多应用不可访问,使网站可用性降低。
  3. 数据在分布式的环境中保持数据一致性非常困难,分布式事务也难以保证。
  4. 系统依赖错综复杂,开发管理维护困难。

因此分布式设计要根据具体情况量力而行。常用的分布式方案有:分布式服务、分布式数据库、分布式计算、分布式配置、分布式锁和分布式文件系统等。

集群

使用分布式虽然已经将分层和分割后的模块独立部署,但是对于用户访问集中的模块,还需要将独立部署的服务器集群化,即多台服务器部署相同应用构成一个集群,通过负载均衡设备共同对外提供服务。

因为服务器集群有更多服务器提供相同服务,因此可以提供更好的并发性,当有更多用户访问的时候,只需要向集群中加入新的机器即可。同时当某台服务器发生故障时,负载均衡设备或者系统的失效转移机制会将请求转发到集群中其他服务器上,提高系统的可用性。

缓存

缓存就是将数据存放在距离计算最近的位置以加快处理速度。缓存是改善软件性能的第一手段,在复杂的软件设计中,缓存几乎无处不在。比如常见的反向代理、Redis(未开启持久化)、CDN等。

使用缓存有两个前提条件,一是数据访问热点不均衡,某些数据会被更频繁的访问,这些数据应该放在缓存中;二是数据在某个时间段内有效,不会很快过期,否则缓存的数据就会因已经失效而产生脏读,影响结果的正确性

缓存除了可以加快数据访问速度,还可以减轻后端应用和数据存储的负载压力,网站数据库几乎都是按照有缓存的前提进行负载能力设计的。

异步

应用系统的一个重要目标是降低耦合性。系统解耦的手段除了前面提到的分层、分割、分布式等,还有一个重要手段是异步,业务之间的消息传递不是同步调用,而是将一个业务操作分成多个阶段,每个阶段之间通过共享数据的方式异步执行进行协作。

异步架构是典型的生产者消费者模式,两者不存在直接调用,只要保持数据结构不变,彼此功能实现可以随意变化而不互相影响,这对网站扩展新功能非常便利。除此之外,使用异步消息队列还有如下优点:

  • 提高系统可用性。消费者服务器发生故障,数据会在消息队列服务器中存储堆积,生产者服务器可以继续处理业务请求,系统整体表现无故障。消费者服务器恢复正常后,继续处理消息队列中的数据。
  • 加快网站响应速度。处在业务处理前端的生产者服务器在处理完业务请求后,将数据写入消息队列,不需要等待消费者服务器处理就可以返回,响应延迟减少。
  • 消除并发访问高峰。用户访问网站是随机的,存在访问高峰和低谷。使用消息队列将突然增加的访问请求数据放入消息队列中,等待消费者服务器依次处理,就不会对整个网站负载造成太大压力。

但需要注意的是,使用异步方式处理业务可能会对用户体验、业务流程造成影响,需要网站产品设计方面的支持。

冗余

网站需要7×24小时连续运行,但是服务器随时可能出现故障,特别是服务器规模比较大时,出现某台服务器宕机是必然事件。要想保证在服务器宕机的情况下网站依然可以继续服务,不丢失数据,就需要一定程度的服务器冗余运行,数据冗余备份,这样当某台服务器宕机时,可以将其上的服务和数据访问转移到其他机器上。

访问和负载很小的服务也必须部署至少两台服务器构成一个集群,其目的就是通过冗余实现服务高可用。数据库除了定期存档进行冷备份外,还需要对数据库进行主从分离,实时同步实现热备份。

自动化与安全

目前应用系统的自动化架构设计主要集中在发布运维方面。包括自动化发布、自动化代码管理、自动化测试、自动化安全监测、自动化部署、自动化监控、自动化告警、自动化失效转移与恢复、自动化降级和自动化分配资源等。

系统在安全架构方面也积累了许多模式:通过密码和手机校验码进行身份认证;登录、交易等操作需要对网络通信进行加密,网站服务器上存储的敏感数据如用户信息等也进行加密处理;为了防止机器人程序滥用网络资源攻击网站,网站使用验证码进行识别;对于常见的用于攻击网站的XSS攻击、SQL注入、进行编码转换等相应处理;对于垃圾信息、敏感信息进行过滤;对交易转账等重要操作根据交易模式和交易信息进行风险控制。

架构核心要素

关于什么是架构,维基百科是这样定义的:“有关软件整体结构与组件的抽象描述,用于指导大型软件系统各个方面的设计”。

一般说来,除了功能需求外,软件架构还需要关注性能、可用性、伸缩性、扩展性和安全性这5个要素。

性能

性能是网站的一个重要指标,任何软件架构设计方案都必须考虑可能会带来的性能问题。也正是因为性能问题几乎无处不在,所以优化网站性能的手段也非常多,主要的方式可以总结如下:

  • 浏览器:浏览器缓存、使用页面压缩、合理布局页面、减少Cookie传输等
  • CDN和反向代理
  • 本地缓存和分布式缓存
  • 异步消息队列
  • 应用层:服务器集群
  • 代码层:多线程、改善内存管理等
  • 数据层:索引、缓存、SQL优化等,以及合理使用NoSQL数据库

可用性

网站高可用的主要手段是冗余,应用部署在多台服务器上同时提供访问,数据存储在多台服务器上互相备份,任何一台服务器宕机都不会影响应用的整体可用,也不会导致数据丢失。

对于应用服务器而言,多台应用服务器通过负载均衡设备组成一个集群共同对外提供服务,任何一台服务器宕机,只需把请求切换到其他服务器即可,但是一个前提条件是应用服务器上不能保存请求的会话信息。

对于存储服务器,需要对数据进行实时备份,当服务器宕机时需要将数据访问转移到可用的服务器上,并进行数据恢复以保证继续有服务器宕机的时候数据依然可用。

除了运行环境,网站的高可用还需要软件开发过程的质量保证。通过预发布验证、自动化测试、自动化发布、灰度发布等手段,减少将故障引入线上环境的可能。

伸缩性

衡量架构伸缩性的主要标准有:是否可以用多台服务器构建集群,是否容易向集群中添加新的服务器,加入新的服务器后是否可以提供和原来的服务器无差别的服务,集群中可容纳的总的服务器数量是否有限制。

对于应用服务器集群,通过使用合适的负载均衡设备就可以向集群中不断加入服务器。对于缓存服务器集群,需要使用高效的缓存路由算法,避免加入新服务器导致路由大面积失效。关系数据库很难做到大规模集群的可伸缩性,因此关系数据库的集群伸缩性方案必须在数据库之外实现,通过路由分区等手段将部署有多个数据库的服务器组成一个集群。至于大部分NoSQL数据库产品,由于其先天就是为海量数据而生,因此其对伸缩性的支持通常都非常好。

扩展性

衡量架构扩展性的主要标准就是不同产品之间是否很少耦合。在网站增加新的业务产品时,是否可以实现对现有产品透明无影响,不需要任何改动或者很少改动既有业务功能就可以上线新产品。

网站可伸缩架构的主要手段是事件驱动架构和分布式服务。

事件驱动架构在网站通常利用消息队列实现,将用户请求和其他业务事件构造成消息发布到消息队列,消息的处理者作为消费者从消息队列中获取消息进行处理。通过这种方式将消息产生和消息处理分离开来,可以透明地增加新的消息生产者任务或者新的消息消费者任务。

分布式服务则是将业务和可复用服务分离开来,通过分布式服务框架调用。新增产品可以通过调用可复用的服务实现自身的业务逻辑,而对现有产品没有任何影响。可复用服务升级变更的时候,也可以通过提供多版本服务对应用实现透明升级,不需要强制应用同步变更。

安全性

网站的安全架构就是保护网站不受恶意访问和攻击,保护网站的重要数据不被窃取。衡量网站安全架构的标准就是针对现存和潜在的各种攻击与窃密手段,是否有可靠的应对策略。

文章目录
  1. 1. 架构演化发展历程
    1. 1.1. 初始阶段
    2. 1.2. 应用和数据分离
    3. 1.3. 使用缓存
    4. 1.4. 使用应用服务器集群
    5. 1.5. 读写分离
    6. 1.6. 反向代理和CDN
    7. 1.7. 使用分布式文件系统和分布式数据库系统
    8. 1.8. 使用NoSQL和搜索引擎
    9. 1.9. 业务拆分
    10. 1.10. 分布式服务
  2. 2. 架构模式
    1. 2.1. 分层
    2. 2.2. 分割
    3. 2.3. 分布式
    4. 2.4. 集群
    5. 2.5. 缓存
    6. 2.6. 异步
    7. 2.7. 冗余
    8. 2.8. 自动化与安全
  3. 3. 架构核心要素
    1. 3.1. 性能
    2. 3.2. 可用性
    3. 3.3. 伸缩性
    4. 3.4. 扩展性
    5. 3.5. 安全性