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

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

文章目录

  • 《大型网站技术架构:核心原理与案例分析》读书笔记
    • 1.大型网站架构演化
      • 1.1 大型网站软件系统的特点
      • 1.2 大型网站架构沿海发展历程
    • 2.大型网站架构模式
      • 2.1网站架构模式
    • 3.大型网站核心架构要素
    • 4.瞬时响应:网站的高性能架构
      • 4.1 网站性能测试
      • 4.2 Web前端性能优化
      • 4.3 应用服务器性能优化
      • 4.4 存储性能优化
    • 5. 万无一失:网站的高可用架构
      • 5.1 网站可用性的度量和考核
      • 5.2 高可用的网站架构
      • 5.3 高可用的应用
      • 5.4 高可用的服务
      • 5.5 高可用的数据
      • 5.6 高可用网站的软件质量保证
      • 5.7 网站运行监控
    • 6.永无止境:网站的伸缩性架构
      • 6.1 网站架构的伸缩性设计
      • 6.2 应用服务器集群的伸缩性设计
      • 6.3 分布式缓存集群的伸缩性设计
      • 6.4数据存储服务器集群的伸缩性
    • 7.随需应变:网站的可扩展架构
      • 7.1 构建可扩展的网站架构
      • 7.2 利用分布式消息降低系统耦合性
      • 7.3 利用分布式服务打造可复用的业务平台
      • 7.4 可扩展的数据结构
      • 7.5 利用开放平台建设网站生态圈
    • 8. 固若金汤:网站的安全架构
      • 8.1 道高一尺魔高一丈的网站应用攻击与防御
      • 8.2 信息加密技术及秘钥安全管理
      • 8.3 信息过滤与反垃圾
      • 8.4 电子商务风险控制
    • 11.海量分布式存储系统Doris的可高可用架构设计分析
      • 11.1 分布式存储系统的高可用架构
      • 11.2 不同故障情况下的高可用解决方案
    • 12 网购秒杀系统架构设计案例分析
      • 12.1 秒杀活动的技术挑战
      • 12.2 秒杀系统的应对策略
      • 12.3 秒杀系统架构设计
    • 13 大型网站典型故障案例分析
      • 13.1 写日志也会引发故障
      • 13.2 高并发访问数据库引发的故障
      • 13.3 高并发情况下锁引发的故障
      • 13.4 缓存引发的故障
      • 13.5 应用启动不同引发的故障
      • 13.6 大文件读写独占磁盘引发的故障
      • 13.7 滥用生产环境引发的故障
      • 13.8 不规范的流程引发的故障
      • 13.9 不好的编程习惯引发的故障

1.大型网站架构演化

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

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

1.2 大型网站架构沿海发展历程

初始阶段的网站架构:

应用程序,数据库,文件等所有资源都在一台服务器上,通常汇集各种免费开源软件,LAMP

应用服务和数据服务分离:

三台服务器:应用服务器,文件服务器,数据库服务器

三台服务器硬件资源配置要求:

应用服务器:需要处理大量业务逻辑=》更快更强大的CPU

数据库服务器:需要快速磁盘检索和数据缓存=》更快的硬盘和更大的内存

文件服务器:需要存储大量用户上传的文件=》更大的硬盘

使用缓存改善网站性能

网站访问特点遵循二八定律:80%业务访问集中再20%数据上。

将访问量多的数据缓存再内存中,可减少数据库压力,提高数据访问速度。

网站缓存类别:

  • 本地缓存,缓存在应用服务器上,访问速度更快一些,但是受应用服务器内存限制,其缓存数据量优先,而且会出现和应用程序征用内存的情况。
  • 远程缓存,缓存在专门的分布式缓存服务器上,可采用集群的方式,部署大内存的服务器作为专门的缓存服务器,可以在理论上左到不受内存容量限制的缓存服务。

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

增加一台服务器改善负载压力,同负载均衡调度服务器,可将来自用户浏览器的访问请求发到应用服务器集群中的任何一台服务器。

数据库读写分离

大部分主流数据库提供主从热备功能,通过配置两台数据库主从关系,可以将一台数据库服务器更新同步到另一台服务器上。

应用服务在写数据时,范文主数据库,主数据库通过主从复制机制将数据更新同步到从数据库。当应用服务器读数据时,可通过从数据库获得数据。

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

CDN和反向代理的基本原理都是缓存,区别在于:

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

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

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

使用NoSQL和搜索引擎

应用服务器通过一个同意数据模块范文各种数据,减轻应用程序管理诸多数据源的麻烦。

业务拆分

通过使用分而治之的手段将整个网站业务分成不同的产品线,分归不同业务团队负责。也会根据产品产品线划分,将网站拆分成许多不同的应用,每个应用独立部署维护,应用之间可通过超链接建立关系,也可通过消息队列进行数据分发,最多还是通过访问同一个数据存储系统来构造一个关联的完整系统

分布式服务

云计算平台,将计算作为一种基础资源出售。

2.大型网站架构模式

2.1网站架构模式

分层

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

细分层

应用层:视图层(美工负责),业务逻辑层(工程师负责)

服务层:数据接口层(适配各种输入和输出的数据格式),逻辑处理层

分割

分层式将软件在横向方面的切分,分割时在纵向方面对软件进行切分

将不同功能和服务分割开来,包装成高内聚低耦合的模块单元,一方面有助于按软件的开发和维护,另一方面,便于不同模块的分布式部署,提高网站的并发处理能力和功能扩展能力。

分布式

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

常用的分布式方案:

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

集群

将独立部署的服务器集群化,即将多台服务器部署相同应用,构成一个集群,通过负载均衡设备共同对外提供服务。

缓存

  • CDN:内容分发网络,部署在距离终端最近的网络服务商,用户的网络请求总是先到达他的网络服务商那里,在这里缓存网站的一些静态资源(较少变化的数据),可以就近以最快速度返回给用户。
  • 反向代理:反向代理属于网站前端架构的一部分,部署在网站的前端,当用户请求到达网站的数据中心时,最先访问到的时反向代理服务器,这里缓存网站的静态资源,无需将请求继续转发给应用服务器就能返回给用户。
  • 本地缓存:在应用服务器本地缓存热点数据,应用程序可以在本机内存中直接访问数据,而无需访问数据库。
  • 分布式缓存:大型网站的数据量非常庞大,及时只缓存一小部分,需要的内存空间也不是单机能承受的,所以除了本地缓存,还需要分布式缓存,将数据缓存在一个专门的分布式缓存群集中,应用程序通过网络通讯访问缓存数据。

使用缓存的两个前提条件:

  • 数据访问热点不均衡,某些数据会被更频繁地访问,这些数据应该放在缓存中
  • 数据在某个时间段内有效,不会很快国企,否则缓存的数据因已经失效而产生脏读,影响结果的准确性。

异步

在单一的服务器内部可通过多线程共享内存队列的方式实现异步,处在业务操作前面的现场将输出写入队列,后面的线程从队列中读取数据进行处理。

在分布式系统中,多个服务器集群通过分布式消息队列实现异步,分布式消息队列可以看作内存队列的分布式部署。

异步架构时典型的生产者消费者模式,两者不存在直接调用,只要保持数据结构不变,批次功能实现可以随意变换而不互相影响,对网站扩展新功能非常便利。

异步消息队列:

  • 提高系统可用性
  • 加快网站响应速度
  • 消除并发访问高峰

冗余

在服务器宕机的情况下网站依然可以继续服务,不丢失数据,就需要一定程度的服务器冗余运行,数据冗余备份,当某台服务器宕机时,可将其上的服务和数据访问转移到其他机器上。

数据库除了定期备份,存档保持,实现冷备份外,为了保证在线业务高可用,还需对数据库进行主从分离,实时同步实现热备份。为了抵御天灾导致网站完全瘫痪,某些大型网站还会对整个数据中心进行备份,全球范围部署灾备数据中心,网站程序和数据实时同步到多个灾备数据中心。

自动化

发布运维:

  • 发布过程自动化
  • 自动化代码管理
  • 自动化测试
  • 自动化安全测试
  • 自动部署
  • 自动化监控
  • 自动化报警
  • 自动化报警
  • 自动化失效转移
  • 自动化失效恢复
  • 自动化分配资源

安全

  • 密码

  • 手机验证码

  • 加密

  • 验证码

  • 攻击

  • 过滤

  • 风险控制

3.大型网站核心架构要素

  • 性能
  • 可用性
  • 伸缩性
  • 扩展性
  • 安全性

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

4.1 网站性能测试

系统操作响应时间表

操作 响应时间
打开一个网站 几秒
在数据库查询一条记录(有索引) 十几毫秒
机械磁盘一次寻址定位 4毫秒
从机械磁盘顺序读取1MB数据 2毫秒
从SSD磁盘顺序读取1MB数据 0.3毫秒
从远程分布式缓存Redis读取一个数据 0.5毫秒
从内存中读取1MB数据 十几微秒
Java程序本地方法调用 几微秒
网络传输2KB数据 1微秒

并发数

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

吞吐量

单位时间内系统处理的请求数量,体现系统的整体处理能力。

单位:请求数/秒、页面数/秒、访问人数/天、处理的业务数/小时

TPS:每秒事务数

HPS:每秒HTTP请求数

QPS:每秒查询数

性能计数器

  • System Load:系统负载,指当前正在被CPU执行的进程数目总和,是反映系统忙闲成都的重要指标。
  • 对象与线程数
  • 内存使用
  • CPU使用
  • 磁盘与网络I/O

性能测试方法

  • 性能测试
  • 负载测试
  • 压力测试
  • 稳定性测试

4.2 Web前端性能优化

浏览器访问优化

  • 减少http请求:合并css,合并JavaScript,合并图片。
  • 使用浏览器缓存:设置HTTP Header中Cache-Control和Expires属性,对css,js,lo,图标等静态资源文件进行缓存,缓存时间可以是数天甚至几个月。批量更新静态资源时需足部更新,有一定时间间隔,一面用户浏览器突然大量缓存失效,造成服务器负载突增。
  • 启用压缩:在服务器端对文件进行压缩,在浏览器端对文件解压。
  • css放在页面最上面,js放在页面最下面:页面下载完css后进行渲染,css在最上面尽快渲染,然后进行js加载。
  • 减少Cookie传输:cookie包含在每次请求和响应中,写入cookie中的数据要慎重,一面数据传输。对于静态资源访问,发送cookie无意义,可考虑静态资源独立域名访问,避免请求时发送cookie。

CDN加速

CDN(Content Distribute Network,内容分发网络)本之仍是一个缓存,而且将数据缓存在离用户最近的地方,使用户以最快速度获取数据,即所谓的网络访问第一跳。

CDN部署在网络运营商机房,用户请求路由的第一跳就到达CDN服务器,当CDN中存在浏览器请求的资源时,从CDN直接返回给浏览器,最短路径返回响应,减少用户访问速度,减少数据中心负载压力。

CDN一般缓存静态资源,如图片,文件,css,js,静态网页等,这些文件访问频率高,将其缓存在CDN可极大改善网页的打开速度。

反向代理

传统代理服务器位于浏览器一侧,代理浏览器将HTTP请求发送到互联网上,而反向代理服务器位于网站机房一侧,代理网站web服务器接收http请求。

反向代理服务器具有保护网站安全的作用,来自互联网的访问请求必须经过代理服务器,相当于在web服务器和可能的网络攻击之间建立一个屏障。

代理服务器可通过配置缓存功能加速web请求。当用户第一次访问静态内容的时候,静态内容被缓存在反向代理服务器上,当其他用户访问该静态内容时就可直接从反向代理服务器返回,加速web请求响应速度,减轻web服务器负载压力。事实上,有些网站会把热门访问的动态内容缓存,当这些动态内容有变换时,通过内部通知机制通知反向代理缓存失效,反向代理会重新加载最新的动态内容再次缓存起来。

反向代理可实现负载均衡功能,通过负载均衡构建的应用集群可提高系统总体处理能力,进而改善网站并发情况下的性能。

4.3 应用服务器性能优化

分布式缓存

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

(1)缓存的基本原理:缓存指将数据存储在相对高访问速度的存储介质中,以供系统处理,一方面缓存访问速度快,可以减少数据访问时间,另一方面如果缓存的数据经过计算处理得到的,那么被缓存的数据无需重复计算即可直接使用,减少计算时间。

缓存的本质时一个内存Hash表,网站应用中,数据缓存以一对key、value的形式存储在内存Hash表中。Hash表数据读写的时间复杂度位O(1);

(2)合理使用缓存:

  • 频繁修改的数据:数据的读写比在2:1以上,即写入一次缓存,在数据更新前至少读取2次以上,缓存才有意义。
  • 没有热点的访问:若应用系统访问数没有热点,不遵循二八定律,那么缓存就没有意义,因大部分数据还没被再次访问就被挤出缓存了。
  • 数据不一致与脏读:一般对缓存的数据设置失效时间,一旦超过失效时间,就从数据库中重新加载,应用要容忍一定时间的数据不一致,这种延迟时可以被接受的,但具体应用需慎重对待。
  • 缓存可用性:缓存时为了提高数据读取性能,缓存数据对视或者缓存不可用不会影响到应用程序的处理,它可以从数据库直接获取数据,但随者业务发展,缓存会承担大部分数据访问的压力,当缓存服务崩溃时,数据库会承受不住压力而宕机。通过分布式缓存服务集群,将缓存数据分布到集群多台服务器上可在一定程度上改善缓存性能。当一台缓存服务器宕机的时候,只有部分缓存数据丢失,重新从数据库加载这部分数据不会对数据库产生很大影响。
  • 缓存预热:缓存中存放的是热点数据,热点数据又是缓存系统利用LRU(最近醉酒未用算法)。新启动的缓存系统,在重建缓存数据过程中加载好热点数据。
  • 缓存穿透:如果因为不恰当的业务或恶意攻击,持续高并发请求某个不存在的数据,由于缓存没有保存该数据,所有请求会落在数据库上,会对数据库造成很大压力,甚至崩溃。对策是将不存在的数据缓存起来,其值为null

(3)分布式缓存架构:分布式缓存指缓存部署在多个服务器组成的集群中,以集群方式提供缓存服务,其架构方式有两种,一种以JBoss Cache为代表的需要风险同步的分布式缓存,一种以Memcached为代表不互相通信的分布式缓存。

  • JBoss Cache:在集群中所有服务器中保存相同的缓存数据,当某台服务器有缓存数据更新时,会通知集群中其他机器更新缓存数据或清除缓存数据。多见于企业应用系统中,很少在大型网站中使用。

  • Memcached:采用一种集中式的缓存集群管理,互不通信的分布式架构方式,缓存与应用分离部署,缓存系统部署在一组专门的服务器上,应用程序通过一致性Hash等路由算法选择缓存服务器远程访问缓存数据,缓存服务器之间不通信,缓存集群的规模可以很容易地实现扩容,具有莱昂和的可伸缩性。

  • Memcached:被大量网站使用

  • 简单的通信协议:Memcached使用TCP协议(UDP也支持)通信,其序列号协议则是一套基于文本的自定义协议,非常简单,一个命令关键词开头,后面是一组命令操作数。

  • 丰富的客户端程序:只要支持该协议的客户端都可以和Memcached服务通信。

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

  • 高效的内存管理:内存碎片管理对策是固定空间分配。

  • 互不通信的服务器集群架构:客户端路由算法一致性Hash称为数据存储伸缩性架构设计的经典凡是。集群内服务器互不通信使得集群可以做到几乎无限制的线性伸缩。

异步操作
使用消息队列将调用异步化,改善网站扩展性和系统的性能。
在使用消息队列后,用户请求的数据发送给消息队列后立即返回,再由消息队列的消费者进出(通常情况下,该进程通常独立部署再专门的服务器集群上)从消息队列中获取数据,异步写入数据库。由于消息队列服务器处理速度远快于数据库(消息队列服务器也比数据库具有更好的伸缩性),因此用户的响应延迟可以得到有效改善。
消息队列具有很好的削峰作用,即通过异步处理,将短时间高并发产生的事务消息存储在消息队列中,从而削平高峰期的并发事务。由于数据写入消息队列后立即返回给用户,数据在后续的业务校验、写入数据库操作可能失败,因此在使用消息队列进行业务异步处理后,需要适当修改业务流程进行配合。

使用集群
在网站高并发访问的场景下,使用负载均衡技术为一个应用构建一个由多台服务器组成的服务器集群,将并发访问请求分发到多台服务器上处理,避免单一服务器因负载压力过大而响应缓慢,使用用户请求具有更好的响应延迟特性。

代码优化
(1) 多线程:使用多线程的两个主要原因:IO阻塞与多CPU。web容器管理服务器的象成任务启用线程数=[任务执行时间/(任务执行时间-IO等待时间)]*CPU内核数。最佳启动线程数和CPU内核数量成正比,和IO阻塞事件成反比。多线程安全主要手段:

  • 将对象设计为无状态对象:无状态对象指对象本身不存储状态信息(对象无成员变量,或成员变量也无状态对象)。servlet对象的设计为无状态对象,可悲应用服务器多线程并发调用处理用户请求。
  • 使用局部对象:在方法内部创建对象,该对象会被每个进入该方法的线程创建。
  • 并发访问资源时使用锁:多线程访问资源时,通过锁的方法使多线程操作转化为顺序操作,从而避免资源被并发修改。

(2) 资源复用:资源复用主要有两种模式:单例和对象池。
(3) 数据结构:Hash表的基本原理,Hash表单读写性能很大程度上依赖HashCode的随机性,即HashCode越随机散列,Hash表的冲突越小,读写性能也越高。
(4) 垃圾回收

4.4 存储性能优化

机械硬盘 —— 固态硬盘

**B+树 —— LSM树 **

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

LSM树:一个N阶合并树,数据写操作都在内存中进行,并且都会创建一个新纪录,这些数据在内存中仍然还是一棵排序树,当数据量超过设定的内存阈值后,会将这棵排序树和磁盘上最新的排序树合并。当这棵排序树的数据量超过设定阈值后,和磁盘上下一级的排序树合并。合并过程中,会用最新的数据覆盖旧数据。

在LSM树上进行一次数据更新不需要磁盘访问,在内存即可完成,速度远快于B+树。当数据访问以写操作为主,而读操作则集中在最近写入的数据上时,使用LSM树可以极大程度地减少磁盘访问次数,加快访问速度。

许多NoSQL产品采用LSM树作为主要数据结构。B+树不是关系数据库所独有的,NoSQL数据库也可使用B+树,同理关系数据库也可使用LSM。

RAID——HDFS

RAID:(廉价磁盘冗余阵列)技术主要为了改善磁盘的访问延迟,增强磁盘的可用性和容错能力。目前服务器级别的计算机都支持插入多块磁盘(8块或更多),通过使用RAID技术,实现数据在多块磁盘上的并发读写和数据备份。

RAID类型 访问速度 数据可靠性 磁盘利用率
RAID0 很快 很低 100%
RAID1 很慢 很高 50%
RAID10 中等 很高 50%
RAID5 较快 较高 (N-1)/N
RAID6 较快 较(RAID5)高 (N-1)/N

HDFS:(Hadoop分布式文件系统)中,系统在整个存储集群的多台服务器上进行数据并发读写和备份,可看作在服务器集群规模上实现类似RAID的功能。HDFS以块为单位管理文件内容,一个文件被分割成若干个块,当应用程序写文本时,没写完一个块,HDFS就将其自动赋值到另外两台机器上,保证每个块有三个副本,即使有两台服务宕机,数据依然可以访问,相当于实现RAID1大数据复制功能

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

5.1 网站可用性的度量和考核

网站可用性度量

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

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

网站可用性考核

分类 描述 权重
事故级故障 严重故障,网站整体不可用 100
A类故障 网站访问不顺畅和核心功能不可用 20
B类故障 非核心功能不可用,或核心功能少数用户不可用 5
C类故障 以上故障以外的其他故障 1

故障分=故障时间(分钟)*故障权重、

5.2 高可用的网站架构

网站的高可用架构设计的主要目的:保证服务器硬件故障时服务依然可用,数据依然保存并能被访问。主要手段:数据和服务的冗余备份及失效转移。

分层模型三层:应用层,服务层,数据层。各层之间具有相对独立性,应用层主要负责具体业务逻辑处理,服务层负责提供可复用的服务,数据层负责数据存储与访问。

中小型网站在具体部署时,通常将应用层和服务层部署在一起,而数据层则另外部署。在复杂的大型网站架构中,划分的粒度会更小,更详细,结构更加复杂,服务器规模更加庞大,但通常还是能够把这些服务器划分到这三层中。

位于应用层的服务器:通常时为了应对高并发的访问请求,通过负载均衡设备将一组服务器组成一个集群共同对外提供服务,当负载均衡设备通过心跳检测等手段监控到某台应用服务器不可用时,就将其从集群列表中剔除,并将请求分发到集群中其他可用的服务器上,使整个集群保持可用,从而实现应用高可用。

位于服务层的服务器:通过集群方式实现高可用,这些服务器被应用层通过分布式服务调用框架访问,分布式服务调用框架会在应用层客户端程序中实现软件负载均衡,并通过服务注册中心提供服务的服务器进行心跳检测,发现有服务不可用,立即通知客户端程序修改服务访问列表,剔除不可用的服务器。

位于数据层的服务器:数据服务器上存储着数据,为了保证服务器宕机时数据不丢失,数据访问服务不中断,需要在数据写入时进行数据同步复制,将数据写入多台服务器上,实现数据冗余备份。当数据服务器宕机时,应用程序将访问切换到备份的服务器上。

网站升级的频率一般都非常高,大型网站一周发布一次,中小型网站一天发布几次。

5.3 高可用的应用

无状态的应用是指应用服务器不保存业务的上下文信息,而仅根据每次每次请求提交数据进行相应的业务逻辑处理,多个服务实例(服务器)之间完成对等,请求提交到任意服务器,处理结果都是完全一样的。

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

对于应用服务器集群,实现这种服务器可用状态实时检测、自动转移失败任务的机制时负载均衡。

负载均衡:主要使用在业务量和数据量较高的情况下,当单服务器不足以承担所有的负载压力时,通过负载均衡手段,将流量和数据分摊到一个集群组成的多台服务器上,以提高整体的负载处理能力。

应用服务器集群Session管理

Web应用中将这些多次请求修改使用的上下文对象乘坐会话,单机情况下,Session可由部署在服务器上Web容器管理。使用负载均衡的集群环境中,由于负载均衡服务器可能会将请求分发到集群任何一台应用服务器上,所以保证每次请求依然能够获得正确的Session比单机时要复杂很多。

Session管理手段:

(1)Session复制

应用服务器开启Web容器的Session复制功能,在集群中的几台服务器之间同步Session对象,使得每台服务器上都保持所有用户的Session信息,这样任何一台辑器宕机都不会导致Session数据的丢失,而服务器使用Session时,也只需要在本机获取即可。不适用于大型网站。

(2)Session绑定

Session绑定可以利用负载均衡的源地址Hash算法实现,负载均衡服务器总是将来源于同一IP的请求分发到同一台服务器上。(也可根据Cookie信息将同一用户的请求总分发到同一台服务器上,这时负载均衡服务器必须工作在HTTP协议层上)整个会话期间,用户所有的请求都在同一台服务器上处理,即Session绑定在某台特定服务器上,保证Session总能在这台服务器上获取,这方法称作会话黏滞。

(3)利用Cookie记录Session

(4)Session服务器

利用独立部署的Session服务器集群统一管理Session,应用服务器每次读写Session时,都访问SessionSession服务器。

将应用服务器的状态分离,分为无状态的应用服务器和有状态Session服务器,然后针对这两种服务器的不同特性分别设计其架构。

5.4 高可用的服务

可复用的服务模块为业务产品提供基础公共服务,大型网站中这些服务都独立分布部署,被具体应用远程调用,是无状态的服务。

高可用的服务策略:

(1)分级管理

运维上将服务器进行分级管理,核心应用和服务优先使用更好的硬件,在运维响应速度上也格外迅速。同时在服务部署上进行必要的隔离,避免故障的连锁反应。低优先级的服务通过启动不同的线程或者部署在不同的虚拟机上进行隔离,而高优先级的服务器则需要部署在不同的物理机上,核心服务和数据甚至需要部署在不同区域的数据中心。

(2)超时设置

在应用程序中设置服务调用的超时时间,一旦超时,通信框架就抛出异常,应用程序根据服务调度策略,可选择继续重试或将请求转移到提供相同服务的其他服务器上。

(3)异步调用

应用对服务的调用通过消息队列等异步方式完成,避免一个服务失败导致整个应用请求失败的情况。

(4)服务降级

在网站访问高峰期,为保证核心应用和功能的正常运行,需对服务进行降级:

拒绝服务:拒绝低优先级应用的调用,减少服务调用并发数,确保核心应用正常使用。随机拒绝访问请求调用,节约资源,让另一部分请求得以成功,避免全部无法响应服务。

关闭功能:关闭部分不重要的服务,或者服务内部关闭部分不重要的功能,以节约系统开销,为重要的服务和功能让出资源。

(5)幂等性设计

在服务层保证服务重复调用和调用一次产生的结果相同即服务具有幂等性,避免虚假失败。

5.5 高可用的数据

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

数据备份是保证数据有多个副本,任意副本的失效都不会导致数据的永久丢失,从而实现数据完全的持久化。

失效转移机制则保证当一个数据副本不可访问时,可以快速切换访问数据的其他副本,保证系统可用。

CAP原理

为保证数据的高可用,网站通常会牺牲另一个很重要的指标:数据一致性。

高可用的数据:数据持久性,数据可访问性,数据一致性。

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

数据一致性分点:

(1)数据强一致:各个副本的数据在物理存储中总是一致的,数据更新操作结果和操作响应总是一致的,即操作响应通知更新失败,那么数据移动没有被更新,而不是处于不确定状态。

(2)数据用户一致:即数据在物理存储中的各个副本的数据可能是不一致的,但是终端用户访问时,通过纠错和检验机制,可以确定一个一致的且正确的数据返回给用户。

(3)数据最终一致:这是数据一致性中较弱的一种,即物理存储的数据可能是不一致的,终端用户访问到的数据可能也是不一致的,但系统经过一段时间的自我回复和修正,数据最终会达到一致。

数据备份

冷备份:定期将数据复制到某种存储价值上并无里存档保管,如果系统存储损害,那么就从冷备份的存储设备中恢复数据,不能保证数据最终一致,不能保证数据的可用性。

热备份:

(1)异步热备方式

异步热备是指多份数据副本的写入操作异步完成,应用程序收到数据服务系统的写操作成功响应时,只写成功了一份,存储系统将会异步地写其他副本。

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

(2)同步热备方式

同步方式是指多份数据副本的写入擦着同步完成,即应用程序收到数据服务系统的写成功响应时,多份数据都已经写操作成功。但是当应用程序收到数据数据写操作失败响应时,可能有部分副本或者全部副本都已经写成功了。

同步热备具体实现的时候,为了提高性能,在应用程序客户端并发向多个存储服务器同时写入数据,然后等待所有存储服务器 都返回操作成功的响应后,再通知应用程序写操作成功。

失效转移

若数据服务器集群中任何一台服务器宕机没那么应用程序针对这台服务器的所有读写操作都需要重新路由到其他服务器,保证数据访问不会失败,这个过程叫做失效转移。

(1)失效确认

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

(2)访问转移

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

(3)数据恢复

系统需要从讲课的服务器复制数据,将数据副本数目恢复制到设定值。

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

网站发布

通常使用发布脚本完成发布:

(1)关闭负载均衡服务器上一台或一小批服务路由

(2)关闭这台(些)服务器应用

(3)同步(复制)软件代码包到这台(些)服务器

(4)启动这台(些)服务器

(5)打开负载均衡服务器上这台(些)服务器的路由

(6)集群所有辑器发布完成?若没有则重复1操作。

发布过程中,每次关闭服务器都是集群中的一小部分,并在发布完成后立即可以访问,因此整个发布过程不影响用户使用。

自动化测试

回归测试

web自动化测试技术,使用自动测试工具或脚本完成测试。

预发布验证

代码控制

(1)主干开发、分支发布

(2)分支开发,主干发布

自动化发布

对于有固定发布日期的网站(很多网站选择周四作为发布日,前三天准备发布,还有一天可以晚会错误,以免周末加班)

火车发布模型:将每个应用的发布过程看作一次火车旅程,火车定点运行,期间有若干站点,每一站都进行例行检查,不通过的项目下册,剩下的项目继续坐在火车旅行,直到火车到达终点(应用发布成功)。但实际中,有可能所有项目都下车了,开着空车前进是没有意义的,火车不得不回到起点,等待解决问题再重来一次,还有可能是车上有达官贵人(终点项目,CEO跟投资人拍胸脯的项目),它不上车,谁也别想走,它出了错,大家跟着他回去重来。

网站火车发布模型:

(1)提交发布请求

(2)QA确认,安全确认,DBA确认,若确认失败,项目退出发布,完善后重新发布,执行(1)操作。

(3)代码合并,若合并失败,项目退出发布,完善后重新发布,执行(1)操作。

(4)预发布验证,若验证失败,项目退出发布,完善后重新发布,执行(1)操作。

(5)正式发布

火车发布模型是基于规则驱动的流程,可采用自动化发布工具实现发布流程自动化。

灰度发布

发布成功后仍然可能发现因为软件问题而引入的故障,这时需发布回滚,即卸载刚发布的软件,讲上一个版本的软件包重新发布,使系统复原,消除故障。

大型网站会使用灰度发布模式,讲集群服务器分成若干部分,每天只发布一部分服务器,观察运行稳定没有障碍,第二天继续发布一部分服务器,持续几天才把整个集群全部发布完毕,期间若发现问题,只需回滚已发布的一部分服务器即可。

灰度发布常用语用户测试,记载部分服务器上发布新版本,其余服务器保持老版本(或者发布另一版本),然后监控用户操作行为,收集用户体验报告,比较用户对两个版本的满意度,以确定最终发布版本,这种手段也称AB测试。

5.7 网站运行监控

监控数据采集

(1)用户行为日志收集:服务器端日志收集,客户浏览器日志收集

(2)服务器性能监控

(3)运行数据报告

监控管理

(1)系统报警

(2)失效转移

(3)自动优雅降级

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

6.1 网站架构的伸缩性设计

网站的伸缩性设计可分成两类:

(1)根据功能进行物理分离实现伸缩,不同的服务器部署不同服务,提供不同功能

(2)单一功能通过集群实现伸缩,集群内多台服务器部署相同的服务,提供相同的功能

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

(1)单一服务器处理所有服务

(2)数据库从应用服务器分离

(3)缓存从应用服务器分离

(4)静态资源从应用服务器分离

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

(1)网站具体产品

(2)可复用业务服务

(3)基础技术服务

(4)数据库

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

(1)交易论坛

(2)买家后台

(3)卖家后台

(4)网站前台

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

集群伸缩性可分为应用服务器集群伸缩性和数据服务器集群伸缩性。

数据服务器集群可分为缓存数据服务器集群和存储数据服务器集群。

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

HTTP重定向负载均衡

HTTP重定向服务器是一台普通的应用服务器,其唯一的功能就是根据用户的HTTP请求计算一台真实的Web服务器地址,并将该Web服务器地址写入HTTP重定向响应中(响应状态码302)返回给用户浏览器。

负载均衡方案优点比较简单,缺点是浏览器需要两次请求服务器才能完成一次访问性能较差,重定向服务器自身的处理能力有可能成为瓶颈,整个集群的伸缩性规模有限;使用HTTP302响应码重定向,有可能使搜索引擎判断为SEO作弊,降低搜索排名。因此实践中使用这种方案进行负载均衡的案例并不多。

DNS域名解析负载均衡

在DNS服务器中配置多个A记录,每次域名解析请求都会根据负载均衡算法计算一个不同的IP地址返回,A记录中配置的多个服务器就构成一个集群,并可以实现负载均衡。

DNS域名解析负载均衡的优点是将负载均衡的工作转交给DNS,省掉了网站管理维护负载均衡服务器的麻烦,同时许多DNS还支持基于地理位置的域名解析,即会将域名解析成距离用户地理最近的一个服务器地址,这样可以加快用户访问速度,改善性能。

DNS域名解析负载均衡缺点:目前的DNS是多级解析,每一级DNS都可能缓存A记录,当下线某台服务器后,即使修改DNS的A记录,要使其生效也需要较长时间,这段时间,DNS依然会将域名解析到已经下线的服务器,导致用户访问失败;而且DNS负载均衡的控制权在域名服务商那里,网站无法对其做更多改善和更强大的管理。

事实上,大型网站总是部分使用DNS域名解析,利用域名解析作为第一级负载均衡手段,即域名解析得到的一组服务器并不是实际提供Web服务的物理服务器,而是同样提供负载均衡服务的内部服务器,这组内部敷在混合服务器再进行负载均衡,将请求分发到真实的Web服务器上。

反向代理负载均衡

在部署位置上,反向代理服务器处于Web服务器前面,这个位置整合是负载均衡服务器的位置,所以大多数反向代理服务器同时提供负载均衡的功能,管理一组Web服务器,将请求根据负载均衡算法转发到不同Web服务器上。Web服务器处理完成的响应也需要通过反向代理服务器返回给用户。由于Web服务器不直接对外提供访问,因此Web服务器不需要使用外部IP地址,而反向代理服务器则需要配置双网卡和内部外部两台IP地址。

由于方向代理服务器转发请求在HTTP协议层面,因此也叫应用层负载均衡,优点和方向代理服务器功能集成在一起,部署简单,缺点是反向代理服务器是左右请求和响应的中转站,性能可能会成为瓶颈。

IP负载均衡

真实物理Web服务器响应数据包如何返还给负载均衡服务器:

一种方案:负载均衡服务器在修改目的IP地址的同时修改源地址,将数据包源地址设为自身IP,即原地址转换(SNAT),这样Web服务器的响应会再回到负载均衡服务器。

另一种方案:将负载均衡服务器同时作为真实物理服务器集群的网关服务器,这样所有响应数据都会到达负载均衡服务器。

IP负载均衡在内核进场完成数据分发,较反向代理负载均衡(在应用程序中分发数据)有更好的处理性能。但由于所有请求响应都需要经过负载均衡服务器,集群的最大响应数据吞吐量不得不受制于负载均衡服务器网卡带宽。对于提供下载服务或者视频服务等需要传输大量数据的网站而言,难以满足需求。

数据链路层负载均衡

数据链路层负载均衡是指在通信协议的数据链路层修改mac地址进行负载均衡。

三角传输模式:负载均衡数据分发过程不修改IP地址,只修改目的mac地址,通过配置真实物理服务器集群所有机器虚拟IP和负载均衡服务器IP地址一致,从而达到不修改数据包的源地址和目的地址就可以进行数据分发的目的,由于实际处理请求的真实物理服务器UP和数据请求目的IP一致,不需要通过负载均衡服务器进行地址转换,可将响应数据包直接返回给用户浏览器,避免负载均衡服务器网卡带宽成为瓶颈,这种负载均衡由称作直接路由方式(DR)

使用三角传输模式的链路层负载均衡是目前大型网站使用最广的一种负载均衡手段,在LInux平台上最好的链路层负载均衡开源产品LVS(Linux Virtual Server)

负载均衡算法

负载均衡服务器的实现可以分为两个部分:

1.根据负载均衡算法和Web服务器列表计算得到集群中一台Web服务器的地址。

2.将请求数据发送到该地址对应的Web服务器上。

具体负载均衡算法:

  • 轮询(Round Robin,RR):所有请求被依次分发到每台应用服务器上,即每台服务器需要处理的请求数目都相同,适合于所有服务器硬件都相同的场景。
  • 加权轮询(Weighted Round Robin,WRR):根据应用服务器硬件性能情况,在轮询的基础上,按照配置的权重将请求分发到每台服务器,高性能的服务器能分配更多请求。
  • 随机(Random):请求被随机分配到各个应用服务器,在许多场合下,该方案简单实用,因为好的随机数本身就很均衡。即使应用服务器硬件配置不同,也可以实用加权随机算法。
  • 最少连接(Least Connection):记录每个应用服务器正在处理的连接数(请求数),将新到的请求分发到最少连接的服务器上,这是最符合负载均衡定义的算法,最少连接算法也可实现加权最少连接。
  • 源地址散列(Source Hashing):根据请求来源的IP地址进行Hash计算,得到应用服务器,这样来自同一个IP地址的请求总在一个服务器上处理,该请求的上下文信息可以存储在这台服务器上,在一个会话周期内重复使用,实现会话粘滞。

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

新上线的的缓存服务器对整个分布式缓存集群的影响最少,即新加入缓存服务器后应使整个缓存服务器集群中已经缓存的数据尽可能被访问到,这是分布式缓冲集群伸缩性设计的最主要目标。

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

应用程序通过Memcached客户端访问Memcached服务器集群,Memcached客户端主要由一组API、Memcached服务器集群路由算法,Memcached服务器集群列表及通信模块构成。

其中路由算法负责根据应用程序输入的缓存数据KEY计算得到应该讲数据写入到Memcached的哪台服务器(写缓存)或者应该从哪台服务器数据(读缓存)。

Memcached分布式缓存集群的伸缩性挑战

余数Hash路由算法可以保证缓存数据在整个Memcached服务器集群中比较均衡地分布。可以稍微改进,实现和负载均衡算法中加权负载均衡一样的加权路由。

当分布式缓存集群需要扩容时,如2台服务器扩容至四台服务器,大约75%(3/4)被缓存了的数据不能正确命中,而随着服务器集群规模的增大,这个比例线性上升。当100台服务器集群中加入一台新服务器,不能命中率时99(N/N+1).

一种解决办法:在网站访问量最少的时候扩容缓存服务器集群,这时对数据库的负载冲击最少,然后通过模拟请求的方法逐渐预热缓存,使缓存服务器中的数据重新分布。(技术团队通宵加班,网站访问低估通常是半夜)

一致性Hash算法:改进路由算法,使得新加入服务器不影响大部分缓存数据的正确命中率。

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

一致性Hash算法通过一个叫做一致性Hash环的数据结构实现KEY到缓存服务器的Hash映射。

具体算法过程:先构造一个长度为02^32的整数环(这个环称作一致性Hash环),根据节点名称的Hash值(其分布范围同样为0232)将缓存服务节点放置在这个Hash环上,然后根据需要缓存的数据KEY值计算得到其Hash值(其分布范围同样为0~232),然后在Hash环上顺时针查找距离这个KEY的Hash值最近的缓存服务器节点,完成KEY到服务器Hash映射查找。

当缓存服务器集群需要扩容的时候,只需要将新加入的节点的Hash值放入一致性Hash环中,由于KEY是顺时针查找距离最近的节点,因此新加入的节点只影响整个环中的一小段。

具体应用中,这个长度为2^32的一致性Hash 环通常使用二叉查找树实现,Hash查找过程实际上是在儿茶查找树中查找不小于查找数的最小数值。这个二叉查找树最右边叶子节点和最左边的叶子节点相连,构成环。

新加入的节点NODE3只应县了原来的节点NODE1,也就是说一部分原来需要访问NODE1的缓存数据现在需要访问NODE3(概率上50%)。但是原来的节点NODE0和NODE2不受影响,这就意味着NODE0和NODE2缓存数据量和负载压力是NODE1与NODE3的两倍。

解决手段:使用虚拟层,将每台物理缓存服务器虚拟一组虚拟换乘服务器,将虚拟服务器的Hash 值放置在Hash环上,KEY在环上先找到虚拟服务器节点,再得到物理服务器的信息。新加入物理服务器节点时,试讲一组虚拟节点加入环中,如果虚拟节点数目足够多,这组虚拟节点将会影响同样多数目的已经在环上存在的虚拟节点,这些已经存在的虚拟节点又对应不同的物理节点,最终的结果是新加入一台缓存服务器,将会较均匀的影响原来集群中已存在的所有服务器,就是说分摊原有缓存服务器集群中所有服务器的一小部分负载,其总的影响范围和上面讨论过的相同。

一般来说,一台物理服务器虚拟为150个虚拟服务器节点比较合适,太多会影响性能,太少会导致负载不均衡,具体情况还需根据集群规模和负载均衡的精度需求确定。

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

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

市场上主要的关系数据都支持数据复制功能,使用这个功能可以对数据库进行简单伸缩。

多台服务器部署MySQL实例,但是他们的角色有主从之分,数据写操作都在主服务器上,由主服务器将数据同步到集群中其他从服务器,数据读操作及数据分析等离线操作在从服务器上进行。

除了数据库主从读写分离,业务分割模式可用数据库,不同业务数据表部署在不同的数据库集群上,即俗称的数据分库。这种方式的约束条件是跨库的表不能进行Join操作。

Cobar是一个分布式关系数据库访问代理,介于应用服务器和数据库服务器之间(Cobar也支持独立部署,以lib的方式和应用程序部署在一起)应用程序通过JDBC驱动访问Cobar集群,Cobar服务器根据SQL和分库规则分解SQL,分发到MySQL集群不同的数据库实例上执行(每个MySQL实例都部署为主、从结构,保证数据高可用)。

(1)前端通信模块负责和应用程序通信,接收到SQL请求后转交给SQL解析模块,SQL解析模块解析获得SQL路由规则查询条件再转交给SQL路由模块

(2)SQL路由模块根据路有规则配置将应用程序提交SQL解析成两条SQL转交给SQL执行代理模块,发送给数据库A和B分布执行。

(3)数据库A和数据库B的执行结果返回至SQL执行模块,通过结果合并将两个返回结果集合并成一个结果集,最终返回给应用程序,完成在分布式数据库中的一次访问请求。

Cobar有两种集群伸缩:Cobar服务器集群的伸缩和MySQL服务器集群的伸缩

Coba服务器可看作是无状态的应用服务器,因此其集群伸缩可以简单使用负载均衡的手段实现。

MySQL中存储着数据,要想保证集群扩容后数据一致负载均衡,必须要做数据迁移,将集群中原来机器中的数据迁移到新添加的机器中。

Cobar利用MySQL的数据同步功能进行数据迁移,数据迁移不是以数据为单位,而是以Schema为单位。在Cobar集群初始化时,在每个MySQL实例创建多个Schema(根据业务远景规划未来集群规模,如集群最大规模为1000台数据库服务器,那么总的初始Schema数>=1000)。集群扩容的时候,从每个服务器中迁移部分schema到新机器中,由于迁移以Schema为单位,迁移过程可以使用MySQL的同步机制。

同步完成时,即新机器中schema数据和原机器中schema数据一致时,修改Cobar服务器的路由配置,将这schema的IP修改为新机器的IP,然后删除原机器中的相关schema,完成MySQL集群扩容。

使用分布式数据库集群必须从业务上回避分布式关系数据库的各种缺点:避免事务或利用事务补偿机制代替数据库事务,分解数据访问逻辑避免JOIN操作等。

还有一类分布式数据库可以支持JOIN操作执行复杂的SQL查询,如GreenPlum,这类数据库的访问延迟比较大,一般使用在数据仓库等非实时业务中。

NoSQL数据库的伸缩性设计

NoSQL,主要指非关系的、分布式的数据库设计模式,Not only SQL,NoSQL是关系数据库的补充,非代替方案。一般而言,NoSQL数据库产品都放弃了关系数据库的两大重要基础:以关系代数为基础的结构化查询语言和事务一致性保证(ACID),强化其他一些大型网站关注的特性:高可用性和可伸缩性

Apache HBase为可伸缩海量数据存储而设计,实现面向在线业务的实时数据访问延迟。HBase的伸缩性主要依赖其可分裂的HRegion及可伸缩的分布式文件系统HDFS实现。

所有HRegion的信息都记录在HMaster服务器上,为了保证高可用,HBase启动多个HMaser,并通过Zookeeper(一个支持分布式一致性的数据管理服务)选举出一个主服务器,应用程序通过Zookeeper获得主HMaser的地址,输入Key获得一个Key所在的HRegionServer地址,然后请求HRegionServer上的HRegion,获得需要的数据。

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

扩展性:指对现有系统影响最少的情况下,系统功能可持续扩展或提升的能力。表现在系统基础设施稳定不需要经常变更,应用之间较少依赖和耦合,对需求变更可以敏捷响应。它是系统架构设计层面的开闭原则(对扩展开放,对修改关闭),架构设计考虑未来功能扩展,当系统增加新功能时,不需要对现有系统的结构和代码进行修改。

伸缩性:指系统能够通过增加(减少)自身资源规模的方式增强(减少)自己计算处理事务的能力。如果这种增减是成比例的,就被称作线性伸缩性。在网站架构中,通常指利用集群的方式增加服务器数量,提高系统的整体事务吞吐能力。

7.1 构建可扩展的网站架构

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

在大型网站中,这些模块通过分布式部署的方式,独立的模块部署在独立的服务器(集群)上,从物理上分离模块之间的耦合关系,进一步降低耦合性提高复用性。

模块分布式式部署以后具体聚合方式主要有分布式消息队列和分布式服务。

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

事件驱动架构

通过在低耦合的模块之间传输事件消息,以保持模块的松散耦合,并借助事件消息的通信完成模块间合作,典型EDA架构就是操作系统中常见的生产者消费者模式。在大型网站架构中,最常用的是分布式消息队列。

消息队列利用发布-订阅模式工作,消息发送者发布消息,一个或者多个消息接受者订阅消息。消息发送者是消息源,在对消息进行处理后将消息发送至分布式消息队列,消息接受者从分布式消息队列获取该消息后继续进行处理。

消息接受者在对消息进行过滤、处理、包装后,构造成一个新的消息类型,将消息继续发送出去,等待其他消息接受者订阅处理该消息。

由于消息发送者不需要等待消息接受者处理数据就可以返回,系统具有更好的系统延迟,同时,在网站访问高峰,消息可以暂时存储在消息队列中等待消息接受者根据自身负载处理能力控制消息处理速度,减轻数据库等后端存储的负载压力。

分布式消息队列

分布式消息队列可看作将这种数据结构部署到独立的服务器上,应用程序可以通过远程访问接口使用分布式消息队列,进行消息存取操作,进而实现分布式的异步调用。

消息生产者应用程序通过远程访问接口将消息推送给消息队列服务器,消息队列服务器将消息写入本地内存队列后立即返回成功响应给消息生产者。消息队列服务器根据消息订阅列表查找订阅该消息的消息消费者应用程序,将消息队列中的消息按照先进先出的原则将消息通过远程通信接口发送给消息消费者程序。

在伸缩性方面,由于消息队列服务器上的数据可以看做是被及时处理的,因此类似于无状态的服务器,伸缩性设计比较简单。将新服务器加入分布式消息队列集群中,通知生产者服务器更改消息队列服务器列表即可。

在可用性方面,为了避免消费者进程处理缓慢,分布式消息队列服务器内存空间不足造成的问题,如果内存队列已满,会将消息写入磁盘,消息推送模块在将内存队列消息处理完以后,将磁盘内容加载到内存队列继续处理。

为了避免消息队列服务器宕机造成消息丢失,会将消息成功发送到消息队列的消息存储在消息生产者服务器,等消息真正被消息消费者服务器处理后才删除消息。在消息队列服务器宕机后,生产者服务器会选择分布式消息队列服务器集群中其他的服务器发布消息。

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

巨无霸应用系统带来的问题:

(1)变异、部署困难

(2)代码分支管理困难

(3)数据库连接耗尽

(4)新增业务困难

解决方案就是拆分,将模块独立部署,降低系统耦合性。拆分可以分为纵向拆分和横向拆分两种。

纵向拆分:将一个大应用拆分为多个小应用,如果新增业务较为独立,那么就直接将其设计部署为一个独立的Web应用系统。

横向拆分:将复用的业务拆分出来,独立部署为分布式服务,新增业务只需要调用这些分布式服务,不需要依赖具体的模块代码,即可快速搭建一个应用系统,而模块内业务逻辑变化的时候,只要接口保持一致就不会影响业务程序和其他模块。

纵向拆分相对较为简单,通过梳理业务,将较少相关的业务剥离,使其成为独立的Web应用。横向拆分,不但需要识别可复用的业务,设计服务接口,规范服务依赖关系,还需要一个完善的分布式服务管理框架。

Web Service与企业级分布式服务

服务提供者通过WSDL(Web Service Description Language,Web服务描述语言)想注册中心(Service Broker)描述自身提供的服务接口属性,注册中心使用UDDI(Universal Description,Discovery,and Integration,统一描述、发现和集成)发布服务提供者提供的服务,服务请求这从注册中心检索到服务信息后,通过SOAP(Simple Object Access Protocol,简单对象访问协议)和服务提供者通信,使用相关服务。

Web Service固有的缺点:

(1)臃肿的注册与发现机制

(2)低效的XML序列化手段

(3)开销相对较高的HTTP远程通信

(4)复杂的部署与维护手段

大型网站分布式服务的需求与特点

负载均衡,失效转移,高效的远程通信,整合异构系统,对应用最少侵入,版本管理,实时监控

分布式服务框架设计

SOA(ServiceOriented Architecture面向服务的体系架构)分布式服务架构

Dubbo的远程服务通信模块支持多种通信协议和数据序列化协议,使用NIO通信框架,具有较高的网络通信性能。

7.4 可扩展的数据结构

许多NoSQL数据库使用的ColumnFamily(列族)设计就是一个可扩展的数据库架构设计(无需修改表结构就可以新增字段)的解决方案。ColumnFamily最早在Google的Bigtable中使用,这是一种面向列族的稀疏矩阵存储格式。

支持ColumnFamily结构的NoSQL数据库,创建表的时候,只需要指定ColumnFamily的名字,无需指定字段(Column),可以在数据写入时再指定,通过这种方式,数据表可以包含数百万的字段,使得应用程序的数据结构可以随意扩展,在查询时,可以通过指定任意字段名称和值进行查询。

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

开放平台时网站内部和外部交互的接口,外部需要面对众多的第三方开发者,内部需要面对网站内部诸多的业务服务。

API接口:是开发平台暴露给开发者使用的一组API,其形式可以是RESTful、WebService、RPC等各种形式。

协议转换:将各种API输入转换成内部服务可以识别的形式,并将内部服务的返回封装给API的格式。

安全:除了一般应用需要的身份识别、权限控制的安全手段,开放平台还需要分级的访问宽度限制,保证平台资源被第三方应用公平合理使用,也保护网站内部服务不会被外部应用拖垮。

审计:记录第三方应用的访问情况,并进行监控、计费等。

路由:将开放平台的各种访问路由映射到具体的内部服务

流程:将一组离散的服务组织成一个上下文相关的新服务,隐藏服务细节,提供统一接口供开发者调用。

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

8.1 道高一尺魔高一丈的网站应用攻击与防御

XSS攻击

XSS攻击即跨站点脚本攻击(Cross Site Script),指黑客通过篡改网页,注入恶意HTML脚本,在用户浏览网页时,控制用户浏览器进行恶意操作的一种攻击方式。

反射型:攻击者诱使用户点击一个嵌入恶意脚本的链接,达到攻击的目的。偷取用户cookie、密码等重要数据,进行伪造交易、盗窃用户财产、窃取情报。

持久型:黑客提交含有恶意脚本的请求,保存在被攻击的Web站点的数据库中,用户浏览页时,恶意脚本被包含在正常页面中,达到攻击目的。

XSS防攻击主要手段:

(1)消毒:XSS攻击者一般都是通过在请求中嵌入恶意脚本达到攻击的目的,这些脚本是一般用户输入中不使用的,如果进行过滤和消毒处理,即对某些html危险字符转移,就可以防止大部分攻击。

(2)HttpOnly:最早由微软提出,即浏览器禁止页面JavaScript范文带有HttpOnly属性的Cookie。HTTPOnly并不是直接对抗XSS攻击的,而是防止XSS攻击者窃取Cookie。对于存放敏感信息的Cookie,可通过对该Cookie添加HTTPOnly属性,避免被攻击脚本窃取。

注入攻击

主要有两种形式:SQL注入攻击和OS注入攻击。

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

攻击者获取数据库表结构信息的手段:

(1)开源:如果网站采用开源软件搭建,那么网站数据结构就是公开的,攻击者可以直接获取。

(2)错误回显:如果网站开启错误回显,即服务器内部500错误会显示到浏览器上。攻击者通过故意构造非法参数,使服务端异常信息输出到浏览器端,为攻击猜测数据库表提供了便利。

(3)盲注:攻击者根据页面变化情况判断SQL语句执行情况,猜测数据库表结构

防御SQL注入攻击首先要避免被攻击者猜测到表名等数据库表结构信息:

(1)消毒:请求参数消毒,通过正则匹配

(2)参数绑定:使用预编译手段,绑定参数是最好的防SQL注入方法。攻击者的恶意SQL会被当做SQL参数,而不是SQL命令被执行。

CSRF攻击

CSRF(Cross Site Request Forgery,跨站点请求伪造),攻击者通过跨站请求,以合法用户的身份进行非法操作。主要手法是利用跨站请求,在用户不知情的情况下,以用户的身份伪造请求。其核心是利用了浏览器Cookie或服务器Session策略,盗取用户身份。

CSRF的防御手段主要是识别请求者身份,主要有几种方法:

(1)表单Token:CSRF是一个伪造用户请求的操作,所以需要构造用户请求的所有参数才可以。表单Token通过在请求参数中增加随机数的办法来阻止攻击者获得所有请求参数:在页面表单中增加一个随机数作为Token,每次响应页面的Token都不相同,从正常页面提交的请求会包含该Token值,而伪造的请求无法获得该值,服务器检查请求参数中Token的值是否存在并且正确以确定请求提交者是否合法。

(2)验证码:请求提交时,需要用户输入验证码,以避免在用户不知情的情况下被攻击者伪造请求。在必要时使用验证码,如支付交易等关键页面。

(3)Referer check:HTTP请求头的Referer域中记录着请求来源,可通过检查请求来源,验证其是否合法,如图片防盗链。

其他攻击和漏洞

Error Code:错误回显,许多Web服务器默认是打开异常信息输出的,即服务端未处理的异常堆栈信息会直接输出到客户端浏览器,这种方式虽然对程序调试和错误报告有好处,但同时给黑客造成可乘之机。通过故意制造非法输入,使系统运行时出错,获得异常信息,从而寻找系统漏洞进行攻击,防御手段:通过配置Web服务器参数,跳转500页面(服务器内部错误)到专门的错误页面即可,web应用常用的MVC框架也有该功能。

HTML注释:HTML注释会显示在客户端浏览器,给黑客造成攻击便利,程序最终发布前需要进行代码review或自动扫描,避免HTML注释漏洞。

文件上传:若上传的是可执行的程序,并通过该程序获得服务器端命令执行能力,那么攻击者就为所欲为了。最有效的防御手段是设置上传文件白名单,只允许上传可靠的文件类型,此外还可以通过修改文件名,使用专门的存储等手段保护服务器免受上传文件攻击。

路径遍历:攻击者在请求的URL中使用相对路径,遍历系统未开放的目录和文件。防御方法主要是将JS、CSS等资源文件部署在独立服务器、使用独立域名,其他文件不使用静态URL访问,动态参数不包含文件路径信息。

Web应用防火墙

ModSecurity是一个开源的Web应用防火墙,探测攻击并保护Web应用程序,既可嵌入到Web应用服务器中,也可作为一个独立的应用程序启动。

ModSecurity采用处理逻辑与攻击规则集合分离的架构模式。处理逻辑(执行引擎)负责请求和响应的拦截过滤,规则加载执行等功能。攻击规则集合则负责描述对具体攻击的规则定义、模式识别、防御策略等功能。处理逻辑比较稳定,规则集合需要不断针对漏洞进行升级,这是一种可扩展的架构设计。

网站安全漏洞扫描

网站安全漏洞扫描工具是根据内置规则,构造具有攻击性的URL请求,模拟黑客攻击行为,用以发现网站安全漏洞的工具。

8.2 信息加密技术及秘钥安全管理

为保护网站的敏感数据,应用需要对这些信息进行加密处理,信息加密技术可分为三类:单向散列加密、对称加密和非对称加密。

单向散列加密

单向散列加密指通过对不同输入长度的信息进行散列计算,得到固定长度的输出,散列计算过程是单向的,即不能对固定长度的输出进行计算从而获得输入信息。常用单向散列算法:MD5、SHA等。该加密算法特点是输入的任何微小变化都会导致输出的完全不同,有时会被用来生成信息摘要、计算具有高离散程度的随机数等用途。

对称加密

对称加密指加密和解密使用的密钥是同一秘钥(或者可以互相推算),通常在信息需要安全交换或存储的场合,如Cookie加密、通信加密等。优点:算法简单,加密效率高,系统开销小,适合对大量数据加密。缺点:加密解密使用同一个密钥。常用算法:DES算法,RC算法。

非对称加密

非对称加密和解密使用的密钥不是同一密钥,其中一个对外公开的公钥,另一个是只有所有者的知道的私钥,该信息不可抵赖,具有签名性质。用公钥加密的信息必须用私钥才能解开,私钥加密的信息只能用公钥才能解开。不能通过公钥计算获得私钥。通常用在信息安全传输,数字签名等场合。实际应用中,常混合使用对称加密和非对称加密。HTTPS传输中浏览器使用的数字证书实质上是经过权威机构认证的费对称加密的公钥。常用算法RSA算法等。

密钥安全管理

改善密钥安全性的手段:

一种方案是把密钥和算法放在一个独立的服务器上,甚至做成一个专用的硬件设施,对外提供加密和解密服务,应用系统通过调用这服务,实现数据的加密解密。

另一种方案是将加密解密算法放在应用系统中,密钥则放在独立服务器中,为了提高密钥的安全性,实际存储时,密钥被切成数片,加密后分别保存在不同存储介质中,兼顾密钥安全性的同时又改善了性能。

8.3 信息过滤与反垃圾

文本匹配

文本匹配主要解决敏感过滤的问题。Trie算法的本质是确定一个有限状态自动机,根据输入数据进行状态转移。双数组Trie算法优化Trie算法,利用两个稀疏数组存储树结构,base数组存储Trie树的节点,check数组进行状态检查。双数组Trie数需要根据业务场景和经验确定数组大小,避免数组过大或者冲突过多。

通过构造多级Hash表进行文本匹配。构造过滤树,用户提交的信息卒子顺序在过滤树中匹配。过滤树的分支可能会比较多,为了提高匹配速度,减少不必要的查找,同一层中相同父节点的字可放在Hash表中。该方案处理速度较快,稍加变形,即可适应各种过滤场景,缺点是使用Hash表会浪费部分内存空间,如果网站敏感词数量不多,浪费部分内存还是可以接受。

分类算法

对如此海量的信息进行人工审核是不实现的,对广告贴、垃圾邮件等内容的识别比较好的自动化方法是采用分类算法。常用算法:贝叶斯分类算法(利用概率统计方法进行分类)

黑名单

黑名单数量少时可通过Hash表实现。

在对过滤需求要求不完全精确的场景下,可用布隆过滤器代替Hash表。通过一个二进制列表和一组随机数映射函数实现。10亿邮件地址黑名单列表在内存建立一个2G大小的存储空间,即16GB个二进制bit。同样数量信息,布隆过滤器只使用Hash表所需内存的1/8。

8.4 电子商务风险控制

交易安全是电子商务网站的底线

风险

账户风险:包括账户被黑客盗用,恶意注册账号等几种情形。

买家风险:买家恶意下单占用库进行不正当竞争;黄牛利用促销抢购低价商品;此外良品拒收,欺诈退款及常见于B2B交易的虚假询盘等。

卖家风险:不良卖家进行恶意欺诈的行为,如货不对板,虚假发货,炒作信用等,还有出售违禁商品、侵权产品等。

交易风险:信用卡盗刷,支付欺诈,洗钱套现等

风控

机器自动风控的技术手段主要有规则引擎和统计模型。

(1)规则引擎:是一种将业务规则和规则处理逻辑相分离的技术,业务规则文件由运营人员通过管理界面编辑,当需要修改规则时,无需更改代码发布程序,即可实时使用新规则。规则处理逻辑则调用规则处理输入的数据。

(2)统计模型:风控领域使用的统计模型使用前面提到的分类算法或者更复杂的机器学习算法进行智能统计。根据历史交易中的欺诈交易信息训练分类算法,然后将经过采集加工后的交易信息输入分类算法,即可得到交易风险分值。经过充分训练后的统计模型,准确率不低于规则引擎,对新出现的交易欺诈还具有一定预测性。

11.海量分布式存储系统Doris的可高可用架构设计分析

11.1 分布式存储系统的高可用架构

在系统架构层面,保持高可用的主要手段是冗余:服务器热备,数据多份存储。使整个集群在部分机器故障的情况下可以进行灵活的失效转移,保证系统整体依然可用,数据持久可靠。

Doris的整个架构:

应用程序服务器:它们是存储系统的客户,对系统发起数据操作请求。

数据存储服务器:它们是存储系统的核心,负责存储数据、响应应用服务器的数据操作请求。

管理中心服务器:这是一个由两台机器组成的主-主热备的小规模服务器集群,主要负责集群管理,对数据存储集群进行健康心跳检测,集群扩容、故障恢复管理;对应用程序服务器提供集群地址配置信息服务等。

应用服务器写入数据时,根据集群配置和应用可用性级别使用路由算法在每个序列中计算得到一台服务器,然后同时并发写入这些服务器中;应用服务器读取数据时,只需要随机选择一个序列,根据相同路由算法计算得到服务器编号和地址,即可读取。通常情况下,系统最少写入的副本份数是两份。

在正常状态下,存储服务器集群中的服务器互不感知,不进行任何通信;应用服务器也只在启动时从管理中心服务器获取服务器集群信息,除非集群信息发生变化(故障,扩容),否则应用服务器不会和管理中心服务器通信。一般而言,服务器之间通信越少,就越少依赖,发生故障时互相影响就越少,集群的可用性就越高。

11.2 不同故障情况下的高可用解决方案

分布式存储系统的故障分类

瞬时故障:引起这类故障的主要原因是网络通信瞬时中断、服务器内存垃圾回收或后台线程繁忙停止数据访问操作响应。特点是故障时间段,在秒级升值毫秒级系统即可自行恢复正常响应。

临时故障:引起这类故障的主要原因是交换机宕机、网卡松动等导致的网络通信终端;系统升级、停机维护等一般运维活动引起的服务关闭;内存损坏、CPU过热等硬件原因导致的服务器宕机;这类故障的主要特点是需要人工干预(更换硬件、重启机器等)才恢复正常。通常持续时间需要几十分钟甚至几小时。故障时间可分为两个阶段:临时故障期间,临时故障恢复期间。

永久故障:引起这类故障的主要原因只有一个:硬盘损坏,数据丢失。虽然损坏硬盘和损坏内存一样,可以通过更换硬盘来重启机器,但丢失的数据却永远找不回来了,因此其处理策略也和前面两种故障完全不同,恢复系统到正常状态也需更长时间。故障时间可分为两个阶段:永久故障期间和永久恢复期间。

正常情况下系统访问结构

应用程序在写数据时,需要路由计算获得两台不同的服务器,同时将数据写入两台服务器;而读数据时,只需要到两台服务器上任意一台服务器读取即可。

瞬时故障的高可用解决方案

瞬时故障是一种严重性较低的故障,一般系统经过较短暂的时间即可自行恢复,遇到瞬时故障只需要多次重试,就可以重新连接到服务器,正常访问。

Doris瞬时故障解决方案:

(1)应用服务器向存储服务器请求服务,

(2)通信超时,不可识别异常,

(3)应用服务器重新请求X次直到成功。

Doris瞬时失效访问模型:

(1)应用程序服务器访问存储服务器失败,

(2)应用程序服务器访问存储服务器重试,

(3)应用程序服务器向管理中心服务器进行失效仲裁

(4)管理中心服务器对存储服务器健康心跳检测、失效仲裁检测。

临时故障的高可用解决方案

(1)应用服务器只从正常服务器读取数据。

(2)写数据时,正常服务的机器依然正常写入,发生故障的机器需要将数据写入到临时存储服务器,等待故障服务器恢复正常后再将临时服务器中的数据迁移到该机器,整个集群的恢复正常了。

永久故障的高可用解决方案

当系统出现临时故障超时(超过设定时间临时故障服务器仍未启动)或者人工确认为永久故障时,系统启用备用服务器代替原来永久失效的服务器,进入永久故障恢复。

12 网购秒杀系统架构设计案例分析

12.1 秒杀活动的技术挑战

(1)对现有网站业务造成冲击

(2)高并发下的应用、数据库负载

(3)突然增加的网络及服务器宽带

(4)直接下单

12.2 秒杀系统的应对策略

(1)秒杀系统独立部署

(2)秒杀商品页面静态化

(3)租借秒杀活动网络宽带

(4)动态生成随机下单页面URL

12.3 秒杀系统架构设计

(1)控制秒杀商品页面购买按钮的点亮

(2)只允许第一个提交的订单被发送到订单子系统

13 大型网站典型故障案例分析

13.1 写日志也会引发故障

故障现象:某应用服务器集群发布后不久就出现多台服务器相继报警,硬盘可用空间低于警戒值,并且很快有服务器宕机,登录到线上的服务器,发现log文件夹里的文件迅速增加,不断消耗磁盘空间。

原因分析:100GB小硬盘,系统,web服务器,Java虚拟机,应用程序等在一个应用服务器,不需存储数据。空闲空间只有几十GB,但开发人员将log输出的level全局配置为Debug,web请求会产生大量的log文件输出,在高并发的用户请求下,很快就消耗完不多的磁盘空间。

经验教训:

(1)应用程序自己的日志输出配置和第三方组件日志输出要分别配置。

(2)检查log配置文件,日志输出级别至少为Warn,并且检查log输出代码调用,调用级别要符合其真实日志级别。

(3)有些开源的第三方组件也会有不恰当地输出太多Error日志,需要关闭这些第三方库日志输出,至于哪些第三方库有问题,只有在遇到问题是才知道。

13.2 高并发访问数据库引发的故障

故障现象:某应用发布后,数据库Load居高不下,远超过正常水平,持续报警。

原因分析:执行频率非常高的有索引数据查询的SQL,远超正常水平,追查这条SQL,发现该网站首页应用调用,首先是被访问最频繁的网页,这条SQL被首页调用,也被频繁执行。

经验教训:

(1)首页不应该访问数据库,首页需要的数据可以从缓存服务器或者搜索引擎服务器获取。

(2)首页最好是静态的。

13.3 高并发情况下锁引发的故障

故障现象:某应用服务器不定时地因为响应超时而报警,但很快又超时解除,恢复正常,如此反复,让运维人员非常苦恼。

原因分析:程序中某个单例对象中多处使用了synchronized(this)由于this对象只有一个,所有的并发请求都要排队获得这唯一的一把锁。一般情况下,一些简单操作获得锁,迅速完成操作,释放锁,不会引起线程排队。但某个需要远程调用的操作也被加了synchronized(this),这个操作知识偶尔会被执行,但每次执行都需要较长的时间才能完成,这段时间锁被占用,所有的用户线程都要等待,响应超时,这个操作执行完后释放锁,其他线程迅速执行,超时解除。

经验教训:使用锁操作要谨慎。

13.4 缓存引发的故障

故障现象:没有新应用发布,但是数据库服务器突然Load飙升,并很快失去响应。DBA将数据库访问切换到备机,Load也很快飙升,并失去响应,最终引发网站全部瘫痪。

原因分析:缓存服务器再网站服务器集群中地位一致比较低,服务器配置和管理级别都比其他服务器要低一些,长期疏于管理缓存服务器,关闭缓存服务器集群的Memcached服务器,导致网站全部瘫痪的重大事故。

经验教训:当缓存已经不仅仅是改善性能,而是成为网站架构不可或缺的一部分时,对缓存的管理就需要提高到和其他服务器一样的级别。

13.5 应用启动不同引发的故障

故障现象:某应用发布后,服务器立即崩溃。

原因分析:应用程序Web环境使用Apache——JBoss的模式,用户请求通过Apache转发JBoss,在发布时,Apache和JBoss同时启动,由于JBoss启动时需要加载很多应用并初始化,话费时间较长,结果JBoss还没完全启动,Apache就已经启动完成开始接受用户请求,大量请求阻塞在JBoss进场中,最终导致JBoss崩溃。除了这种Apache和JBoss启动不同步的情况,网站还有还有很多类似的场景,都需后台服务准备好,前台应用才能启动,否则就会导致故障。

经验教训:在应用程序加入一个特定的动态页面(比如返回OK),启动脚本先启动JBoss,然后在脚本中不断用curl命令访问这个特定页面,直接受到OK,才启动Apache。

13.6 大文件读写独占磁盘引发的故障

故障现象:某应用主要功能时管理用户图片,接到部分用户投诉,表示上传图片非常慢,原来只需一两秒,现在需几十秒,有时等半天结果浏览器显示服务器超时。

原因分析:图片需要使用存储,最可能出错的地方是存储服务器。检查存储服务器,发现大部分文件只有几百KB,而是几个文件非常大,有数百兆,读写这些大文件一次需要几十秒,这段时间,磁盘基本被这文件操作独占,导致其他用户的文件操作缓慢。

经验教训:存储的使用需要根据不同文件类型和用途进行管理,图片都是肖文杰,应该使用专用的存储服务器,不能喝大文件共用存储,批处理用的大文件可以使用其他类型的分布式文件系统。

13.7 滥用生产环境引发的故障

故障现象:监控发现某个时间段,某些应用突然变慢,内部网络访问延迟非常厉害。

原因分析:检查发现,该时段内网卡流量也下降,但没找到原因,工程师在线上生产环境进行性能压力测试,占用率大部分交换机带宽。

经验教训:访问线上生产环境要规范,不小心就会导致大事故。

13.8 不规范的流程引发的故障

故障现象:某应用发布会,数据库Load迅速飙升,超过报警值,回滚发布后报警消除。

原因分析:发现该应用发布后出现大量数据库读操作,而这些数据本来应该从分布式缓存读取。检查缓存,发现数据已经数据已经被缓存了。检查代码,发现访问缓存的那行代码被注释掉了。读取缓存的代码被注释掉了。

经验教训:

(1)代码提交前使用diff命令进行代码比较,确认没有提交不该提交的代码。

(2)加强code review,代码在正式提交前必须被只是一个其他工程师做过code review,并且共同承担因代码引起的故障责任。

13.9 不好的编程习惯引发的故障

故障现象:某应用更新功能后,有少量用户投诉没法正常访问该功能,一点击就显示出错误信息。

原因分析:原因分析,分析这些用户都是第一次使用该功能,检查代码,发现程序历史使用记录构造一个对象,如果该对象为null,导致NullPointException

经验教训:

(1)程序在处理一个输入的对象时,如果不能明确该对象是否为空,必须做空指针判断。

(2)程序在调用其他方法时,输入的对象尽量保证不是null,必要时构造空对象(使用空对象模式)

你可能感兴趣的:(web前端)