架构:大型网站架构演化

概述

如何打造一个高性能、高可用、易扩展、可伸缩且安全的网站。

如何让网站随应用所需灵活变动。

随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进。

1561193987325

架构

单一应用架构

当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。

当用户访问量提高时。

垂直应用架构

当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键。

分布式服务架构

当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。

流动计算架构

当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键。

常见问题

雪崩

由于一个节点的原因导致资源一直被占用,如果这部分资源没有被释放,就会导致其他节点的崩溃。

大型网站软件系统的特点

对大型网站而言,访问量于数据量缺一不可。

与传统企业应用系统相比,大型网站软件系统的特点有:

  • 高并发、大流量。
    • 需要面对高并发用户,大流量访问。
  • 高可用。
    • 系统7*24小时不间断服务,大型网站的宕机事件会成为新闻焦点。
  • 海量数据。
    • 需要存储、管理海量数据,需要使用大量服务器。
  • 用户分布广泛,网络情况复杂。
    • 需要为全球用户提供服务,各地网络情况千差万别。
    • 国内,各个运营商网络互通难的问题。
    • 中美光缆的数次故障,需要一些对国外用户依赖大的网站考虑建立海外数据中心。
  • 安全环境恶劣。
    • 互联网的开放型,容易受到攻击。
  • 需求快速变更,发布频繁。
  • 渐进式发展。
    • 从一个小网站渐进的发展过来。

大型网站架构演化发展历程

任何简单的业务一旦需要处理数以P计的数据和面对数以亿计的用户,问题就会很棘手。大型网站架构主要解决这类问题。

初始阶段的网站架构

  • 一台服务器即可。
  • 应用程序、数据库、文件等所有资源都在一台服务器上。

1554990959181

应用服务与数据服务分离

问题

  • 一台服务逐渐不能满足需求,越来越多的用户导致性能越来越差,越来越多的数据导致存储空间不足。

解决方案:将数据和应用分离:

三台服务器:

  • 应用服务器:需要处理大量业务逻辑,需要更强CPU。
  • 数据库服务器:快速磁盘检索和数据缓存,因此需要更快的磁盘和更大的内存。
  • 文件服务器:存储大量用户上传的文件,需要更大的硬盘。

1563719826873

不同的服务器承担不同的服务角色,网站的并发处理能力、数据存储空间得到很大改善。

使用缓存改善网站性能

问题

  • 数据库压力太大导致访问延迟,进而影响整个系统的性能。

解决方案

  • 网站的访问特点:二八定律,80%的业务访问集中在20%的数据上。
  • 将频繁访问的数据缓存在内存当中。

缓存分类:

  • 本地缓存,缓存在应用服务器上。
    • 速度更快。
    • 受内存限制,并与应用程序争夺内存。
  • 远程缓存,缓存在专门的分布式缓存服务器上。
    • 理论上不受内存限制。

1554991453473

使用应用服务器集群改善网站的并发处理能力

问题

  • 单一应用服务器能够处理的请求连接有限,在网站访问高峰期,应用服务器成为瓶颈。

解决方案

增加服务器分担压力,并可以以此方式不断提升系统性能。实现系统的可伸缩性。

1555049101516

负载均衡调度服务器:将来自用户的访问请求分发到应用服务器集群的任何一台服务器中,若有更多的用户,则在集群中增加更多的服务器,使应用服务器的负载压力不再成为整个网站的瓶颈。

解决集群后的Session问题

当使用集群后,当第一次访问在服务器A上,则Session就建立在了服务器A上,如果不做处理,则无法保证接下来的请求每次都落在服务器A上,则存在Session问题,会话数据无法保存在单机上。

解决方案有:

  • Session Sticky。较好。
  • Session Replication。
  • Session数据集中存储。较好。
  • Cookie Based。

Session Sticky

如果保证同一个会话的请求都在同一个Web服务器上处理,那么对这个会话的个体来说,与之前单机的情况是一样的。则需要负载均衡器能够根据每次请求的会话的标识来进行请求转发。

1566021138175

该方案只是在负载均衡器上做处理,使得同样Session的请求每次都发送到同一个服务器端处理,非常利于针对Session进行服务器端本地的缓存。不过存在以下问题:

  • 如果有一台Web服务器宕机或重启,那么这台机器上的会话数据会丢失,此时用户需要重新登录了。
  • 会话标识是应用层的信息,那么负载均衡器要将同一个会话的请求都保存到同一个Web服务器上的话,就需要进行应用层的解析,开销大。
  • 负载均衡器变为了一个有状态的节点,要将会话保存到具体Web服务器的映射,与无状态节点相比,内存消耗会更大,容灾方面会更麻烦。

举例讲即会话数据是我们的碗筷,要保证每次吃饭都用自己的碗筷,就讲碗筷存在某一家,每次都去这家吃。

Session Replication

如果我们在每个店都存放一套自己的餐具,就可以更自由地选择饭店了。

由Web服务器间保证不同服务器间的Session数据的一致。一般的应用容器都支持该方式。

1566021471625

存在的问题:

  • 同步Session数据带来了网络带宽的开销,只要Session数据有变化,就需要将数据同步到所有其他机器上,机器数越多,同步带来的网络带宽开销就越大。
  • 每台Web服务器都要保存所有的Session数据,如果整个集群的Session数很大的话,则占用大量的空间。

即不适合集群机器数很大的场景。

Session数据集中存储

将Session数据集中存储起来,不同的Web服务器从同样的地方来获取Session。

1566021661022

存在的问题:

  • 读写Session引入了网络操作,相对于本机的数据读取来说,问题在于时延和不稳定性,但一般内网问题很小。
  • 如果集中存储的Session的机器或集群存在问题就会影响应用。

在Web服务器数量较大时,优势很明显。

Cookie Based

该方案通过Cookie传递Session数据,我们将Session数据存放于Cookie中,在Web服务器上从Cookie生成Session数据,就好比每次将自己的碗筷都带在身上。

1566021878494

存在的问题:

  • Cookie长度的限制。
  • 安全性。
  • 带宽消耗。
  • 性能影响。

数据库读写分离

问题

  • 数据库负载压力过高
    • 即使使用缓存后,绝大部分数据访问都可以不通过数据库就能完成
    • 但依然有一部分读操作(缓存访问不命中、缓存过期)和全部的写操作需要访问数据库。会导致数据库负载压力过高

解决方案

配置数据库的主从关系,将一台数据库服务器上的数据更新同步到另一台服务器上,实现读写分离。

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

1555049383697

使用反向代理和CDN加速网站响应

问题:

  • 复杂的网络环境,导致不同地区的用户访问网站速度差别极大

解决方案:

  • 加快网络响应速度,提高用户体验
  • 反向代理与CDN加速
    • 原理为:利用缓存
    • CDN:部署在网络提供商的机房,使得用户在请求网站服务时,可以从距离自己最近的网络提供方机房获得数据
    • 反向代理:部署在网站的中心机房,当用户请求到达中心时,首先访问反向代理服务器,如果反向代理服务器缓存着用户请求的数据,就直接返回给用户
  • 通过缓存数据,也减轻了后端服务器的负载压力

1555049588729

使用分布式存储系统和分布式数据库系统

问题:

  • 任何强大的单一服务器都无法满足持续增长的业务需求。
  • 数据库服务器与文件服务器无法支持。

解决方案:

  • 分布式数据库:
    • 分布式数据库是网站数据库拆分的最后手段,只有在单表数据规模非常庞大时才使用。
    • 常见手段为业务分库,不同业务的数据库部署在不同服务器上。
  • 分布式存储系统:
    • 包含分布式文件系统、分布式Key-Value系统和分布式数据库。
    • 分布式文件系统解决小文件和大文件的存储问题,分布式Key-Value提供高性能的半结构化的支持,分布式数据库提供一个支持大数据、高并发的数据库系统。
    • 通过集群提供高容量、高并发访问、数据冗余容灾的支持。

1555049722498

使用NoSQL和搜索引擎

问题:

  • 网站业务复杂,对数据存储和检索的需求越来越复杂,需要采用一些非关系数据库(NoSQL)以及非数据库查询技术(搜索)。

解决方案:

  • NoSQL与搜索引擎都是源自互联网的技术手段,对于可伸缩的分布式特性有更好地支持。
  • 搜索引擎可作为一个读库,降低数据库压力。
  • 应用服务器通过一个统一的数据访问模块访问各种数据,减轻应用程序管理诸多数据源的麻烦。

1555049803999

分库分表

问题:

数据库的压力依然在不断上升。

垂直拆分

  • 分而治之,将整个网站业务分为不同的产品线。将不同的表拆分到不同的数据库。
  • 将一个网站拆分为许多不同的应用,每个应用独立部署维护。
    • 应用间通过超链接建立关系。
    • 也可以通过消息队列进行数据分发。
    • 最多的是通过访问同一个数据存储系统来构成一个关联的完整系统。

1555050083322

水平拆分

水平拆分即将同一个表的数据拆分到两个数据库中,其原因是某个业务的数据量或更新量到达的单个数据库的瓶颈。

即使一个订单表,也可以拆分为家电订单、图书订单。

也可以依据时间拆分,即2018年的订单,2019年的订单。

此时自增字段等不可用。

分布式服务

问题

  • 随着业务拆分越来越小,存储系统越来越大,应用系统的复杂性指数级增大,部署维护越来越困难。
  • 所有应用都要与数据库系统相连接,数万台服务器中,连接的数目是服务器^2,导致数据库资源不足,拒绝服务。

解决方案

  • 将每一个应用系统需要执行的相同业务操作提取出来独立部署。
  • 由这些可复用的业务连接数据库,提供公共的业务服务。
  • 应用系统只需要管理用户界面,通过分布式服务调用共用业务服务完成具体业务操作。

1555050317233

云计算平台

当大型网站架构解决了海量数据管理和高并发事务的处理,就可以将这些解决方案应用到网站自身以外的业务上,即建设云计算平台,将计算作为一种基础资源出售。

总结

1566022529950

大型网站架构演化的价值观

网站的价值在于它能够为用户提供什么样的价值,在于能做什么,而不是怎么做的。

因此在小网站上追求架构是舍本逐末的,需要做的就是为用户提供更好的服务创造价值。

核心价值观:随网站所需灵活应对

核心价值是随着小型网站业务的逐步发展,慢慢演化为一个大型网站。

驱动大型网站技术发展的主要力量:网站的业务发展

创新的业务发展模式对网站架构逐步提出了更高的要求,才使得创新的网站架构得以发展成熟。

业务成就了技术,后努力提高技术回馈业务。

网站架构设计的误区

一味追求大公司的解决方案

为了技术而技术

网站技术是为业务而存在的,除此毫无意义。

在技术选型和架构设计中,脱离网站业务发展的实际,一味追求技术,会使得架构之路越走越难。

企图用技术解决所有问题

12306的故障问题:

  • 真正的问题是在于业务架构。
    • 在几亿中国人一票难求的情况下进行窗口售票模式在网上售票(0点后出售若干天后的车票)。
    • 是一种促销秒杀。
  • 进行分时段售票、排队机制等,控制并发访问的量。

技术是解决业务问题,业务手段也可以解决业务问题。

没有一劳永逸的解决方案,不当的解决方案只会带来更棘手的问题,真正的方案是在对问题的深刻理解上的创造和创建。

大型网站架构模式

高并发下的方案选择需要依靠业务场景,进行方案的组合。

跨越JVM的一致性问题

强一致性:指分布式事务。

最终一致性:通过补偿和记录的方式实现,在做所有不确定的事情前,先将事情记录下来,再去做不确定的事情。其结果有:

  • 成功:清除记录。
  • 失败:依靠定时任务补偿性不断重试,直到成功。并需要做好幂等
  • 不确定:视为失败。

分层

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

应用层 负责具体业务和视图展示,如网站首页以及搜索输入和结果展示
服务层 为应用层提供服务支持,如用户管理服务、购物车服务等
数据层 提供数据存储访问服务,如数据库、缓存、文件等

各层之间具有一定的独立性,只要维持调用接口不变,各层可以根据具体问题独立发展演化,而不需要其它层做出调整。

分布式

分层与分割的一个主要目的是为了切分后的模块便于分布式部署,即将不同模块部署在不同的服务器上,通过远程协同工作。但是分布式带来了其他的问题:

  • 分布式意味着服务调用必须通过网络,这可能会对性能造成比较严重的影响。
  • 服务器越多,服务宕机的概率也就越大,一台服务器宕机可能导致很多应用不可访问,使得网站的可用性降低。
  • 数据在分布式环境下保持数据一致性也比较困难,分布式事务也难以保证,对网站业务正确性和业务流程有可能造成影响。
  • 分布式导致网络依赖错综复杂,开发管理维护困难。

常见分布式应用方案:

  • 分布式应用和服务。
  • 分布式静态资源。
  • 分布式数据和存储。
  • 分布式计算。
  • 分布式配置、分布式锁、分布式文件系统等。

集群

对于用户访问集中的模块,还需要将独立部署的服务器集群化,即多台服务器部署相同应用构成一个集群,通过负载均衡设备共同对外提供服务。

扩容

占用内存大小取决于工作内存内变量的多少与大小。

单个线程占用的内存不会很大,但是很多线程占用内存就会很多。

  • 垂直扩容:提高系统部件能力(加内存)。
    • 内存的价格,以及当前的内存性能瓶颈等问题。
  • 水平扩容:增加更多系统成员来实现(加服务器),即服务器集群。
    • 共享的资源与系统间的协调性是一种约束条件。例如网络IO、数据库资源。

扩容-数据库

读操作扩展:

  • 垂直扩容。
  • memcache。
  • redis。
  • CDN等缓存。

写操作扩展:

  • 水平扩容。而垂直扩容存在硬盘与CPU的平衡,因此不如水平扩容有效。
  • Cassandra。
  • Hbase等。

缓存

缓存可以使用在APP端、网络转发端、服务端、存储端等位置,而不仅仅是在服务端。现代CPU越来越快的一个重要因素就是使用了更多的缓存。

  • CDN。内容分发网络,部署在距离终端用户最近的网络服务商,用户的网络请求总是先到达他的网络服务商那里,在这里缓存网站的一些静态资源或变化较少的数据,就可以以最快的速度返回给用户。
    • 直播、门户网站、新闻APP。
  • 反向代理。反向代理属于网站前端架构的一部分,部署在网站的前端,当用户请求到达网站的数据中心时,最先访问到的就是反向代理服务器,这里缓存网站的静态资源。
  • 本地缓存。在应用服务器本地缓存着热点数据,应用程序可以在本机内存中直接访问数据,而无需访问数据库。
  • 分布式缓存。大型网站的数据量非常大,即使只缓存一小部分,需要的内存空间也不是单机能够承受的。

使用缓存有两个前提条件

  • 数据访问热点不均衡,某些数据会被更频繁的访问,这些数据应该放在缓存中。
  • 数据在某个时间段内有效,不会很快过期,否则缓存的数据就会因已经失效而产生脏读。

消息队列

开始流程A->发送消息A1到队列->消息A1被处理->处理消息A1。

特性

  • 业务无关,只做消息分发。
  • FIFO,先投递先到达。
  • 容灾,节点的动态增删和消息的持久化。
  • 性能:吞吐量提示,系统内部通信效率提高。

为什么需要

生产和消费的速度或稳定性等因素不一致。

优势

  • 业务解耦。
  • 最终一致性。两个系统的状态保持一致,其存在一个时间限制。
    • A成功,B失败
    • A成功,B成功,但是没有获得B成功的消息,网络异常。
    • A成功,B失败,A想回滚,但是此时A宕机了。
  • 广播。如果没有消息队列,每有一个新的接口都需要联调一次,而消息队列只需要将其投放到消息队列即可。
  • 错峰、流控。将短板效应进行一定的弥补,针对延迟不敏感的服务可以实现错峰。

举例

  • Kafka。
  • RabbitMQ。

应用拆分

从整体将一个应用拆分为多个应用。

原则

  • 业务优先。业务存在边界,根据业务的不同进行拆分。
  • 循序渐进。拆分与测试缺一不可,保证系统测试与功能要完整。
  • 兼顾重构与分层。不能为了分布式而分布式,分层进行提高代码的可重用性。
  • 可靠测试。

思考

  • 应用间通信:RPC(dubbo)、消息队列。
    • RPC框架实时性更高;消息队列传输数据包小,但数据量大,对实时性要求不是很高。
  • 应用间数据库设计,每个应用都有独立的数据库。
  • 避免事务操作跨应用。分布式事务非常消耗资源。

限流

限流:限制一段时间内某段代码执行的频率。

如果直接将大量数据直接插入到master库,则很可能导致master库崩溃。并且master库还要同步到slave库,slave库的延迟也会很大,导致slave的查询也会错误。

如果使用限流,则可以以恒定速率写入到master库,并以正常同步的速度同步到slave库。

限流算法

计数器法

设置一个计数器,对于A接口,每当一个请求到达,Counter++,如果在时间到达1分钟,则重置Counter。

但是存在临界问题:在重置节点附近瞬间发出大量请求。

滑动窗口

每过一定时间窗口向右滑动一格,每一格都有对应的Counter。窗口越多越平滑。

漏桶

请求到达之后存放在一个桶当中,桶中的请求以一个恒定的速率做处理。

令牌桶

一个固定容量的令牌桶。

  • 系统的守护线程一直在向令牌桶内部丢令牌。
  • 当请求到达时,桶内存在令牌,则执行业务。如果没有令牌则根据处理策略处理请求,但一定不会让你执行。

对业务峰值有一定承担能力。

服务降级与熔断

服务降级

流量对一些服务和页面做有策略的降级,以此缓解保证部分或大部分的客户得到正确的响应。

如果当前服务处理不了或请求出错了,则给出一个默认的返回。

服务降级可以根据不同的接口做不同的或默认的定级。

分类

  • 自动降级:超时、失败次数、故障、限流。
    • 超时:超时时间和超时次数。定时用异步机制探测回复情况。
    • 失败:不稳定的API一定的失败次数。使用异步机制探测回复情况。
    • 故障:调用的远程服务挂掉了,可能是DNS、网络故障等。
    • 限流。限流后后续的服务会自动降级。
  • 人工降级:
    • 秒杀、双11等。

考虑

  • 核心服务、非核心服务。
  • 是否支持降级,降级策略。
  • 业务放通场景,策略。

服务熔断

当服务过载,为防止整个系统崩溃,则停止额外的服务。

服务降级与服务熔断

  • 共性:
    • 目的:从可用性、可靠性着想,防止系统整体缓慢或崩溃。
    • 最终表现:某些用户感觉服务不可达或不可用。
    • 粒度:一般是服务级别。
    • 自治性:要求自动处理。
  • 区别:
    • 降级:从整体负荷考虑而引起。降级有层级,一般从最外围开始。
    • 熔断:某个服务或下游服务故障而引起。熔断是框架级处理,每个服务都需要。
    • 实现不同。

数据库分库分表

数据库瓶颈:

  • 单个库数据量太大(1-2T):多个库。
  • 单个数据库服务器压力太大、读写瓶颈:多个库。
  • 单表数据量过大:分表。

切库:

  • 实际应用:读写分离。

分表:

  • 什么时候考虑分表:
    • 当表数据量很大,即使SQL和索引优化后,操作时延还是影响使用。

高可用手段

高可用的手段是数据和服务的冗余备份及失效转移,一旦某些服务器宕机,就将服务切换到其他可用的服务器上。

  • 任务调度系统分布式:elastic-job + zookeeper。
  • 主备切换:apache curator + zookeeper分布式锁实现。
  • 监控报警机制。

总结

  • 利用云计算资源,便可以对所需要的一切技术资源:计算、存储、网络都可以按需购买,线性伸缩。
  • 因此亲身经历架构演化之路的人会越来越少。架构师更应该对这个过程深刻了解,理解成熟的网站架构技术方案的来龙去脉和历史渊源,才能在技术选型和架构决策时有的放矢,直击要害。

参考