《大型网站技术架构:核心原理与案例分析》读书笔记

学习者:zohar.zzh

时 间:2019-1-21

目录


第1篇 概述


1 大型网站架构演化

1.1 大型网站软件系统的特点

  • 高并发,大流量
  • 高可用: 7x24h不间断服务。
  • 海量数据
  • 用户分布广泛,网络情况复杂
  • 安全环境恶劣
  • 需求快速变更,发布频繁
  • 渐进式发展

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

1.2.1 初始阶段的网站架构

应用程序、数据库、文件等所有资源都在一个服务器上。操作系统为Linux,应用程序使用PHP,部署在Apache上,数据库为MySql。

1.2.2 应用服务器和数据服务分类

应用和数据分离之后形成三个服务器:应用服务器、文件服务器和数据库服务器。

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

    问题:数据库访问延时。

1.2.3 使用缓存改善网站性能

大部分业务访问集中在一小部分数据上,把这一小部分数据缓存在内存中,减少数据库访问压力。

缓存分两种:

  1. 缓存在应用服务器上的本地缓存
  2. 缓存在专门的分布式缓存服务器上的远程缓存
  • 本地缓存:访问速度更快一些,但是受到应用服务器内存限制,缓存数据量有限,会出现和应用程序争用内存的情况
  • 远程分布式缓存:使用集群的方式,部署在大内存的服务器上。

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

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

使用集群是网站解决高并发、海量数据问题的常用手段。当网站持续增长的业务需求,增加一台服务器分担原有的访问以及存储压力也是一种恰当的做法。 通过负载均衡调度服务器,将访问请求分发到应用服务器集群中的任何一台服务器中。

1.2.5 数据库读写分离

虽然使用缓存能够缓解部分压力,但是仍有一部分读操作(缓存不命中、缓存过期)和全部的写操作仍是需要访问数据库的。

大部分主流数据库都提供主从热备功能,通过配置两台数据库主从关系,可以将一台数据库服务器的数据更新同步到另外一台服务器上。利用这一功能,实现数据库读写分离,从而改善数据库负载压力。 应用服务器写数据的时候,访问主数据库服务器,主数据库通过主从复制机制将数据更新同步到从数据库,这样应用服务器读取数据的时候,就可以读取从数据库服务器。应用服务器使用专门的数据访问模块,可以使数据库读写分离。

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

CDN和反向代理的基本原理都是缓存。

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

CDN和反向代理都是为了尽快的返回数据给用户,一方面加快用户访问速度,另一方面减轻服务器的负载压力。

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

将数据库和文件系统拆分到多个服务器上。

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

1.2.8 使用NOSQL和搜索引擎

满足数据存储和检索的需求。

NoSQL和搜索引擎对可伸缩的分布式特性具有很好的支持。 应用服务器通过一个统一的数据访问模块访问各种数据,减轻应用程序管理诸多数据源的麻烦。

1.2.9 业务拆分

  • 根据产品线划分,讲一个网站拆分成许多不同的应用,每个应用独立部署维护。
  • 应用之间通过一个超链接建立关系,也可以通过消息队列进行数据分发。

1.2.10 分布式服务

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

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

  • 大型网站架构的核心价值是随网站所需灵活应对。
  • 驱动大型网站技术法阵的主要力量是网站的业务发展

1.4 网站架构设计误区

  • 以为追随大公司的解决方法:值得学习和借鉴,但是不能盲从。
  • 为技术而技术:网站技术是为业务而存在的。
  • 企图用技术解决所有问题:技术用来解决业务问题,业务问题也可以通过业务手段来解决。

2 大型网站架构模式

2.1 网站架构模式

  • 问题与挑战:高并发访问、海量数据处理、高可靠运行。
  • 目标:高性能、高可用、易伸缩、可扩展、安全。

2.1.1 分层

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

网站软件系统分为:应用层、服务层、数据层。

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

    需要严格遵守分层架构的约束,禁止跨层次调用(应用层直接调用数据层)以及逆向调用(数据层调用服务层)。

大的分层结构内部还可以继续分层:

  • 应用层可以分为视图层业务逻辑层
  • 服务层可以分为数据接口层逻辑处理层

在实际部署上,可以部署在同一个服务器上,也可以分别部署在不同服务器上。

2.1.2 分割

对软件在纵向方面进行切分。将不同的功能和服务分割开来,包装成高内聚低耦合的模块单元。

2.1.3 分布式

分割和分层的一个主要目的是为了切分后的模块便于分布式部署,也就是将不同的模块部署在不同的服务器上,通过远程调用协同工作。

解决高并发问题的同时也会带来的一些问题:

  • 分布式服务调用必须通过网络,会造成性能影响;
  • 服务器越多,宕机的可能性也就越大,也就会造成部署在该部分的应用不可访问,网站可用性降低;
  • 数据在分布式环境下保持一致性也是比较困难的;
  • 分布式事务难保证;
  • 导致网站依赖错综复杂,开发管理维护困难。

常用的分布式方案:

  • 分布式应用和服务:分层和分割后的应用和服务模块分布式部署,不仅改善网站性能和并发性、加快开发和发布速度、减少数据库连接资源消耗,还可以让不同的应用复用共同的服务。
  • 分布式静态资源:将网站的静态资源如JS,CCS,LOGO图片等资源独立分布式部署,并采用独立的域名。静态资源分布式部署可以减轻应用服务器的负载压力;采用独立域名可以加快浏览器并发加载速度。
  • 分布式数据和存储
  • 分布式计算

2.1.4 集群

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

个人理解: 分布式和集群的差别 :分布式就是通过分层和分割将应用进行切分,形成更小的模块,然后这些模块可以部署在不同的服务器上面,这也就是所谓的“分布”的更通俗的含义;集群就是针对一个模块,会同时部署在很多服务其上面,这些服务器共同完成一个功能任务,这几个服务器也就组成一个集群。

2.1.5 缓存

缓存就是将数据放在距离计算最近的位置以加快处理速度。

  • CDN:内容分发网络,部署在距离用户最近的网络服务商,用户请求总是先到达网络服务商那里。网络服务商会缓存网站的一些静态资源(较少变化的数据),以最快速度返回给用户。
  • 反向代理:属于网站前端架构的一部分,部署在网站的前端,当用户请求达到网站的数据中心时,最先访问的就是反向代理服务器。反向代理服务器缓存的是网站的静态资源,无需将请求转发给应用服务器就能返回给用户。
  • 本地缓存:在应用服务器本地缓存着热点数据,应用程序可以在本机内存中直接访问数据,无需访问数据库。
  • 分布式缓存:数据缓存在一个专门的分布式缓存集群中,应用程序通过网络通信访问缓存数据。

使用缓存的因素:

  • 数据访问热点不均衡,某些数据访问更频繁;
  • 数据某个时间段内有效,否则缓存数据就会因为失效而产生脏读,影响结果的正确性。

2.1.6 异步

系统解耦合的手段:分层、分割、分布、异步。

异步:业务之间的消息传递不是同步调用,而是讲一个业务操作分成多个阶段,每个阶段之间通过共享数据的方式异步执行执行协作。

实现异步:

  • 单一服务器:内部通过多线程共享内存队列的方式实现异步。处在业务操作前面的线程把输出写入到队列,后面的线程从队列中读取数据进行操作
  • 分布式系统:多个服务器集群通过分布式消息队列实现异步。分布式消息队列可以看做是内存队列的分布式部署。

异步架构是典型的生产者消费者模式。

使用异步消息队列的特性:

  • 提高系统可用性:消费者服务器发生故障,数据会在消息队列服务器中存储堆积,生产者服务器继续处理业务请求,系统整体表现不异常,消费者服务器恢复正常后,仍可以继续处理。
  • 加快网站相应速度:生产者服务器将数据写入消息队列后不用等待,可以继续返回响应。
  • 消除并发访问高峰:只是消息队列中的请求访问数据增加,等待消费者服务器依次处理即可。

2.1.7 冗余

服务器冗余运行、数据冗余备份,保证网站的正常运行。

  • 冷备份:数据库定期备份,存档保存。是指在关闭数据库并且数据库不能更新的状况下进行的数据库完整备份。
  • 热备份:数据库进行主从分离,保证在线业务高可用。是系统处于正常运转状态下的备份。
  • 灾备中心:全球范围部署。

2.1.8 自动化

自动化代码管理、自动化测试、自动化安全监测、自动化部署、自动化监控、自动化报警、自动化失效转移、自动化失效恢复、自动化降级、自动化分配资源。

2.1.9 安全

密码、手机校验码、加密、过滤、风险控制。

3 大型网站核心架构要素

5个架构要素:性能、可用性、伸缩性、扩展性、安全性。

3.1 性能

提高性能手段:

  • 浏览器端:浏览器缓存、页面压缩、合理布局页面、减少Cookies传输。
  • CDN:减少访问路径。
  • 反向代理:缓存热点文件。
  • 应用服务端:本地缓存和分布式缓存,通过缓存在内存中的热点数据处理用户请求,加快请求处理过程。
  • 异步操作:将用户请求消息发送至消息队列。
  • 集群:多台服务器组成一个集群共同对外服务。
  • 代码层面:使用多线程、改善内存管理。
  • 数据库服务端:索引、缓存、SQL优化,NOSQL通过优化数据模型、存储结构、伸缩特性等手段。

衡量网站性能指标:响应时间、TPS、系统性能计数器。

3.2 可用性

提高可用性手段:主要是冗余

  • 应用服务器:多台服务器通过负载均衡设备组成一个集群共同对外服务。
  • 存储服务器:对数据进行实时备份。

3.3 伸缩性

伸缩性:通过不断向集群中加入服务器的手段来缓解不断上升的用户并发访问压力和不断增长的数据存储需求。

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

3.4 扩展性

扩展性关注网站的功能需求。

衡量的主要标准:增加新的业务产品时,是否可以实现对现有产品透明无影响,不需要任何改动或者很少改动既有的业务功能就可以上线新产品。不同产品之间是否很少耦合,一个产品改动,其他产品无影响。

主要手段:事件驱动架构和分布式服务。

事件驱动架构:利用消息队列实现。 分布式服务:将业务和可复用服务分离,通过分布式服务框架进行调用。新增产品可以通过调用可复用服务实现自身业务逻辑,而对现有产品没有任何影响。可复用服务升级变更时,可以通过提供多版本服务对应用实现透明升级,不需要强制应用同步变更。

3.5 安全性

衡量的主要标准:针对现存和潜在的各种攻击与窃密手段,是否有可靠的应对策略。


第2篇 架构


4 瞬时响应:网站的高性能架构

4.1 网站性能测试

性能测试是性能优化的前提和基础,也是性能优化结果的检查和度量标准。

4.1.1 不同视角下的网站性能

  1. 用户角度

    网站响应的时间。 用户网站角度性能 优化手段:前端架构优化手段,优化HTML式样、利用浏览器端的并发和异步特性。

  2. 开发人员角度

    关注应用程序本身与相关子系统性能,包括响应延时、系统吞吐量、并发处理能力、系统稳定性等技术指标。

    主要优化是手段:利用缓存加速数据读取,使用集群提高吞吐量,使用异步消息加快请求响应以及实现削峰,优化代码改善性能等手段。

  3. 运维人员角度

    关注基础设施性能和资源利用率。如:带宽、硬件配置、网络架构、服务器和带宽的利用率等。 优化手段:建设优化骨干网、高性比服务器、虚拟化技术优化资源利用率。

4.1.2 性能测试指标

主要指标:响应时间、并发数、吞吐量、性能计算器等。

  1. 响应时间

    发送请求到收到响应的时间间隔。一般测试多次取平均值。

  2. 并发数

    同时处理请求的数目。

  3. 吞吐量

    单位时间内处理的请求数量。

  4. 性能计数器

    描述服务器或者操作系统性能的一些数据指标。包括:System Load、对象与线程数、内存使用、CPU使用、磁盘与网络I/O等指标。System Load即系统负载,指当前正在被CPU执行和等待被CPU执行的进程数目总和,是反映系统闲忙程度的重要指标。

4.1.3 性能测试方法

可分为:性能测试、负载测试、压力测试、稳定性测试。

  1. 性能测试

    以系统设计初期规划的性能指标为预期目标,对系统不断施加压力,验证系统在可接受范围内,是否能达到性能预期。

  2. 负载测试

    不断增加并发请求,知道系统某项和多项性能指标达到安全临界值。

  3. 压力测试

    超过安全负载的情况下,对系统继续施加压力,知道系统崩溃或者不能再处理任何请求来获得系统的最大压力承受能力。

  4. 稳定性测试

    在特定硬件软件、网络环境条件下,给系统加载一定业务压力,让系统较长时间运行,检测系统是否稳定。

性能测试曲线 对应的并发响应时间曲线: 并发响应时间曲线

4.1.4 性能测试报告

4.1.5 性能优化策略

  1. 性能分析

    检查请求处理的各个环节的日志,分析哪个环节响应时间不合理。-> 然后检查监控数据,分析影响性能的主要原因是内存、磁盘、网络还是CPU,是代码问题还是架构不合理,或是系统资源确实不足。

  2. 性能优化

    分为:Web前端性能优化、应用服务器性能优化、存储服务器性能优化。

4.2 Web前端性能优化

Web前端指网站业务逻辑之前的部分,包括:浏览器加载、网站视图模型、图片服务、CDN服务。

主要优化手段:优化浏览器访问、反向代理、CDN。

4.2.1 浏览器访问优化

  1. 减少http请求

    主要手段:合并CSS、JavaScript、图片成一个文件,这样浏览器只需要请求一次。多张图片可以合成一张,如果每张图片都有不同的超链接,可通过CSS便宜响应鼠标点击操作,构造不同的URL。

  2. 使用浏览器缓存

    CSS、JavaScript、Logo、图标这些静态资源文件更新频率都比较低,而这些文件几乎是每次http请求都是需要的,如果将这些文件缓存在浏览器中,可以较好地改善性能。通过设置HTTP头中Cache-Control和Expires属性,可设定浏览器缓存时间。

    有时候静态资源文件变化需要及时或者立马进行更新,这种时候可以通过改变文件名来实现,即更新JavaScript文件而并不是更新JS文件内容,而是生成一个新的JS文件并更新HTML文件中的引用。

    在更新静态资源的时候,应采用批量更新方法,比如更新10个图标文件,不宜把10个文件一次全部更新,而是一个一个文件逐步更新,并有一定的间隔时间,以免用户浏览器突然大量缓存失效,集中更新缓存,造成服务器负载骤增、网络堵塞的情况。

  3. 启用压缩

    服务器端进行压缩,浏览器端对文件进行解压,可有效减少通信传输的数据量。

  4. CSS放在页面最上面、JavaScript放在页面最下面

    浏览器会下载完全部CSS之后才对整个页面进行渲染,因此最好的做法是将CSS放在页面最上面。JavaScrpit则相反,浏览器在加载JS后立即执行,有可能会阻塞整个页面,造成页面显示缓慢,因此放在页面最下面。但如果页面解析时要用到JavaScprit,这时候放在底部就不合适了。

  5. 减少Cookies传输

    一方面,Cookie包含在每次请求和响应中,太大的Cookie会影响数据传输。另一方面,某些静态资源文件的访问,发送Cookie没有意义,可以将静态资源使用独立域名访问,避免请求静态资源时发送Cookie,减少Cookie传输次数。

4.2.2 CDN加速

CDN(内容分发网络)本质上也是一中缓存。部署在网络运行商机房。 利用CDN网站的架构

4.2.3 反向代理

利用反向代理的网站架构

反向代理服务器既可以保护网站安全的作用,又可以通过配置缓存功能加速Web请求。还可以加上负载均衡技术。

4.3 应用服务器性能优化

优化手段:缓存、集群、异步。

4.3.1 分布式缓存

网站性能优化第一定律:优先考虑使用缓存优化性能。

  1. 缓存的基本原理

缓存指将数据存储在访问速度高的存储介质中。一方面,因为访问速度高可以减少时间。另一方面,存储已经计算过的数据,就不用再重复进行计算可以直接使用,减少时间。

缓存的本质:一个内存Hash表。数据缓存以Key,Value的形式存储在Hash表中。

计算Hash表的索引下标,做简单的就是余数法,Hash表索引 = HashCode % Hash表长度。

缓存主要用来存储读写比例高但是变化很少的数据。

先从缓存中读取,如果读取不到或者数据失效,在访问数据库,并将数据写入缓存中。

  1. 合理使用缓存

避免以下几种情况:

  • 频繁修改的数据:写入缓存后,应用还来不及读取,数据就已经失效。
  • 没有热点的访问:缓存使用内存作为存储,不可能将所有数据都缓存起来,只能讲最新访问的数据缓存起来,将历史数据清理。
  • 数据不一致与脏读:对缓存设置失效时间,一旦超过失效时间,就要从数据库中重新加载。
  • 缓存可用性:缓存设计的初衷是提高数据的读取性能,缓存数据丢失或者不可用时不会影响到应用程序处理(可以直接从数据库中读取)。但是大部分数据库访问是完成不能承受如此大的压力而发生宕机。
  • 缓存预热:热点数据利用LRU(最近最久未用算法)对不断访问的数据筛选淘汰处理的,这个过程需要花费很长时间。新启动的缓存系统没有任何数据,因此性能都不会太好,最好的方法是在缓存系统启动之前吧热点数据加载好,这个加载手段就叫做缓存预热
  • 缓存穿透:持续高并发请求某个不存在的数据。缓存没有该数据,所有请求都会落在数据库上。简单的对策:将一个不存在的数据也缓存起来(value 为 null)
  1. 分布式缓存架构

缓存部署在多个服务器组成的集群种。其架构有两种方式:JBoss Cache为代表的需要同步更新的分布式缓存;Memcached为代表的互不通信的分布式缓存。

  • JBoss Cache:所有服务器中保存相同的缓存数据,当某台服务器有缓存数据更新的时候,会通知集群中其他机器更新缓存数据或者清楚缓存数据。通常将应用程序和缓存部署在同一台服务器上,因此受限于服务器内存空间。多用于企业应用系统中,大型网站少使用。

  • Memcached:互不通信、海量数据可伸缩。

4.3.2 异步操作

使用消息队列将调用异步化。

不使用消息队列,用户请求数据直接写入数据库,造成压力。使用消息队列后,请求发送给消息队列后彼便立即返回,再由消息队列的消费者进程从消息队列中获取数据,异步写入数据库,具有很好的削峰作用。

4.3.3 使用集群

使用负载均衡技术为一个应用构建一个有多台服务器组成的服务器集群,将并发访问请求分发到多台服务器上。

4.3.4 代码优化

  1. 多线程

使用多线程的主要原因是:IO阻塞和多CPU。

解决线程安全的主要手段:

  • 将对象设计为无状态对象:无状态对象是指对象本身不存储状态信息(对象无成员变量,或者成员变量也是无状态对象)。
  • 使用局部对象:方法内部创建对象。
  • 并发访问资源时使用锁:通过锁方式使多线程并发操作转化为顺序操作。
  1. 资源复用

减少开销很大的系统资源的创建和销毁。比如:数据库连接、网络通信连接、线程、复杂对象等。从编程角度,资源复用的两种模式:单例模式对象池

对象池:数据库连接对象创建好后,将连接对象放入对象池容器中,应用程序要连接的时候,就从对象池中获取一个空闲的连接使用,使用完毕后再将该对象归还到对象池中即可。

  1. 数据结构

  2. 垃圾回收

以JVM为例,内存主要划分为:堆(heap)堆栈(stack)。堆栈:用于存储线程上下文信息,如:方法参数、局部变量等。堆:存储对象的内存空间,对象的创建和释放、垃圾回收就是在堆这里进行的。

JVM回收机制中,将应用程序的堆空间分为年轻代(Young Gen)和年老代(Old Gen)。年轻代又分为:Eden区、From区、To区,新建对象总是在Eden区中被创建,当Eden区空间填满时,就触发一次Young GC,将还在使用的对象复制到From区,这样Eden区都是未被使用的空间,可以继续创建对象。当Eden区再次被用完,又会触发一次Young GC,将Eden区和From区还在被使用的对象复制到To区,在下一次Young GC则是将Eden区和To区还在被使用的对象复制到From区。经过多次Young GC,某些对象会多次在From区和To区多次复制,如果超过某个阈值还没有释放,则会将对象复制到年老代(Old Gen)。如果年老代空间也用完了,就会出发Full GC,也就是全量回收。全量回收队系统性能产生较大影响,因此要合理设置年轻代和年老代空间大小,减少Full GC。

4.4 存储性能优化

4.4.1 机械硬盘和固态硬盘

4.4.2 B+树和LSM树

B+树是专门针对磁盘存储而优化的N叉排序树,以节点为单位存储在磁盘中。从根节点开始查找所需节点编号和磁盘位置。将节点一次一次加载到内存中查找,直到找到所需数据。

LSM树可以看做N阶合并树。数据写操作(插入、修改、删除)都在内存中进行,这些数据在内存中仍然还是一棵排序树,当数据量超过设定内存阈值后,会将这棵排序树和磁盘上最新的排序树进行合并。当这棵排序树的数据量也超过设定的阈值后,和磁盘下一级的排序树继续合并。合并过程中会用最新的数据覆盖旧的数据。数据读操作总是从内存中的排序树开始搜索,如果没有找到,就从磁盘上的排序树顺序查找。

4.4.3 RAID和HDFS

RAID磁盘阵列,改善磁盘的访问延时,增强磁盘的可用性和容错能力。

  • RAID0:写数据时,根据磁盘数量将数据分为N份,同时写入N快磁盘中。读取也是一样。不做备份,如果有一块损坏,数据完整性破快。
  • RAID1:数据会同时写入两块磁盘。相当于做备份。
  • RAID10:集合RAID0和RAID1的特点,把数据切分为N/2块同时写入两块磁盘中。
  • RAID5:加入了校验数据,可以通过校验数据和N-1块磁盘上的数据得出该磁盘上的数据。校验数据螺旋式地写入了所有磁盘中。因为添加了校验数据,所有实际存储的容量应该只有N-1块的容量。
  • RAID6:数据需要较高的可靠性,出现同时损坏两块磁盘的情况下,仍需要修复数据,可用这个方法。这种方法相当于存储了2倍的校验数据,也就是说实际能够存储数据的容量为N-2块。

HDFS(hadoop分布式文件系统)在集群上进行数据并发读写和备份。

HDFS以块(Block)为单位管理文件内容,一个文件被分为n个块。写文件时,每写完一个块,HDFS就会将其自动复制到另外两台机器上,保证一个块有3个副本。相当于RAID1功能。

对文件进行处理计算时,通过MapReduce并发计算任务框架,可以启动多个计算子任务(MapReduce Task),同时读取文件的多个块。相当于RAID0功能。

5 万无一失:网站的高可用架构

5.1 网站可用性度量与考核

5.1.1 网站可用性度量

网站不可用时间(故障时间) = 故障修复时间点 - 故障发生(报告)时间点

网站年度可用性指标 = ( 1 - 网站不可用时间/年度总时间)x 100%

5.1.2 网站可用性考核

5.2 高可用网站架构

实现高可用架构的主要手段是数据和服务的冗余备份以及失效转移,一旦某些服务器宕机,就将服务切换到其他可用的服务器上,如果磁盘损坏,则从备份的磁盘读取数据。

典型分层模型是三层:应用层、服务层、数据层。各层次相互独立。应用层:负责具体业务逻辑处理;服务层:提供可复用的服务;数据层:负责数据的存储和访问。

中小型网站实际的部署如下图:

复杂大型网站架构:

  • 位于应用程序的服务器通常为了应对高并发的访问请求,会通过负载均衡设备将一组服务器组成一个集群。负载均衡设备通过心跳检测手段监控服务器,某台服务器不可用,就从集群列表中剔除。
  • 位于服务层的服务器通过集群方式实现高可用,这些服务器被应用层通过分布式服务调用框架访问。分布式服务调用框架会在应用层客户端程序中实现软件负载均衡,并通过服务注册中心对外提供服务的服务器进行心跳检测,发现服务不可用,立即通知客户端程序修改服务访问列表,剔除不可用的服务器。
  • 数据层服务器:写入数据时同时进行复制到其他服务器。实现数据冗余。

5.3 高可用的应用

应用层:处理网站的业务逻辑。显著特点:应用的无状态性。

无状态:应用服务器不保存业务的上下文信息,仅仅根据每次请求提交的数据进行相应的业务逻辑处理,多个服务器都是完全对等的,请求提交到任意服务器所得到的结果都是完全一致的。

5.3.1 通过负载均衡进行负状态服务的失效转移

无状态的一个好处就是,对于任何一台服务器来进行处理都会返回相同的结果,于是可以利用负载均衡机制将请求转发到任意一台服务器上。

负载均衡:实现服务器可用状态实时监控、自动转移失败任务。

负载均衡器通过心跳监测机制发现服务器失去响应,就会将请求发送到其他服务器上。

5.3.2 应用服务器集群的Session管理

  • 单机情况下:Session可由Web容器(如JBos)管理
  • 集群环境下:
    1. Session复制

    每台服务器都保存搜索用户的Session信息,获取Session信息就可以在本机上获取,但是会占用服务器和网络的大量资源。

    1. Session绑定

    负载均衡利用源地址进行Hash算法,将同一IP地址的请求分发到同一台服务器上(也可以根据Cookies信息将同一个用户的请求总是分发到同一台服务器上,这时候负载均衡服务器必须工作在HTTP协议层上)。

    一旦某台服务宕机,保存了的session信息也就不复存在了。

    1. 利用Cookies记录Session

    浏览器将记录Session信息的Cookies一起发送。

    缺点:Cookies大小有限;每次传输都需要传输Cookies,影响性能;用户关闭Cookies访问就不会正常。

    1. Session服务器

    独立部署Session服务器(集群)统一管理Session。

5.4 高可用的服务

高可用服务器策略:

  • 分级管理:核心应用和服务优先使用较好的硬件;服务部署上进行必要的隔离,避免故障连锁反应。低优先级服务通过启动不同的线程或者部署在不同的虚拟机上进行隔离,高优先级服务需要部署在不同的物理机上,核心服务和数据甚至部署在不同地域的数据中心。
  • 超时设置:重试或者将请求转移到提供相同服务的其他服务器上。
  • 异步调用:通过消息队列等异步方式完成。
  • 服务降级:拒绝服务关闭服务拒绝服务:拒绝低优先级应用的调用,减少服务调用的并发数,确保核心应用的正常使用;或者随机拒绝部分应用请求,节约资源。关闭功能:关闭部分不重要的服务,或者服务内部关闭部分不重要的功能。
  • 幂等性设计:服务层保证服务重复调用和调用一次产生的结果相同,即服务具有幂等性。

5.5 高可用的数据

(上面讲完了高可用架构的三层的前面两层:应用层、服务层,下面就开始讲:数据层)

保证数据高可用的手段主要是:数据备份失效转移机制

数据备份:保证数据有多个副本,任意副本失效都不会导致数据的永久丢失,从而实现数据完全的持久化。 失效转移机制:保证一个服务不可访问时,可以快速切换访问数据的其他副本,保证系统可用。

5.5.1 CAP原理

高可用数据的含义: 数据持久性数据可访问性数据一致性

CAP原理:一个提供数据服务的存储系统无法同时满足数据:一致性(Consistency)、可用性(Availibility)、分区耐受性(Patition Tolerance,系统具有跨网络分区的伸缩性)这三个条件。

5.5.2 数据备份

冷备份:定期将数据复制到某种存储介质上。 热备份:分为异步热备同步热备

异步方式:写入操作异步完成。应用程序收到数据服务系统的写操作成功响应时,只写成功了一份,存储系统将会异步地写其他副本(这个过程可能会失败)。

存储服务器分为主存储服务器(Master)和从存储服务器(Slave),应用程序正常情况下只连接主存储服务器,数据写入时,由主存储服务器的写操作代理模块写入本机存储系统后立即返回写操作成功响应,然后异步线程将写操作数据同步到从存储服务器。

同步方式:多分数据副本的写操作同步完成。

5.5.3 失效转移

失效转移:集群中的某台服务器宕机,应用程序针对这台服务器的所有读写操作都需要重新路由到其他服务器,保证数据访问不会失败。

失效转移操作的三个组层部分:失效确认访问转移数据恢复

  1. 失效确认

判断服务器宕机是系统进行失效转移的第一步,系统确认一台服务器是否宕机的手段:心跳检测和应用程序访问失败报告。

对于应用程序的访问失败报告,控制中心还需要再发一次心跳检测进行确认。

  1. 转移访问

确认某台服务器宕机后,就需要将数据读写访问重新路由到其他服务器上。

  1. 数据恢复

恢复到系统设定的副本数目。

5.6 高可用网站的软件质量保证

5.6.1 网站发布

每次关闭的服务器都是集群中的一小部分,发布后然后重启并不会对用户的使用产生多大影响。

5.6.2 自动化测试

使用自动化测试工具或者脚本完成测试。比如Selenium工具。

5.6.3 预发布验证

将应用先发布在预发布服务器上,预发布服务器都部署在相同的物理环境。

网站应用中强调一个处理错误的理念是快速失败,即如果系统启动时发现问题就立刻抛出异常,停止启动让工程师介入排查错误,而不是启动后执行错误的操作。

5.6.4 代码控制

SVN/Git

5.6.5 自动化发布

5.6.6 灰度发布

发布不是一次性全部都发布到集群中,而是将集群分成几批,每天只发布一部分服务器,观察运行稳定没有故障,如果发现问题,只需要回滚已发布的一部分服务器即可。

5.7 网站运行监控

5.7.1 监控数据采集

  1. 用户行为日志收集:服务端日志收集和客户端浏览日志收集。
  2. 服务器性能监控。
  3. 运行数据报告

5.7.2 监控管理

系统报警、失效转移、自动优雅降级

6 永无止境:网站的伸缩性架构

6.1 网站架构的伸缩性设计

两类:一类是:根据功能进行物理分离实现伸缩,也就是不同的服务器部署不同的服务;一类是:通过集群实现伸缩,也就是集群内部署多台服务器部署相同的服务。

6.1.1 不同功能进行物理分离实现伸缩

从一台服务器中不断剥离出不能功能部署在不同服务器上。

纵向分离:将业务处理流程上的不同部分分离部署,实现系统伸缩性。

横向分离:将不同的业务模块分离部署,实现系统伸缩性。

6.1.2 单一功能通过集群规模实现伸缩

集群伸缩性分为:应用服务器集群伸缩 和 数据服务器集群伸缩(又可以分为:缓存数据服务器集群和存储数据服务器集群)。

6.2 应用服务器集群的伸缩性设计

主要通过负载均衡实现应用服务器的伸缩性

6.2.1 HTTP重定向负载均衡

需要两次请求服务器才能完成一次访问,性能较差;重定向服务器自身的处理能力有限可能成为瓶颈,整个集群的伸缩性规模有限;使用HTTP 302响应码重定向,有可能是搜索引擎判断为SEO作弊,降低搜索排名。使用该方法不多见。

6.2.2 DNS域名解析负载均衡

DNS是多级解析,这也就带来了不易更新的问题。而且DNS负载均衡的控制权在域名服务商那里,不易进行改善和管理。

6.2.3 反向代理负载均衡

6.2.4 IP负载均衡

网络层修改请求不标地址进行负载均衡。

6.2.5 数据链路层负载均衡

修改mac地址进行负载均衡。

Web服务器集群中所有服务器的虚拟IP地址都和负载均衡服务器的IP地址相同,只修改目的mac地址。

6.2.6 负载均衡算法

  • 轮询:所有请求依次分发到每台服务器上,所有服务器处理的请求数据都相同。
  • 加权轮询:按照配置的权重分发请求。
  • 随机:随机分配。
  • 最少连接:将请求分发到最少连接的服务器上。
  • 源地址散列:将源IP地址地址进行Hash计算,得到服务器。这样同一个IP地址总是分配到一个服务器,该请求的上下文信息可以储存在这台服务器上。

6.3 分布式缓存集群的伸缩性设计

分布式缓存和应用服务器集群伸缩性设计不同,不能使用简单的负载均衡手段来实现,必须要让新上线的缓存服务器对整个分布式缓存集群影响最小,也就是说新加入的缓存服务器后应使整个服务器集群中已缓存的数据尽可能还被访问到。

6.3.1 Memcached分布式缓存集群的访问模型

缓存数据<”Beijing”,DATA>通过API输入到路由算法模块,来得到一台服务器编号,进而得到机器的IP地址和端口,然后将数据写入到相应编号的缓存服务器中。

6.3.2 分布式缓存伸缩性挑战

简单的路由算法可以使用余数Hash。但是当添加缓存服务器的时候会造成缓存不命中的情况。

6.3.3 分布式缓存的一致性Hash算法

使用一致性Hash环。

6.4 数据存储服务器集群的伸缩性设计

不同于缓存服务器伸缩性设计,数据存储服务器伸缩性主要保证的是数据的可用性和正确性。分为关系数据库集群的伸缩性设计和NoSql数据库的伸缩性设计。

6.4.1 关系数据库集群的伸缩性设计

比较成熟的支持数据分片的分布式关系数据库产品:Amoeba和Cobar。

6.4.2 NoSQL数据库的伸缩性设计

最广泛使用的是HBase

HBase的伸缩性主要依赖于可分裂的HRegion和可伸缩的分布式文件系统HDFS实现。

数据以HRegion为单位进行管理。HRegionServer是物理服务器,每个HRegionServer可以启动多个HRegion实例。所有的HRegion信息都会保存在HMaster上,也可能会同时启动多个HMaster,通过Zookeeper来选出一个主服务器。

应用程序通过Zookeeper获得主HMaster地址,数据Key值得到HregionServer服务器地址,然后请求该服务器上的HRegion获取数据。

7 髓需应变:网站的可扩展架构

  • 扩展性:指对现有系统影响最小的情况下,系统功能可持续扩展或提升能力。
  • 伸缩性:系统通过增加(减少)自身资源规模的方式增强(减少)自己计算处理事务的能力。

7.1 构建可扩展的网站架构

7.2 利用分布式消息队列降低系统耦合性

7.2.1 事件驱动架构

典型的事件驱动架构就是生产者消费者模式。常用的手段就是分布式消息队列。

7.3 利用分布式服务打造可复用的业务平台

  • 纵向拆分:将一个大应用拆分为多个小应用。
  • 横向拆分:将复用的业务拆分出来,独立部署为分布式服务,新增业务只需要调用这些分布式服务。

7.3.3 分布式服务框架设计

7.4 可扩展的数据结构

传统的关系数据库再设计表的结构的时候,就需要指定schema,数据结构难扩展困难。

解决方法:NoSql使用ColumnFamily(列族)。

7.5 利用开放平台建设网站生态圈

  • API接口:给开发者使用的一组API,其形式可以是RESTful、WebService、RPC等;
  • 协议转换:将API输入转换成内部服务可以是别的形式,并将内部服务返回封装成API格式;
  • 安全:除了一般安全手段(身份识别、权限控制等)还要分等级访问带宽限制。
  • 审计:记录第三方应用的访问情况,并进行监控、计费;
  • 路由:各种访问映射到具体的内部服务;
  • 流程:将一组离散的服务组织成一个上下文相关的新服务,隐藏服务细节,提供统一接口供开发者调用。

8 固若金汤:网站的安全架构

8.1 攻击与防御

XSS攻击、SQL注入攻击、CSRF、Session劫持。

8.1.1 XSS攻击

XSS攻击:跨站点脚本攻击,指篡改网页,注入恶意脚本,当用户浏览网页时,控制用户浏览器进行恶意操作的一种攻击方式。

防御手段:

  • 消毒:过滤和消毒处理。如:”>”转义为”&gt”.
  • HttpOnly:禁止页面JS访问带有HttpOnly属性的Cookie。

8.1.2 注入攻击

分为SQL注入攻击和OS注入攻击。

防御手段:

  • 消毒:将请求参数进行消毒。通过正则匹配进行过滤请求数据中可能注入的SQL。
  • 参数绑定:最好的防SQL注入方法。

8.1.3 CSRF攻击

跨站点请求伪造。在用户不知情下,以用户身份伪造请求,其核心就是利用利用了浏览器Cookie或者服务器Session策略,盗取用户信息。


第3篇 案例(抽取的例子)


12 网站秒杀系统架构设计

12.2 秒杀系统应对策略

  1. 秒杀系统独立部署
  2. 秒杀商品页面静态化
  3. 租借秒杀活动网络带宽:将秒杀商品页面缓存在CDN,同样需要临时租借新增的出口带宽。
  4. 动态生成随机下单页面URL:服务端动态生成随机数作为下单页面URL的参数,在秒杀开始的时候才能得到。
  • 如何控制秒杀商品购买按钮的点亮? 购买按钮只有在活动开始的时候才会点亮,如果是动态页面可以在服务端构造响应页面输出,如果是静态页面,缓存在CDN、反向代理器上,甚至浏览器上,用户刷新页面,请求根本不会到达应用服务器。

    解决办法:使用JS脚本控制,使用随机版本号,不会被浏览器、CDN和反向代理服务器缓存。 每次刷新都只是访问JS服务器,来重新加载JS文件。

  • 如何只允许第一个提交的订单被发送到订单子系统? 控制进入下单页面的入口,只有少数用户进入下单页面,其他用户直接进入秒杀结束页面。


第4篇 架构师


2019-2-11 完