笔记 | 大型网站技术架构

对比现阶段微服务、网格服务以及无服务等架构,本书介绍的架构模式略显陈旧,然而选择什么架构模式应该依据解决什么问题、问题规模大小而定夺。更具象理解,架构模式应服务于系统需求,每种架构模式都有它的适用阶段与使用场景,架构设计的原则就是站在较高角度俯瞰全局,权衡利弊之后做出选择与取舍,切莫为了最新技术而生搬硬套。

其次,相较于学习具体一种架构技术,学习基本的架构思维也同样重要,因为系统是全生命周期管理的,我们不能只局限性能、运维等单方面考量去设计、开发、部署、运维系统。此外,基本技术架构思维有助于理解进阶的架构技术,技术架构的迭代过程本身就是一种演化过程,无论是单体系统,还是大型分布式系统都曾有它的历史舞台及功能作用。知其然知其所以然,才会理解大动干戈、迭代升级系统技术架构背后的客观诉求,亦或是高瞻远瞩的愿景。

为此,我们要辩证地看待任何技术更替,技术发展源于业务发展,适时适用就是最好的;以万变应不变,学习经典架构技术之余,也要不断钻研新型的技术架构。

本书框架

  • 第一篇章:演化、模式、要素三维度,以描述网站整体架构
  • 第二篇章:高性能、可用性、伸缩性、拓展性、安全性五要素,详细介绍网站架构的核心原理

    其中负载均衡、异步处理、分布式缓存等技术方案分章节进行详细描述。

  • 第三篇章:具体案例讲解网站技术架构

  • 第四篇章:以架构师角度复盘技术架构

第1篇 概述

01 大型网站架构演化

网站架构演化发展历程

  • 初始阶段的网站架构:应用程序、数据库、文件等所有资源都集中于一台服务器上。

  • 应用与数据服务分离:

    • 应用和数据分离后,整个网站使用三种服务器,即 应用服务器文件服务器数据库服务器
    • 三种服务器对硬件资源的要求不同:

      • 应用服务器:处理大量业务逻辑,偏向于 CPU 算力。
      • 数据库服务器:磁盘检索和数据缓存,偏向于硬盘读写能力和内存容量。

        存在问题:随着用户逐渐增多,数据库读写瓶颈导致访问延迟,从而拖垮整个网站性能。

      • 文件服务器:存储大量用户上传的文件,偏向于硬盘容量。

  • 缓存改善网站的性能:

    • 二八定律:80% 的业务集中在 20% 的数据上。将业务访问集中的小部分数据缓存在内存中,可减少数据库的访问压力。
    • 缓存类型

      • 缓存于应用服务器的 本地缓存
      • 缓存于分布式缓存服务器的 远程缓存

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

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

    • 负载均衡:将用户请求分发到应用服务器集群中任何一台服务器上。若有更多用户加入,在集群中通过增加应用服务器即可(系统可伸缩性)。
  • 数据库读写分离

    • 存在问题:当网站使用缓存后,使得绝大部分数据读操作可不通过数据库即可完成访问,但仍有部分读操作(缓存访问不命中、缓存过期)和写操作需访问数据库,同样会因数据库负载压力过高成为网站瓶颈。
    • 解决方案
      • 数据库的 主从热备份 得以实现数据库读写分离功能。当应用服务器写数据时,访问主数据库;
      • 主数据库通过 主从复制机 机制将数据更新同步至从数据库。当应用服务器读数据时,访问从数据库即可获得数据。
  • 反向代理和 CDN 加速网站响应:它们的基本原理都是 缓存,区别在于:

    • CDN:部署在网络提供商的机房,用户在请求服务时,可从距离最近的网络提供商机房获取数据。
    • 反向代理:部署在网站的中心机房,当用户请求到达中心机房后,首先访问的服务器是反向代理服务器。若反向代理服务器中缓存着用户请求的资源,就将其直接返回给用户。
  • 分布式文件系统和分布式数据库系统

    • 分布式数据库:在单表规模非常庞大时使用,通常数据库拆分手段是 业务分库,将不同业务的数据库部署于不同物理服务器上。
  • NoSQL 和搜索引擎

    • 非关系型数据库技术,比如 NoSQL
    • 非数据查询技术,比如搜索引擎
  • 业务拆分

    • 分而治之 的指导思想,将完整网站业务分成不同的产品线,以产品线开发应用,各位单独部署及维护。
    • 应用之间可通过消息队列进行数据分发,通过访问同一个数据存储系统来构成一个关联的完整系统。
  • 分布式服务:每个应用系统都需执行许多相同的业务操作,比如用户管理、商品管理等,将相同公用的业务抽象成可复用的业务、独立部署。

    分布式服务架构图

网站架构演化的价值观

  • 核心价值:随网站所需要灵活应对
  • 驱动发展:技术发展源于业务发展

网站架构设计误区

  • 一味追随大公司的解决方案
  • 为了技术而技术
  • 企图用技术解决所有问题:技术解决业务问题,业务问题还需业务手段解决。

02 大型网站架构模式

  • 模式:来自建筑学的定义,模式描述了一个在我们周围不断重复发生的问题及该问题解决方案的核心,当遇到适用场景复用该方案即可。
  • 为解决大型网站面临的 高并发访问海量数据处理高可靠运行 等问题与挑战,大型互联网公司在实践中提出了许多解决方案,以实现网站 高性能高可用易伸缩可拓展安全 等各种技术架构目标。

分层

  • 在横向维度将系统切分成几个部分,每个部分负责一部分相较单一的职责,然后通过上下层的依赖和调用组成一个完整系统。
  • 分层可更好地将一个庞大软件系统切分为不同的部分,便于分工合作开发和维护;各层之间具有一定的独立性,只要维持调用接口不变,各层可根据具体问题独立演化发展,其他层不需要做出相应调整。

    [例如] 计算机硬件、操作系统、应用软件分层结构;网络的七层协议;网站软件系统抽象为应用层、服务层、数据层,具体如下表所示。

    层级 功能 举例
    应用层 负责具体业务和视图展示 网站首页、内容检索、数据统计等
    服务层 为应用层提供服务支持 用户管理服务、购物车服务等
    数据层 提供数据存储访问服务 数据库、缓存、文件、搜索引擎等
  • 挑战:合理规划层次边界和接口,开发中必须严格遵循分层架构约束,禁止跨层次调用(应用层直接调用数据层)或是逆向调用(数据层调用服务层等)。

  • 实践
    • 大的分层结构可细化继续分层,比如应用层细分为视图层和业务逻辑层(前端和后端)。
    • 分层架构是逻辑层面的,实际上是可以部署于同一台物理机器上。但随着网站业务发展,必须对已分层模块分离部署,即三层结构分别部署在不同服务器上,使得网站拥有更多计算资源以应对越来越多的用户访问。

分割

  • 分层 是将软件在 横向 方面进行切分;分割 就是在 纵向 方面对软件进行切分。
  • 将网站不同的 功能服务 分割开来,包装成高内聚低耦合的模块单元,有助于:
    • 软件开发和维护;
    • 不同模块分布式部署,提高网站并发处理能力以及功能拓展能力。

分布式

  • 分层、分割的主要目的之一是让切分后的模块便于 分布式部署
  • 分布式意味着可以使用更多计算机硬件资源完成同样的功能,即处理并发访问量和承载数据量更大,进而为更多用户提供服务。
  • 分布式解决高并发问题,同时也存在其他问题:
    • 服务调用必须通过网络,网络会成为性能瓶颈口;
    • 服务器越多,服务器宕机概率越大,部分服务器宕机造成的服务不可用可能会导致很多应用不可用;
    • 数据在分布式环境中保持数据一致性非常困难,同样分布式事务也难以保证。
  • 在网站应用中,常用的分布式方案:

    • 分布式应用和服务:将分层 / 分割后的应用和服务的模块分布式部署。
    • 分布式数据和存储:除了传统关系型数据库外,为分布式网站应用而生的各种非关系型数据库,比 如 NoSQL。
    • 分布式静态资源

      • 动静分离,静态资源(HTML、JS、CSS)分布式部署可减轻应用服务器的负载压力;
      • 通过独立域名访问加快浏览器并发加载的速度;
      • 可由独立前端团队负责界面开发、用户体验维护。
    • 分布式计算:严格意义上,应用、服务、事实数据处理都是计算,网站除了在线业务要处理,还有后台业务也要处理,比如搜索引擎索引构建、数据仓库的数据分析统计等。

      目前网站普遍使用 Hadoop 及其 MapReduce 分布式计算框架进行批处理计算,其特定是移动计算(非移动数据),即将计算程序分发至数据所处位置,以实现加速计算、分布式计算。

集群

  • 多台服务器部署相同应用以构成一个集群,通过负载均衡设备(比如 F5)共同对外提供服务。

    对于用户访问集中的模块(网站首页等),还需要集群以提供更好的并发特性。

  • 由于一个应用由多台服务器提供服务,当某台服务器发生故障时,负载均衡设备系统实效转移机制 会将请求转发到集群其他服务器上,使得服务器故障不影响用户正常使用,提高 系统可用性

缓存

  • 缓存:将数据存放在距离计算最近的位置以加快处理速度。
  • CDN:内容分发网络,部署于距离终端用户最近的网络服务商,用户网络请求总是先达到他的网络服务商。此处会缓存网站的静态资源(较少变化的数据),可就近以最快速度返回给用户。
  • 反向代理:属于网站前端架构一部分,当用户请求到达网站数据中心时,最先访问的是反向代理服务器。此处缓存网站的静态资源,无需将请求继续转发应用服务器就能返回给用户。
  • 本地缓存:在应用服务器本地缓存数据,应用程序可在本机内存中直接访问数据,而无需访问数据库。
  • 分布式缓存:数据缓存于分布式缓存集群,应用程序通过网络通信访问缓存数据。
  • 使用缓存需满足以下前提条件:
    • 数据访问热点不均衡,某些数据会被频繁访问;
    • 数据在某时间段内有效,不会短时间过期,否则缓存的数据会因失效而产生脏读,影响结果正确性。

异步

  • 异步是系统解耦合的重要手段,业务间的消息传递不是同步调用,而是将一个业务操作分成多个阶段,每阶段之间通过共享数据方式异步执行、进行协作。
  • 单一服务器,内部通过 多线程共享内存队列 实现异步;分布式系统,多个服务器集群通过 分布式消息队列 实现异步。
  • 异步架构是典型的 生产者-消费者 模式,两者不存在直接调用,只需保持数据结构不变,彼此功能实现可随意变化而不相互影响。使用异步消息队列拥有以下特性:

    生产者:把数据放到消息队列;消费者:从消息队列里边取数据

    • 提高系统可用性:消费者服务器故障宕机,数据会在消息队列服务器中存储堆积,生产者服务器可继续处理业务请求。
    • 消除并发访问高峰:生产者服务器将突然增加的访问请求数据放入消息队列,等待消费者服务器依次,以缓解网站负载压力过大问题。
    • 加快网站响应速度

冗余

  • 服务器冗余运行、数据库冗余备份,当其中某台服务器宕机时,可将其实的服务和数据访问转移至其他机器上。
    • 冷备份:数据库定期备份,存档保存。
    • 热备份:数据库进行主从分离,实时同步实现热备份。
    • 部署灾备数据中心。

自动化

  • 发布过程自动化
  • 自动化代码管理:代码版本控制、代码分支创建/合并等过程自动化
  • 自动化测试:代码部署至测试环节,自启用测试用例进行测试
  • 自动化安全测试:代码静态安全扫描、部署到安全测试环节进行安全攻击测试
  • 自动化部署
  • 自动化监控:服务器心跳检测
  • 自动化报警:异常 / 阈值报警
  • 自动化失效转移:自动隔离失效服务器
  • 自动化失效恢复:重启服务,同步数据保证数据一致性。
  • 自动化降级
  • 自动化分配资源

03 大型网站架构要素

  • 架构:通俗说法,高层次的规划,难以改变的决定。
  • 软件架构:有关软件整体结构与组件的抽象描述,用于指导大型软件系统各方面的设计。

    系统各个重要组成部分及其关系构成了系统的架构,这些组成部分可以是具体功能模块,也可以是非功能性设计于决策。它们之间相互关联组成一个整体,共同构成软件系统的架构。

  • 软件架构设计:除了满足当前系统功能需求之外,额外关注 性能可用性伸缩性拓展性安全性 的架构要素。

高性能

  • 代码层面:使用多线程、改善内存管理等手段优化性能。
  • 浏览器端
    • 通过浏览器缓存、使用页面压缩、合理布局页面、减少 Cookie 传输等手段改善性能。
    • 使用 CDN 将网站静态内容分发至距离用户最近的网络服务商机房,使得用户以最短访问路径获取数据。在网站机房部署反向代理服务器,缓存热点文件,加快请求响应速度,减轻应用服务器负载压力。
  • 应用服务器端
    • 缓存:使用服务器本地缓存和分布式缓存,通过缓存在内存中的数据处理用户请求,加快请求处理过程,减轻数据库负载压力。
    • 异步:通过异步操作将用户请求发送至消息队列等待后续任务处理,而当前请求直接返回响应给用户。
    • 集群:将多台应用服务器组成一个集群共同对外服务,提高整体处理能力。
  • 数据库服务器端
    • 关系型数据库:索引、缓存、SQL 优化。
    • 非关系型数据库:NoSQL 数据库通过优化数据模型、存储结构、伸缩性等手段提高性能。
  • 衡量网站性能有一系列指标,如响应时间、TPS、系统性能计数器等。通过监控这些指标,可以分析系统瓶颈、预测网站容量,并对异常指标进行报警,以保证系统可用性。

可用性

  • 高可用性的主要手段是冗余,应用程序部署于多台服务器上同时提供访问,数据存储在多台服务器上相互备份。
  • 对于应用服务器:多台应用服务器通过负载均衡设备组成一个集群共同对外提供服务,任何一台服务器宕机,只需把请求切换到其他服务器即可实现应用的高可用。
  • 对于存储服务器:实时备份数据。

伸缩性

  • 衡量网站架构伸缩性的主要标准:
    • 是否可以多台服务器构建集群;
    • 是否容易向集群中添加新的服务器;
    • 加入新服务器后是否可提供原来无差别的服务;
    • 集群中可容纳的总服务器数量是否有限制。
  • 对于应用服务器:服务器上不保存数据,所有服务器都是对等的(无状态),通过负载均衡设备向集群不断加入服务器。
  • 对于缓存服务器集群:加入新的服务器会导致缓存路由失效(虽然缓存数据可通过数据库重新加载),需改进缓存路由算法以保证缓存数据的可访问性,比如分布式缓存的一致性 Hash 算法。
  • 关系数据库的集群伸缩性必须在数据库之外实现,通过 路由分区 等将部署有多个数据库的服务器组成一个集群。

拓展性

  • 衡量网站架构拓展性的主要标准:
    • 是否可以对现有产品透明无影响,不需要任何改动或很少改动现有业务功能就可以上线新产品。
    • 不同产品之间强内聚、弱耦合。
  • 网站可拓展架构主要手段:事件驱动架构分布式服务
    • 事件驱动架构:利用 消息队列 实现,将用户请求和其他业务事件构造成消息发布到消息队列,消息消费者从消息队列中获取消息并进行处理。分离消息产生和消息处理过程,可透明地增加新的消息生成者任务或新的消息消费者任务。
    • 分布式服务:将业务和可复用服务分离开来,通过分布式服务框架调用。

第2篇 架构

04 网站高性能架构

网站性能测试

不同视角的网站性能
  • 用户:网站响应速度快慢,由用户计算机与服务器通信时间、网站服务器处理时间、用户浏览器构造请求、解析响应数据时间所决定。

    优化手段:前端架构优化、调整浏览器缓存策略、CDN 服务、反向代理等。

  • 开发人员:关注应用程序本身及其相关子系统的性能,包括响应延迟、系统吞吐量、并发处理能力、系统稳定性等指标。

    优化手段:缓存加速数据读取、使用集群提高吞吐、使用异步消息加快请求响应及 “削峰”。

  • 运维人员:关注基础设施和资源利用率,比如网络运营商带宽、服务器硬件配置、数据中心网络结构、服务器和网络贷款的资源利用率。

    优化手段:建设优化骨干网、利用虚拟化技术优化资源利用等。

性能测试指标
  • 性能测试指标:不同人员视觉有不同的性能衡量指标,以开发与测试人员为例,网站性能测试的主要指标有响应时间、并发数、吞吐量、性能计数器。

  • 响应时间:从发出请求开始,到最后响应数据所需要的时间。

    • 测试程序通过模拟应用程序,记录收到响应和发出请求之间的时间差来计算系统响应时间。
    • 若测试目标操作本身花费时间较少,则可以通过重复请求,通过求均值方式计算得单次请求的响应时间。
  • 并发数:系统能够同时处理请求的数量。

    • 对于网站而言,并发数即网站并发用户数,指同时提交请求的用户数目。
    • 相对应的还有网站在线用户数和网站系统用户数(注册用户),其数量关系为:

      网站系统用户数 >> 网站在线用户数 >> 网站并发用户数

    • 网站产品设计初期需要规划不同发展阶段的网站系统用户数,根据产品特性和运营手段,推算在线用户数和并发用户数。

    • 通过多线程模拟并发用户,以测试系统的并发处理能力。为模拟真实用户行为,不仅仅是启用多线程不断发送请求,而是在请求之间加入一个随机等待时间(也称作 思考时间),以还原真实的高并发场景。
  • 吞吐量:单位时间内系统处理的请求数量,体现系统的整体处理能力。

    • TPS(每秒事务数);HPS(每秒 HTTP 请求数);QPS(每秒查询数)
    • 系统并发数由小逐渐增大过程中,当中过程也伴随着服务器系统资源消耗逐渐增大,系统吞吐量逐渐增加,但达到一定极限值时随着并发数增长而下降,系统资源耗尽达到系统崩溃点,吞吐量为零。

      系统吞吐量、系统并发数和响应时间的关系可理解为:高速公路的通行情况,吞吐量表示每天通过收费站的车辆数(等价于高速费),正在行驶的车辆数记作并发数,响应时间记作车数。当行驶车辆少时,车数行驶较快,收取高速费相对较少;随着行驶车辆数增加,车速会变慢,但收到的高速费会逐渐增加;行驶车辆数增加超过某极限值后,任何偶然因素都会导致高速路瘫痪,车走不动、收费停滞(资源耗尽)。

  • 性能计数器:描述服务器或者操作系统性能的数据指标,包括 System Load、对象与线程数、内存使用、CPU使用、磁盘与网络 I/O 等。对指标设定目标阈值,当监控系统发现性能计数器超过阈值时,就向运维及开发人员报送系统异常警报。

    • System Load:系统负载,当前正被 CPU 执行和等待 CPU 执行的进程数目总和,反映系统忙闲程度的指标。Load 值等于 CPU 核W数为理想值,表示系统资源被充分利用。
性能测试方法
  • 性能测试:以系统设计初期规划的性能指标为预期目标,对系统不断施压,验证系统再资源可接受范围内。
  • 负载测试:对系统不断增加并发请求,直到系统某项或者多项性能指标达到安全临界值。若某种资源已呈饱和状态,继续对系统施加压力,系统处理不但不提高反而下降。
  • 压力测试:超过安全负载的情况下,对系统不断施加压力直至系统崩溃或不能再处理任何请求,以测量得系统最大压力承受能力。
  • 稳定性测试:不同生产环境、不同时间点的请求压力是不均匀的、呈波浪特性的,为更好地模拟生产环境,稳定性测试过程中也应该不均匀地对系统施加压力。

Web 前端性能优化

浏览器访问优化
  • 减少 HTTP 请求:每次 HTTP 请求都需建立通信链路进行数据传输,在服务端需要启动独立线程处理每个 HTTP 请求。减少 HTTP 请求,降低通信、服务器线程开销,可有效提高访问性能的有效手段。

    例如:合并 CSS、合并 JavaScript、合并图片等,即减少文件请求加载次数。

  • 使用浏览器缓存:

    • CSS、JavaScript、图标等静态资源文件的更新频率都较低,而他们几乎是每次 HTTP 请求都需要的,将文件缓存在浏览器中,可极好地改善性能。

      若静态资源文件变化需要及时生效、应用至客户端浏览器,可通过改变文件名实现。

    • 通过 HTTP Header 中的 Cache-Control 和 Expires 属性来设定浏览器缓存。

  • 启用压缩:在服务端启用 GZIP 对 HTML、CSS、JavaScript 文件进行压缩,有效减少通信传输的数据量。

  • CSS 前置、JavaScript 后加载:

    • 遵循浏览器自上而下的页面渲染机制,CSS 先行加载渲染页面;
    • JavaScript 在加载完成后即执行,有可能会造成阻塞而页面加载显示缓慢,一般后置。

      当然具体问题要具体分析,若页面解析需用到 JavaScript 时,代码后置就不合适了。

CDN 加速
  • 内容分发网络(Content Distribute Network, CDN)

    • 网络访问第一跳,即数据缓存在距离用户最近的地方,得以最快速度获取数据。
    • CDN 部署在网络运营商机房,同时运营商又是终端用户的网络服务提供商,为此用户请求路由的第一跳即达到 CDN 服务器。
    • CDN 缓存了浏览器请求的资源时,CDN 直接返回给浏览器,实现最短路径返回响应。

      CDN 缓存的一般是静态资源,比如图片、 CSS、JavaScript 脚本等。

负载均衡
  • 负载均衡:通过负载均衡构建的应用集群以提高系统总体处理能力、改善网站高并发性能。
  • 正向代理 v.s. 反向代理

    • 正向代理:客户端代理,隐藏的是客户。客户端为了从目标服务器取得内容,客户端向代理服务器发送一个请求并指定目标,然后代理服务器向目标服务器转交请求并将获得的内容返回给客户端。
    • 反向代理:服务端代理,隐藏的是服务器。以代理服务器来接受 Internet 上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给 Internet 上请求连接的客户端。

      正向代理 v.s. 反向代理

应用服务器性能优化

分布式缓存
  • 网站性能优化第一定律:使用缓存(数据缓存、文件缓存、页面片段缓存)
  • 缓存的基本原理

    • 存储在访问速度相对较高的存储介质中。
    • 缓存的数据是经过计算处理得到的,被缓存的数据无需重复计算即可直接使用,例如统计类数据。
    • 缓存本质是一个哈希表(HashTable),以 Key-Value 形成存储在内存 Hash 表中。

      • 通过 HashCode(Key) 计算 Key 的 HashCode,即对象的唯一标识符。
      • 通过 HashCode 获得 HashTable 的索引下标,对 Hash 表数组长度求模(余数法),余数即为索引下标。
      • 通过索引下标在 Hash 表数组即可取得 Key-Value 键值对。

        HashTable 存储实例

  • 分布式缓存架构

    • 以集群方式提供缓存服务,架构方式有两种:
      • JBoss Cache:需要更新同步的分布式缓存
      • Memcached:不互相通信的分布式缓存
    • JBoss Cache 架构

      • JBoss Cache 通常将应用程序与缓存部署于同一台服务器上。
      • 在集群中所有服务器中保存相同的缓存数据。
      • 当某台服务器有缓存数据更新时,会通知集群中其他机器更新缓存数据或清除缓存数据。

        JBoss Cache 之间的更新同步

    • Memcached 架构

      • 应用程序与缓存分离部署于不同服务器上。
      • 应用程序通过一致性 Hash 等路由算法选择缓存服务器远程访问缓存数据,缓存服务器之间不通信。

        互不通信的 Memcached

  • Memcached

    • 简单的通信协议

      • 通信协议:TCP 协议、UDP 协议
      • 通信序列化协议:基于文本的自定义协议,以一个命令关键字 + 一组命令操作数,例如读取数据的命令协议 get <key>

        通信序列化协议:数据传输的两端必须使用彼此可识别的数据序列化方式才能使通信得以完成,比如 XML、JSON 等文本序列化协议、Google Protobuffer 等二进制序列化协议。

    • 丰富的客户端程序:C/C++、Java、Python、Perl、Ruby 等。

    • 高性能的网络通信:基于 Libevent 的通信模块,支持事件触发的网络通信程序库。

      Libevent:一个用 C 语言编写的、轻量级的开源高性能事件通知库。

    • 高效的内存管理:固定空间分配 —— 内存管理 ——> 内存碎片管理

      • 将内存空间分为一组 Slab,其中又包含一组 Chunk。同一 Slab 的每个 Chunk 的大小是固定的,拥有相同大小 Chunk 的 Slab 被组织在一起,称为 Slab_class。

        Memcached 内存管理

      • 存储数据根据数据大小,寻找一个最小 Chunk 将数据写入,且内存的分配与释放都以 Chunk 为单位。

        数据只能存入一个比它大的 Chunk,且一个 Chunk 只能存一个数据,造成其他内存空间被浪费了。

      • 采用 LRU 算法是否最近最久未被访问数据占用的空间,并标记释放的 Chunk 为未用。

    • 互不通信的服务器集群架构

异步操作
  • 使用 消息队列 将调用 异步化,改善网站的扩展性,也可提升网站系统的性能。
  • 消息队列的 削峰作用:通过异步处理,将短时间高并发产生的事务消息存储再消息队列中,从而削平高峰期的并发事务。
  • 消息队列与业务异步处理注意事项
    • 存在问题:数据写入消息队列即返回用户,数据的业务校验、写数据操作可能失败。
    • 业务流程调整:订单数据写入消息队列,不能立即返回用户订单提交成功,需在消息队列的订单消费者进程真正处理完该订单,甚至业务出库后再通知订单成功,避免交易纠纷。
使用集群
  • 使用 负载均衡 技术为一个应用构建一个由多台服务器组成的服务器集群,将 并发访问请求 分发到多台服务器处理。
代码优化
  • 多线程:I/O 阻塞、多 CPU 利用、线程安全
  • 资源复用:减少系统资源的创建和销毁,比如数据库连接、网络通信连接、线程、复杂对象等。
    • 单例模式(Singleton):
    • 对象池模式(Object Pool):复用对象实例,减少对象创建和资源消耗。
  • 数据结构:不同场景中,灵活组合各种数据结构改善数据读写和计算特性。
  • 垃圾回收:Young GC、Full GC

存储性能优化

  • 缓存解决一部分数据的读压力,但磁盘仍然是系统最严重的瓶颈,磁盘可用性和容错性也是改善网站性能的重要因素。
HDD / SSD
  • 硬盘驱动器(Hard Disk Drive):也称机械硬盘。

    • 其通过马达驱动磁头臂,带动磁头到指定的磁盘位置访问数据。
    • 由于每次访问数据都需移动磁头臂,为此在数据连续访问和随机访问时,移动磁头臂的次数相差巨大,致使性能表现差别也大。

      连续访问:访问数据存储在连续磁盘空间上;随机访问:访问数据存储在不连续磁盘空间上

  • 固态驱动器(Solid State Disk):也称固态硬盘。数据存储在可持久记忆的硅晶体上,为此可像内存一样快速随机访问。

B+ Tree / LSM Tree
  • 文件系统或数据库系统通常会对数据排序后存储,以加快数据检索速度。若需要保证数据在不断更新、插入、删除后依然有序,索引的数据结构也有讲究:MySQL 多数采用 B+ 树;NoSQL 多数采用 LSM 树。
  • B+ 树:N 叉排序树,以树的节点为单位存储数据,从根开始查找所需数据所在节点编号和磁盘位置,将其加载到内存中。
  • LSM 树:N 阶合并树,数据写操作(增/删/改)都在内存中进行。

    • 数据写操作都会创建一个新记录,修改则记录新的数据值,删除则记录一个删除标记。
    • 数据在内存仍然是一颗排序树,当数据量超过设定内存阈值后,会将该棵排序树和磁盘上最新的排序树合并。
    • 当数据量也超过设定阈值后,将和磁盘上下一级的排序树合并。
    • 合并过程中,新数据会覆盖旧数据,或者记录为不同版本。

      LSM 树上进行更新不需磁盘访问,在内存即可完成。同理,读操作也总是从内存的排序树开始搜索,若没有找到再从磁盘上的排序树顺序查找。

RAID / HDFS
  • 廉价磁盘冗余阵列(RAID):利用多块磁盘实现数据的并发读写和数据备份,主要为了改善磁盘的访问延迟,增加磁盘的可用性和容错能力。
  • 常见 RAID 技术,其技术原理如下图所示:

    常见 RAID 的技术原

    • RAID 0:根据磁盘将数据分为 n 份,数据同时并发写入 n 快磁盘,使得数据整体写入速度是一块磁盘的 n 倍。同理,RAID 0 也具有极速的读速度。

      但 RAID 0 不做数据备份,n 块硬盘中只要有一块损坏,数据完整性即被破环,所有磁盘的速度都会损坏。

    • RAID 1:一份数据同时写入两块硬盘,这样任何一块硬盘损坏都不会导致数据丢失,插入一块新磁盘可通过复制数据方式自动修复。

    • RAID 10:将所有磁盘平均分成两份,数据同时在两份磁盘写入(相当与 RAID 1)。但每一份数据在 n/2 块磁盘上,利用 RAID 0 技术并发读写。

      结合 RAID 0 和 RAID 1 两种方案,但 RAID 10 磁盘利用率较低(一半磁盘用来备份数据)。

    • RAID 3:一般情况,一台服务器不会同时损坏两块磁盘的情况(物理因素除外),假设在只损坏一块磁盘情况下,利用其他磁盘的数据回复损坏磁盘的数据,即保证了可靠性和性能,同时提升了磁盘利用率。

      将数据分成 n-1 份,并发写入 n-1 块磁盘,并在第 n 块磁盘记录校验数据,任何一块磁盘损坏(包含校验数据磁盘),都可利用其他 n-1 块磁盘恢复数据。

    • RAID 5:原理与 RAID 3 相似,但校验数据不是写入第 n 块磁盘,而是螺旋式地写入所有磁盘中。即检验数据也平均到所有磁盘上,避免了频繁写坏一块磁盘。

    • RAID 6:原理与 RAID 3 相似,数据写入 n-2 块磁盘中,并螺旋式地在两块磁盘中写入校验信息(使用不同算法生成)。

  • Hadoop 分布式文件系统(HDFS):在整个存储集群的多台服务器上进行数据并发读写和备份。

    • HDFS 以块(Block)为单位管理文件内容,一个文件被分割成若干 Block。
    • 当应用程序写文件时,每写完一个 Block,HDFS 就将其自动复制至另外两台机器上,以保证每个 Block 有三个副本。

      相当于 RAID 1 的数据复制功能。

    • 当对文件进行处理计算时,通过 MapReduce 并发计算任务框架,启动多个计算子任务(MapReduce Task)同时读取文件的多个 Block。

      相当于 RAID 0 的数据并发访问功能。

  • HDFS 包括两个重要服务器角色:名称服务节点(NameNode)和数据存储节点(DataNode)

    • NameNode 在整个 HDFS 只部署一个实例,提供元数据 服务。相当于操作系统中的文件分配表(FAT),管理文件名 Block 的分配,维护整个文件系统的目录树结构。
    • DataNode 部署在 HDFS 集群的单独服务器上,可以部署多个实例,提供 数据存储 服务。
    • 应用程序(Client)需要写文件时,首先访问 NameNode,请求分配数据块,NameNode 根据管理的 DataNode 服务器的磁盘空间,按照一定的负载均衡策略,分配若干数据块以供 Client 使用。

05 网站高可用架构

事物总是先求存后发展,保证网站可用、万无一失,任重道远。

可用性度量

  • 网站不可用也称 网站故障,业界通常用 多少个9 来衡量网站的可用性。

    例如 QQ 的可用性是 4 个 9,即 99.99%。表示 QQ 服务要保证其在所有运行时间中,只有 0.01% 的时间不可用,大约一年当中最多 53 分钟不可用。

    • 2个9 为基本可用,网站年度不可用时间小于 88 小时;
    • 3个9 为较高可用,网站年度不可用时间小于 9 小时;
    • 4个9 为具有自动恢复能力的高可用,网站年度不可用时间小于 53 分钟;
    • 5个9 为极高可用,网站年度不可用时间小于 5 分钟;
  • 网站不可用时间(故障时间)= 故障修复时间点 - 故障发现(报告)时间点

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

高可用架构

  • 高可用架构主要手段是:数据和服务的 冗余备份失效转移
    • 冗余备份:磁盘损坏,则从备份的磁盘读取数据。
    • 失效转移:服务器宕机,将服务切换至其他可用服务器上。
  • 网站架构基本分层模型:

    网站架构基本分层模型

    对于中小型网站,通常应用层和服务层部署在一起;数据层另外部署。

  • 复杂大型网站结构中,划分力度会更小、更细:

    网站架构基本分层模型

    • 位于 应用层 的服务器:为应对高并发的访问请求,通过负载均衡设备将一组服务器组成一个集群共同对外提供服务。
      • 负载均衡设备通过 心跳检测 等手段监控应用服务器是否可用。
      • 遇到不可用的应用服务器时,将其从集群中剔除,并将请求分发到集群其他服务器。
    • 位于 服务层 的服务器:应用层通过分布式服务调用框架访问该些服务器。
      • 分布式服务调用框架会在应用层客户端中实现软件负载均衡。
      • 并且通过 服务注册中心 对提供服务的服务器进行心跳检测,当发现服务不可用时,通知客户端修改服务访问列表,提出不可用服务器。
    • 位于 数据层 的服务器:
      • 数据写入时进行数据同步复制,将数据写入多台服务器上,实现数据冗余备份。
      • 当数据库服务器宕机时,应用程序将访问切换至有备份数据的服务器。
  • 网站的可用性架构设计,不仅要考虑 实际硬件故障 引起的宕机,而且也要考虑 网站升级发布 引起的宕机。

高可用应用

  • 应用层也称业务逻辑层,用于处理网站应用的业务逻辑。
  • 应用层中,应用的显著特定是应用无状态性。
    • 无状态的应用指应用服务器不保存业务的 上下文信息,仅根据每次请求提高的数据进行相应的数据进行相应的业务逻辑处理。
    • 多个服务器实例(服务器)之间 完全对等,请求提交到任意服务器,处理结果都是完全一样的。
负载均衡进行无状态服务的失效转移
  • 所有服务器完全对等,当任意一台或多台服务器宕机,请求可提交集群中任意一台可用机器处理。
  • 实现服务器可用状态实时监测、自动转移失败任务的机制是负载均衡。

    利用负载均衡实现高可用

应用服务器集群的 Session 管理手段
  • Web 应用中将多次请求修改使用的上下文对象称作 会话(Session)。
    • 单机环境:Session 可由部署在服务器上的 Web 容器管理,如 JBoss。
    • 集群环境 :Session 复制;Session 绑定;利用 Cookie 记录 Session;Session 服务器
  • Session 复制

    利用 Session 复制实现应用服务器共享 Session

    • 服务器开启 Web 容器的 Session 复制功能,在集群中几台服务器之间同步 Session 对象,使得每台服务器上都保存所有用户的 Session 信息。
    • 存在问题:
      • 1) 集群规模较大时,集群服务器需大量通信进行 Session 复制,占用服务器和网络资源;
      • 2) 所有用户的 Session 在每台服务器都拥有备份,当出现大量用户访问时会致使服务器内存不够 Session 开销。
  • Session 绑定

    利用负载均衡的会话黏滞机制将请求绑定到特定服务器

    • 利用负载均衡的源地址 Hash 算法实现,负载均衡服务器总是将来源于同一 IP 的请求分发到同一台服务器上,或根据 Cookie 将同一用户的请求总是分发到同一台服务器上。
    • Session 绑定于某台特定服务器上,保证 Session 总能从这台服务器上获取,这种方法又称作 会话黏滞
    • 存在问题:当某台服务器宕机,存在于机器上的 Session 就不复存在,用户请求切换至其他机器因没有 Session 而无法完成业务处理。
  • 利用 Cookie 记录 Session

    利用 Cookie 记录 Session 信息

    • Session 记录在客户端;网站则利用浏览器 Cookie 记录 Session。
    • 存在问题:
      • 1) 受 Cookie 大小限制,记录信息有限。
      • 2) 每次请求响应都需传输 Cookie。
      • 3) 用户关闭 Cookie,访问会不正常。
  • Session 服务器

    利用 Session 服务器共享 Session

    • 独立部署 Session 服务器(集群)统一管理 Session,应用服务器每次读写 Session 时都访问 Session 服务器。
    • 该方案事实上是将应用服务器的状态分离,即 无状态应用服务器有状态 Session 服务器,然后针对这两种服务器的不同特性分别设计其架构。

      有状态 Session 服务器的应用场景:单点登录(SSO)、用户服务等。

高可用服务

可复用的服务和应用一样,也是无状态的服务,可使用类似负载均衡的失效转移策略实现高可用服务。

  • 分级管理
    • 运维上将服务器进行分级管理。
    • 服务器部署时进行必要隔离,避免故障的连锁反应。
  • 超时设置:由于服务器宕机、线程死锁等会导致应用程序对服务端的调用失去响应,用户长时间得不到响应,同时还占用应用程序的资源,不利于及时将访问请求转移到正常服务器上。

    • 在应用程序中设置服务调用的超时时间,一旦超时通信框架就抛出异常。
    • 应用程序根据服务调度策略,可继续重试或将请求转移到提供相同服务的其他服务器上。
  • 异步调用:应用对服务的调用通过消息队列等异步方式完成。

    注意:并不是所有服务调用都适用异步调用,对于获取用户信息此类调用,异步方法会延长响应时间;对于必须确认服务调用成功才能继续下一步操作的应用也不适合使用异步调用。

  • 服务降级:拒绝服务和关闭服务

    • 拒绝服务:
      • 拒绝低优先级应用的调用,减少服务调用并发数,确保核心应用正常使用。
      • 随机拒绝部分请求调用,让部分请求成功,避免大家一起死的 “惨状”。
    • 关闭服务:关闭部分不重要的服务或者服务内部不重要的功能,以节约系统开销。

      淘宝在每年双十一促销中,会在系统最繁忙时段关闭 ”评价“、”确认收货“ 等非核心服务。

  • 幂等性设计:服务层保证服务重复调用和调用一次产生的结果相同,即服务具有幂等性。

高可用数据

  • CAP 原理:数据一致性(Consistency)、数据可用性(Availibility)、数据伸缩性(Partition Tolerance)。

    • 高可用数据的含义
      • 数据持久性:写入数据持久性存储,数据多份副本存放在不同物理设备上。
      • 数据可访问性:一个数据存储设备损坏,切换访问数据源,时间要足够短即终端用户无感知性。
      • 数据以一致性:多份数据副本,由于网络、服务器或者软件故障导致部分副本写入成功、部分写入失败,造成各副本间的数据不一致。
    • CAP原理认为,一个提供数据服务的存储系统无法同时满足以上三个条件。

      通常在大型网站中会强化分布式存储系统的可用性 (A) 和伸缩性 (P),某程度弱化或放弃一致性 (C)。

  • 数据备份

    • 冷备份:定期拷贝数据至某种存储介质上物理存档保管。
    • 热备份:异步热备、同步热备

      异步热备 v.s. 同步热备

  • 失效转移

    • 失效确认:心跳检测和应用程序访问失败报告
    • 访问转移
      • 完全对等存储的服务器:应用程序根据配置直接切换到对等服务器上。
      • 不对等存储的服务器:重新计算路由,重新选择服务器。
    • 数据恢复:服务器宕机,为此数据存储副本数减少,必须将副本数目恢复至系统设定值。

软件质量保证

为了保证网站线上系统的可用性,需要采取有别于传统软件开发的 质量保证手段

  • 网站发布:事实上,网站发布过程和服务器宕机效果相当,即关闭应用再重新部署启动应用。

  • 自动化测试

    • 引入原因:代码上线前需进行严格测试;为保证系统没有引入始料未及的 BUG,还需对整个网站系统(至少上下文关联的系统模块)进行全面回归测试。

      使用人工测试,基于成本、时间、效果以及测试覆盖率都不尽理想。

    • 解决方案:为此,引入 Web 自动化测试工具。

      比如 Selenium 可在浏览中模拟用户操作进行测试。

  • 预发布验证

    • 引入原因:即使通过严格的测试,应用部署到线上服务器仍然会出现各种问题,更甚无法启动服务器。主要原因是生产与测试环境不一致导致的,比如数据库、缓存、第三方服务接口等。
    • 解决方案:在预发布服务器上预发布验证。预发布服务器与正式服务器都部署在相同的物理环境,同一数据源、相同生产配置、依赖 相同外部服务等。区别于正式服务器, 唯一区别是没有配置在负载均衡服务器上,外部用户无法访问。

      注意:预发布服务器连接的是真实生产环境、真实数据库,所有预发布验证操作都是真实有效的数据,这些操作也许会引起不可预期的问题。

  • 代码控制:代码控制核心问题是代码管理,既保证代码发布版本稳定正确,同时保证不同团队开发互不影响。

    • 主干开发,分支发布:一个分支一个发布版本,该版本有BUG继续在该分支上修改发布,待验证稳定时合并回主干。
    • 分支开发,主干发布:开发新功能或修复BUG,从主干拉取一个分支进行,开发测试通过后合并回主干,由主干进行发布。主干上的代码永远是最新发布版本。
  • 自动化发布:火车发布模型

    很多网站选择周四作为发布日,这样周一至周三准备上线工作,周五留有余地以挽回错误。而选择周五发布,发现问题周末就得凉凉了。

  • 灰度发布

    • 将服务器分成若干部分,每天只发布一部分服务器,观察运行是否稳定无故障。
    • 持续几天逐渐把整个集群全部发布完毕。
    • 期间若发现问题,只需要回滚已发布的部分服务器。

      灰度发布也常用于用户测试(AB测试),即部分服务器上新版本应用,其余服务器保持老版本。然后监控用户操作行为,收集用户体验报告,比较用户对两个版本的满意度,以确定最终发布版本。

网站运行监控

盲人骑瞎马,夜半临深池。不允许没有监控的系统上线,没有指标可循,提高可用性、减少故障率就无从做起了。

数据采集
  • 用户行为日志收集:用户在浏览器的所有操作、操作系统与浏览器版本信息、IP 地址、页面访问路径、页面停留时间等。

    获得以上数据,可用于 PV/UV 指标、分析用户行为、优化网站设计、个性化推荐与营销等。

    • 客户端浏览器日志收集:利用页面嵌入 JavaScript 脚本收集用户真实的操作行为,比如百度统计、腾讯统计等。
    • 服务端日志收集:Web 服务器的日志记录功能。
  • 服务器性能监控:内存占比、磁盘I/O、网络 I/O 等。

监控管理
  • 系统报警:给监控指标设定报警阈值。
  • 失效转移:主动通知应用,应用及时进行失效转移。
  • 自动优雅降级:访问高峰期间,主动关闭部分非核心功能,释放部分系统资源。

06 网站伸缩性架构

  • 网站伸缩性:不需要改变网站的软硬件设计,仅通过改变部署的服务器数量就可扩大或缩小网站的服务处理能力。

网站架构的伸缩性设计

  • 网站伸缩性设计分类

    • 不同功能进行物理分离实现伸缩:即按照不同服务分类,比如应用部署、数据存储、数据缓存等。

      • 纵向分离(服务分层后分离):系统服务分离,将业务处理流程的不同部分分离部署,实现伸缩性。
      • 横向分离(业务分割后分离):业务模块分离,将不同业务模块分离部署,实现伸缩性。
    • 单一功能通过集群规模实现伸缩:集群内多台服务器部署相同的服务。

      网站伸缩性实现方式

  • 一个具有良好伸缩性架构设计的网站,其架构设计必定走在业务发展前面。当业务剧增时,只需要购买服务器,简单部署实施即可。

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

  • 实现思路:负载均衡(HTTP 重定向、DNS 域名解析、反向代理)
  • 前提条件:应用服务器应该设计成无状态的,每次用户请求都可以送达集群中任意一台服务器上处理。
  • 负载均衡服务器:实现应用服务器的伸缩性。

    • 可感知、可配置集群的服务器数量
    • 向服务器分发请求
  • HTTP 重定向负载均衡

    • 优势:实现简单
    • 劣势:两次服务器请求完成一次访问;重定向服务器自身处理能力可能成为瓶颈。

      HTTP 重定向负载均衡

  • DNS 域名解析负载均衡:配置多个 @ 记录,DNS 根据 @ 记录和负载均衡算法计算得到 IP 地址。

    • 优势:将负载均衡的任务转移至 DNS。
    • 劣势:DNS 是多级解析,每一级 DNS 都可能缓存 @ 记录。当下线某台服务器,即使修改了 DNS 的 @ 记录,但由于生效时间限制,若此时域名解析到下线的服务器,将导致用户访问失败。

      DNS 域名解析负载均衡

  • 反向代理负载均衡:在应用程序中分发数据

    • 优势:缓存资源,改善网站性能。
    • 劣势:所有请求和响应的中转站,代理服务器性能会成为瓶颈。

      反向代理负载均衡

  • IP 负载均衡

    • 优势:在内核进程中完成数据分发,较反向代理负载均衡有更好性能。
    • 劣势:集群中最大响应数据吞吐量受限制于负载均衡服务器的网卡带宽。

      IP 负载均衡

  • 数据链路层负载均衡:在通信协议的数据链路层修改 MAC 地址进行负载均衡,又称作直接路由方式。

    • 优势:不需要通过负载均衡服务器进行地址转换,响应数据包直接返回浏览器,以避免负载均衡服务器网卡带宽成为瓶颈。

      直接路由方式是目前较为广泛使用的负载均衡手段,相关开源产品 LVS(Linux Virtual Server)。

      数据链路层负载均衡

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

  • 实现目标:因缓存请求必须找到缓存有数据的服务器才能访问,该特点会严重制约分布式缓存集群的伸缩性设计。为此,新加入的缓存服务器应使得整个缓存服务器集权已缓存的数据尽可能还被访问到,这是分布式缓存集群伸缩性设计的主要目标。

  • 路由算法:分布式缓存的一致性 Hash 算法

    • 算法过程
      • Step.01:先构造一个长度为 $2^{32}$ 的整数环(二叉排序树实现,且二叉树的最左叶子节点与最右叶子节点相连接构成环);
      • Step.02:根据节点名称的 Hash 值(Hash $\in [0, 2^{32}-1]$)将缓存服务器节点放置于 Hash 环上;
      • Step.03:计算缓存数据的 Key 值得到 Hash 值,然后在 Hash 环上顺时针查找距离 Key 的 Hash 值最近的缓存服务器节点;
      • Step.04:完成 Key 到服务器的 Hash 映射查找。
    • 实操演示

      一致性 Hash 算法

      • 假设 Node1 的 Hash = 3594963423,Node2 的 Hash = 1845328979,而 Key0 的 Hash = 2534256785,那么 Key0在环上顺序针查找最近的节点就是 Node1。
      • 当缓存服务器集群需要扩容时,只需将新加入节点 Node3 的 Hash 值放入一致性 Hash 环中。由于 Key 是顺时针查找距离最近邻节点,因此新加入的节点只影响整个环中的一小段。

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

  • 缓存的目的是加速数据读取并减轻数据存储服务器的负载压力,部分缓存数据丢失不影响业务的正常运作。
  • 数据存储服务器必须保证数据的可靠存储,任何情况都必须保证数据的可用性与正确性。
关系数据库集群
  • 主从模式
    • 数据写操作都在主服务器上,主服务器将数据同步到集群中其他从服务器。
    • 数据读操作及数据分析等离线操作在从服务器上进行。
  • 业务分割模式:不同业务数据表部署于不同的数据库集群上、同一张表分片储存于多个数据库,及数据库的 分库分表
NoSQL 数据库
  • 引言:先设计数据库然后设计程序,从而导致关系模型绑架对象模型,引申出旷日持久的对象贫血模型与充血模型之争。
    • 贫血模型:业务对象仅包含数据而不包含行为,其作用只是数据载体或数据传递介质。系统的业务逻辑全部放到业务逻辑层,会导致业务逻辑层比较庞大。
    • 充血模型:业务对象既包含数据又包含行为,他的作用不再只是数据的载体而是一个真正有行为的对象。
  • 问题:关系数据库难以克服的缺陷,即糟糕的海量数据处理能力以及僵硬的设计约束。
  • 方案:Not Only SQL(NoSQL),指非关系的、分布式数据库设计模式。但 NoSQL 只是关系数据库的补充而非替代方案。
  • HBase

    HBase 架构

    • 其伸缩性主要依赖于分裂的 HRegion 以及可伸缩的分布式文件系统 HDFS。
    • HRegion:数据以 HRegion 为单位进行管理,当应用程序访问数据时,必须先找到 HRegion,然后将读/写操作提交给 HRegion,由 HRegion 完成存储层面的数据操作。
      • 每个 HRegionServer 上启用多个 HRegion 实例;
      • 每个 HRegion 中存储一段 Key 值区间的数据,比如 [key1, key2) ;
      • 每当 HRegion 写入数据达到配置阈值时,触发分裂机制变成两个 HRegion,并在集群中进行迁移使得 HRegionServer 负载均衡。
    • HMaster:用于存储 HRegion 信息(Key 值区间、所在 HRegionServer 地址、访问端口等)。
      • 为提高高可用性,HBase 启用多个 HMaster,并通过 Zookeeper 管理(需选出一个主服务器)。
      • 通过 Zookeeper 获得主 HMaster 地址,输入 Key 值获得所在 HRegionServer 地址,最后请求 HRegionServer 上的 HRegion 获得数据。

07 网站可扩展架构

  • 扩展性与伸缩性的误用

    • 扩展性(Extensibility):软件层面,基础设施稳定不需要经常变更,应用之间较少依赖耦和耦合,对需求变更可敏捷响应。
    • 伸缩性(Scalability):硬件层面,能够通过增加或减少自身资源规模的方式增强或减少自己计算处理事务的能力。

      在网站架构设计中,通常指利用集群方式增加服务器数量,以提高系统的整体事务吞吐能力。

  • 网站可扩展性架构的核心思想是模块化,在此基础上降低模块间的耦合性,提高模块的复用性。

  • 利用分层和分割方式将应用系统分割为若干个低耦合的独立组件模块,组件模块间通过消息传递及依赖调用方式聚合成一个完整系统。模块分布式部署后具体聚合方式有 分布式消息队列分布式服务

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

通过消息对象来降低系统耦合性。

  • 事件驱动架构(Event Driven Architecture):在低耦合的模块之间传输事件消息,并借助事件消息的通信完成模块间合作。常用实现手段是分布式消息队列。

    典型 EDA 架构就是生产者-消费者模式。

  • 分布式消息队列:发布-订阅模式,类似 “队列” 的数据结构,先进先出(FIFO)对消息进行存取操作;应用程序通过远程访问接口实现分布式的异步调用。

    常见的消息队列产品有 Kafka、RabbitMQ 等,理论上使用数据库也可以实现分布式消息队列的,消息生产者程序将消息记录写入库并为记录附上时间戳。

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

通过系统接口来降低系统耦合性。

  • 巨无霸应用系统困境

    • 新增业务困难
    • 编译部署困难
    • 数据库连接耗尽
    • 代码分支管理困难
  • 解决方案:拆分模块,独立部署

    • 纵向拆分:大应用拆成小应用。
    • 横向拆分:可复用的业务独立部署为分布式服务。

可扩展数据结构

  • 问题:传统关系数据库为保证关系运算的正确性,则需要指定表的字段及数据类型,并遵循特定的范式要求设计数据表。僵硬的数据结构难以面对需求变更带来的挑战,虽可通过冗余字段来应对变化,但糟糕的数据库设计总会有不灵之时。
  • 解决:如何做到可扩展的数据结构设计,无需修改表结构扩展数据字段?

    NoSQL 数据库:使用列簇设计(ColumnFamily),一种面向列簇的稀疏矩阵存储格式。它在创建表时,只需指定 ColumnFamily 的名字即可,无须指定字段(Column),在写入数据时再指定。

08 网站的安全架构

网站应用攻击与防御

  • XSS 攻击:跨站点脚本攻击(Cross Site Script),篡改网页注入 HTML 脚本,在用户浏览网页时控制用户浏览器进行恶意操作。

    • 反射性:攻击者诱使用户点击一个嵌入恶意脚本的链接,达到攻击目的。
    • 持久性:攻击者提高含有恶意脚本的请求,保存在被攻击的 Web 站点的数据库中,用户浏览网页时,恶意脚本包含在正常的内容页上。
    • 消毒手段
      • 对 HTML 危险字符转义,比如 > 转换为 &gt;< 转换为 &lt;
      • 浏览器禁止页面 JavaScript 访问带有 HttpOnly 属性的 Cookie。
  • 注入攻击

    • SQL 注入:在 HTTP 请求中注入恶意 SQL,服务器用请求参数构造数据库 SQL 时被一起构造并执行。

      注:关注错误回显功能,即服务器内部 500 错误回显,容易暴露数据库表结构。

    • OS 注入:OS 命令

    • 消毒手段:SQL 预编译和参数绑定
  • CSRF 攻击:跨站点请求伪造(Cross Site Request Forgery),核心是利用了浏览器 Cookie 或者服务器 Session 策略,盗取用户身份进行跨站请求、非法操作。常见防范手段:

    • Token 校验:每次请求都需附带时效性 Token 发起请求,且通过注册机制获得 Token。
    • 验证码校验:人机交互方式。
  • 错误码: 服务端未处理异常堆栈信息,而是直接输出客户端浏览器,该方式虽然对调试和错误报告友好但对攻击者提供了可乘之机。

    拦截异常要处理、对错误码进行拦截处理。

  • 文件上传:建立文件白名单机制,限制文件类型。

  • 路径遍历:在请求 URL 中使用相对路径,遍历系统未开放的目录和文件。比如 ../

信息加密及密钥管理

  • 单向散列加密:输入不同长度的信息进行散列计算,得到固定长度的输出,具有不可逆性。比如 MD5、SHA 算法。

    • 对密码进行单向散列加密,密码密文存入数据库;
    • 密码验证时,同样对密码明文散列计算,与数据库比对。
  • 对称加密:加密、解密使用同一密钥,比如 DES、RC、SM4 算法。

  • 非对称加密:使用两个密钥来进行加密和解密,一个是对外公开的公钥(Public key)和私钥(Private key)。比如 RSA、ECC、SM2 算法。

  • 密钥安全管理

    • 保证线上线下环境配置不同的密钥,密钥写在配置文件中,且使用密文形式保存。
    • 加解密算法放在应用系统,密钥存放于独立服务器,且密钥实际存储时被切割为数片,加密存储于不同存储介质上。