《大型网站技术架构:核心原理与案例分析》读书笔记
学习者:zohar.zzh
时 间:2019-1-21
pdf下载地址
第1篇 概述
应用程序、数据库、文件等所有资源都在一个服务器上。操作系统为Linux,应用程序使用PHP,部署在Apache上,数据库为MySql。
应用和数据分离之后形成三个服务器:应用服务器、文件服务器和数据库服务器。
问题:数据库访问延时。
大部分业务访问集中在一小部分数据上,把这一小部分数据缓存在内存中,减少数据库访问压力。
缓存分两种:
问题:使用缓存后,数据库访问压力可以得到缓解,但是单一应用服务器能够处理的请求连接有限,在网站访问高峰期,应用服务器成为整个网站的瓶颈。
使用集群是网站解决高并发、海量数据问题的常用手段。当网站持续增长的业务需求,增加一台服务器分担原有的访问以及存储压力也是一种恰当的做法。
通过负载均衡调度服务器,将访问请求分发到应用服务器集群中的任何一台服务器中。
虽然使用缓存能够缓解部分压力,但是仍有一部分读操作(缓存不命中、缓存过期)和全部的写操作仍是需要访问数据库的。
大部分主流数据库都提供主从热备功能,通过配置两台数据库主从关系,可以将一台数据库服务器的数据更新同步到另外一台服务器上。利用这一功能,实现数据库读写分离,从而改善数据库负载压力。
应用服务器写数据的时候,访问主数据库服务器,主数据库通过主从复制机制将数据更新同步到从数据库,这样应用服务器读取数据的时候,就可以读取从数据库服务器。应用服务器使用专门的数据访问模块,可以使数据库读写分离。
CDN和反向代理的基本原理都是缓存。
CDN和反向代理都是为了尽快的返回数据给用户,一方面加快用户访问速度,另一方面减轻服务器的负载压力。
将数据库和文件系统拆分到多个服务器上。
分布式数据库是网站数据库拆分的最后手段,只有在单表数据规模非常庞大的时候才使用。不到不得已时,网站更常用的数据库拆分手段是业务分库,将不同的业务的数据库部署到不同的物理服务器上。
满足数据存储和检索的需求。
NoSQL和搜索引擎对可伸缩的分布式特性具有很好的支持。
应用服务器通过一个统一的数据访问模块访问各种数据,减轻应用程序管理诸多数据源的麻烦。
将相同的业务提取出来,独立部署,由可复用的业务连接数据库,来提供共用业务服务,应用系统只需要管理用户界面,通过分布式服务调用共用业务服务完成具体业务操作。
将系统在横向维度上切分成几个部分,每个部分负责相对比较单一的职责,然后通过上层对下层的依赖和调用组成一个完整的系统。
网站软件系统分为:应用层、服务层、数据层。
需要严格遵守分层架构的约束,禁止跨层次调用(应用层直接调用数据层)以及逆向调用(数据层调用服务层)。
大的分层结构内部还可以继续分层:
在实际部署上,可以部署在同一个服务器上,也可以分别部署在不同服务器上。
对软件在纵向方面进行切分。将不同的功能和服务分割开来,包装成高内聚低耦合的模块单元。
分割和分层的一个主要目的是为了切分后的模块便于分布式部署,也就是将不同的模块部署在不同的服务器上,通过远程调用协同工作。
解决高并发问题的同时也会带来的一些问题:
常用的分布式方案:
使用分布式虽然可以将分层和分割后的模块独立部署,但是对于用户访问集中的模块,还需要将独立不是的服务器集群化,即多台服务器部署相同的应用构成一个集群,通过负载均衡设备共同对外提供服务。
个人理解:
分布式和集群的差别 :分布式就是通过分层和分割将应用进行切分,形成更小的模块,然后这些模块可以部署在不同的服务器上面,这也就是所谓的“分布”的更通俗的含义;集群就是针对一个模块,会同时部署在很多服务其上面,这些服务器共同完成一个功能任务,这几个服务器也就组成一个集群。
缓存就是将数据放在距离计算最近的位置以加快处理速度。
使用缓存的因素:
系统解耦合的手段:分层、分割、分布、异步。
异步:业务之间的消息传递不是同步调用,而是讲一个业务操作分成多个阶段,每个阶段之间通过共享数据的方式异步执行执行协作。
实现异步:
异步架构是典型的生产者消费者模式。
使用异步消息队列的特性:
服务器冗余运行、数据冗余备份,保证网站的正常运行。
自动化代码管理、自动化测试、自动化安全监测、自动化部署、自动化监控、自动化报警、自动化失效转移、自动化失效恢复、自动化降级、自动化分配资源。
密码、手机校验码、加密、过滤、风险控制。
5个架构要素:性能、可用性、伸缩性、扩展性、安全性。
提高性能手段:
衡量网站性能指标:响应时间、TPS、系统性能计数器。
提高可用性手段:主要是冗余
伸缩性:通过不断向集群中加入服务器的手段来缓解不断上升的用户并发访问压力和不断增长的数据存储需求。
衡量的主要标准:是否可以用多台服务器构建集群,是否容易向集群中添加服务器。加入新的服务器后能否提供和原来的服务器无差别服务。集群中可容纳的总的服务器数量是否有限制。
扩展性关注网站的功能需求。
衡量的主要标准:增加新的业务产品时,是否可以实现对现有产品透明无影响,不需要任何改动或者很少改动既有的业务功能就可以上线新产品。不同产品之间是否很少耦合,一个产品改动,其他产品无影响。
主要手段:事件驱动架构和分布式服务。
事件驱动架构:利用消息队列实现。
分布式服务:将业务和可复用服务分离,通过分布式服务框架进行调用。新增产品可以通过调用可复用服务实现自身业务逻辑,而对现有产品没有任何影响。可复用服务升级变更时,可以通过提供多版本服务对应用实现透明升级,不需要强制应用同步变更。
衡量的主要标准:针对现存和潜在的各种攻击与窃密手段,是否有可靠的应对策略。
第2篇 架构
性能测试是性能优化的前提和基础,也是性能优化结果的检查和度量标准。
用户角度
网站响应的时间。
优化手段:前端架构优化手段,优化HTML式样、利用浏览器端的并发和异步特性。
开发人员角度
关注应用程序本身与相关子系统性能,包括响应延时、系统吞吐量、并发处理能力、系统稳定性等技术指标。
主要优化是手段:利用缓存加速数据读取,使用集群提高吞吐量,使用异步消息加快请求响应以及实现削峰,优化代码改善性能等手段。
运维人员角度
关注基础设施性能和资源利用率。如:带宽、硬件配置、网络架构、服务器和带宽的利用率等。
优化手段:建设优化骨干网、高性比服务器、虚拟化技术优化资源利用率。
主要指标:响应时间、并发数、吞吐量、性能计算器等。
响应时间
发送请求到收到响应的时间间隔。一般测试多次取平均值。
并发数
同时处理请求的数目。
吞吐量
单位时间内处理的请求数量。
性能计数器
描述服务器或者操作系统性能的一些数据指标。包括:System Load、对象与线程数、内存使用、CPU使用、磁盘与网络I/O等指标。System Load即系统负载,指当前正在被CPU执行和等待被CPU执行的进程数目总和,是反映系统闲忙程度的重要指标。
可分为:性能测试、负载测试、压力测试、稳定性测试。
性能测试
以系统设计初期规划的性能指标为预期目标,对系统不断施加压力,验证系统在可接受范围内,是否能达到性能预期。
负载测试
不断增加并发请求,知道系统某项和多项性能指标达到安全临界值。
压力测试
超过安全负载的情况下,对系统继续施加压力,知道系统崩溃或者不能再处理任何请求来获得系统的最大压力承受能力。
稳定性测试
在特定硬件软件、网络环境条件下,给系统加载一定业务压力,让系统较长时间运行,检测系统是否稳定。
对应的并发响应时间曲线:
性能分析
检查请求处理的各个环节的日志,分析哪个环节响应时间不合理。-> 然后检查监控数据,分析影响性能的主要原因是内存、磁盘、网络还是CPU,是代码问题还是架构不合理,或是系统资源确实不足。
性能优化
分为:Web前端性能优化、应用服务器性能优化、存储服务器性能优化。
Web前端指网站业务逻辑之前的部分,包括:浏览器加载、网站视图模型、图片服务、CDN服务。
主要优化手段:优化浏览器访问、反向代理、CDN。
减少http请求
主要手段:合并CSS、JavaScript、图片成一个文件,这样浏览器只需要请求一次。多张图片可以合成一张,如果每张图片都有不同的超链接,可通过CSS便宜响应鼠标点击操作,构造不同的URL。
使用浏览器缓存
CSS、JavaScript、Logo、图标这些静态资源文件更新频率都比较低,而这些文件几乎是每次http请求都是需要的,如果将这些文件缓存在浏览器中,可以较好地改善性能。通过设置HTTP头中Cache-Control和Expires属性,可设定浏览器缓存时间。
有时候静态资源文件变化需要及时或者立马进行更新,这种时候可以通过改变文件名来实现,即更新JavaScript文件而并不是更新JS文件内容,而是生成一个新的JS文件并更新HTML文件中的引用。
在更新静态资源的时候,应采用批量更新方法,比如更新10个图标文件,不宜把10个文件一次全部更新,而是一个一个文件逐步更新,并有一定的间隔时间,以免用户浏览器突然大量缓存失效,集中更新缓存,造成服务器负载骤增、网络堵塞的情况。
启用压缩
服务器端进行压缩,浏览器端对文件进行解压,可有效减少通信传输的数据量。
CSS放在页面最上面、JavaScript放在页面最下面
浏览器会下载完全部CSS之后才对整个页面进行渲染,因此最好的做法是将CSS放在页面最上面。JavaScrpit则相反,浏览器在加载JS后立即执行,有可能会阻塞整个页面,造成页面显示缓慢,因此放在页面最下面。但如果页面解析时要用到JavaScprit,这时候放在底部就不合适了。
减少Cookies传输
一方面,Cookie包含在每次请求和响应中,太大的Cookie会影响数据传输。另一方面,某些静态资源文件的访问,发送Cookie没有意义,可以将静态资源使用独立域名访问,避免请求静态资源时发送Cookie,减少Cookie传输次数。
CDN(内容分发网络)本质上也是一中缓存。部署在网络运行商机房。
反向代理服务器既可以保护网站安全的作用,又可以通过配置缓存功能加速Web请求。还可以加上负载均衡技术。
优化手段:缓存、集群、异步。
网站性能优化第一定律:优先考虑使用缓存优化性能。
缓存指将数据存储在访问速度高的存储介质中。一方面,因为访问速度高可以减少时间。另一方面,存储已经计算过的数据,就不用再重复进行计算可以直接使用,减少时间。
缓存的本质:一个内存Hash表。数据缓存以Key,Value的形式存储在Hash表中。
计算Hash表的索引下标,做简单的就是余数法,Hash表索引 = HashCode % Hash表长度。
缓存主要用来存储读写比例高但是变化很少的数据。
先从缓存中读取,如果读取不到或者数据失效,在访问数据库,并将数据写入缓存中。
避免以下几种情况:
缓存部署在多个服务器组成的集群种。其架构有两种方式:JBoss Cache为代表的需要同步更新的分布式缓存;Memcached为代表的互不通信的分布式缓存。
使用消息队列将调用异步化。
不使用消息队列,用户请求数据直接写入数据库,造成压力。使用消息队列后,请求发送给消息队列后彼便立即返回,再由消息队列的消费者进程从消息队列中获取数据,异步写入数据库,具有很好的削峰作用。
使用负载均衡技术为一个应用构建一个有多台服务器组成的服务器集群,将并发访问请求分发到多台服务器上。
使用多线程的主要原因是:IO阻塞和多CPU。
解决线程安全的主要手段:
减少开销很大的系统资源的创建和销毁。比如:数据库连接、网络通信连接、线程、复杂对象等。从编程角度,资源复用的两种模式:单例模式和对象池。
对象池:数据库连接对象创建好后,将连接对象放入对象池容器中,应用程序要连接的时候,就从对象池中获取一个空闲的连接使用,使用完毕后再将该对象归还到对象池中即可。
数据结构
垃圾回收
以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。
B+树是专门针对磁盘存储而优化的N叉排序树,以节点为单位存储在磁盘中。从根节点开始查找所需节点编号和磁盘位置。将节点一次一次加载到内存中查找,直到找到所需数据。
LSM树可以看做N阶合并树。数据写操作(插入、修改、删除)都在内存中进行,这些数据在内存中仍然还是一棵排序树,当数据量超过设定内存阈值后,会将这棵排序树和磁盘上最新的排序树进行合并。当这棵排序树的数据量也超过设定的阈值后,和磁盘下一级的排序树继续合并。合并过程中会用最新的数据覆盖旧的数据。数据读操作总是从内存中的排序树开始搜索,如果没有找到,就从磁盘上的排序树顺序查找。
RAID磁盘阵列,改善磁盘的访问延时,增强磁盘的可用性和容错能力。
HDFS(hadoop分布式文件系统)在集群上进行数据并发读写和备份。
HDFS以块(Block)为单位管理文件内容,一个文件被分为n个块。写文件时,每写完一个块,HDFS就会将其自动复制到另外两台机器上,保证一个块有3个副本。相当于RAID1功能。
对文件进行处理计算时,通过MapReduce并发计算任务框架,可以启动多个计算子任务(MapReduce Task),同时读取文件的多个块。相当于RAID0功能。
网站不可用时间(故障时间) = 故障修复时间点 - 故障发生(报告)时间点
网站年度可用性指标 = ( 1 - 网站不可用时间/年度总时间)x 100%
实现高可用架构的主要手段是数据和服务的冗余备份以及失效转移,一旦某些服务器宕机,就将服务切换到其他可用的服务器上,如果磁盘损坏,则从备份的磁盘读取数据。
典型分层模型是三层:应用层、服务层、数据层。各层次相互独立。应用层:负责具体业务逻辑处理;服务层:提供可复用的服务;数据层:负责数据的存储和访问。
中小型网站实际的部署如下图:
复杂大型网站架构:
应用层:处理网站的业务逻辑。显著特点:应用的无状态性。
无状态:应用服务器不保存业务的上下文信息,仅仅根据每次请求提交的数据进行相应的业务逻辑处理,多个服务器都是完全对等的,请求提交到任意服务器所得到的结果都是完全一致的。
无状态的一个好处就是,对于任何一台服务器来进行处理都会返回相同的结果,于是可以利用负载均衡机制将请求转发到任意一台服务器上。
负载均衡:实现服务器可用状态实时监控、自动转移失败任务。
负载均衡器通过心跳监测机制发现服务器失去响应,就会将请求发送到其他服务器上。
单机情况下:Session可由Web容器(如JBos)管理
集群环境下:
每台服务器都保存搜索用户的Session信息,获取Session信息就可以在本机上获取,但是会占用服务器和网络的大量资源。
负载均衡利用源地址进行Hash算法,将同一IP地址的请求分发到同一台服务器上(也可以根据Cookies信息将同一个用户的请求总是分发到同一台服务器上,这时候负载均衡服务器必须工作在HTTP协议层上)。
一旦某台服务宕机,保存了的session信息也就不复存在了。
浏览器将记录Session信息的Cookies一起发送。
缺点:Cookies大小有限;每次传输都需要传输Cookies,影响性能;用户关闭Cookies访问就不会正常。
独立部署Session服务器(集群)统一管理Session。
高可用服务器策略:
(上面讲完了高可用架构的三层的前面两层:应用层、服务层,下面就开始讲:数据层)
保证数据高可用的手段主要是:数据备份和失效转移机制。
数据备份:保证数据有多个副本,任意副本失效都不会导致数据的永久丢失,从而实现数据完全的持久化。
失效转移机制:保证一个服务不可访问时,可以快速切换访问数据的其他副本,保证系统可用。
高可用数据的含义:
数据持久性、数据可访问性、数据一致性。
CAP原理:一个提供数据服务的存储系统无法同时满足数据:一致性(Consistency)、可用性(Availibility)、分区耐受性(Patition Tolerance,系统具有跨网络分区的伸缩性)这三个条件。
冷备份:定期将数据复制到某种存储介质上。
热备份:分为异步热备和同步热备。
异步方式:写入操作异步完成。应用程序收到数据服务系统的写操作成功响应时,只写成功了一份,存储系统将会异步地写其他副本(这个过程可能会失败)。
存储服务器分为主存储服务器(Master)和从存储服务器(Slave),应用程序正常情况下只连接主存储服务器,数据写入时,由主存储服务器的写操作代理模块写入本机存储系统后立即返回写操作成功响应,然后异步线程将写操作数据同步到从存储服务器。
同步方式:多分数据副本的写操作同步完成。
失效转移:集群中的某台服务器宕机,应用程序针对这台服务器的所有读写操作都需要重新路由到其他服务器,保证数据访问不会失败。
失效转移操作的三个组层部分:失效确认、访问转移、数据恢复。
判断服务器宕机是系统进行失效转移的第一步,系统确认一台服务器是否宕机的手段:心跳检测和应用程序访问失败报告。
对于应用程序的访问失败报告,控制中心还需要再发一次心跳检测进行确认。
确认某台服务器宕机后,就需要将数据读写访问重新路由到其他服务器上。
恢复到系统设定的副本数目。
每次关闭的服务器都是集群中的一小部分,发布后然后重启并不会对用户的使用产生多大影响。
使用自动化测试工具或者脚本完成测试。比如Selenium工具。
将应用先发布在预发布服务器上,预发布服务器都部署在相同的物理环境。
网站应用中强调一个处理错误的理念是快速失败,即如果系统启动时发现问题就立刻抛出异常,停止启动让工程师介入排查错误,而不是启动后执行错误的操作。
SVN/Git
发布不是一次性全部都发布到集群中,而是将集群分成几批,每天只发布一部分服务器,观察运行稳定没有故障,如果发现问题,只需要回滚已发布的一部分服务器即可。
系统报警、失效转移、自动优雅降级
两类:一类是:根据功能进行物理分离实现伸缩,也就是不同的服务器部署不同的服务;一类是:通过集群实现伸缩,也就是集群内部署多台服务器部署相同的服务。
从一台服务器中不断剥离出不能功能部署在不同服务器上。
纵向分离:将业务处理流程上的不同部分分离部署,实现系统伸缩性。
横向分离:将不同的业务模块分离部署,实现系统伸缩性。
集群伸缩性分为:应用服务器集群伸缩 和 数据服务器集群伸缩(又可以分为:缓存数据服务器集群和存储数据服务器集群)。
主要通过负载均衡实现应用服务器的伸缩性
需要两次请求服务器才能完成一次访问,性能较差;重定向服务器自身的处理能力有限可能成为瓶颈,整个集群的伸缩性规模有限;使用HTTP 302响应码重定向,有可能是搜索引擎判断为SEO作弊,降低搜索排名。使用该方法不多见。
DNS是多级解析,这也就带来了不易更新的问题。而且DNS负载均衡的控制权在域名服务商那里,不易进行改善和管理。
网络层修改请求不标地址进行负载均衡。
修改mac地址进行负载均衡。
Web服务器集群中所有服务器的虚拟IP地址都和负载均衡服务器的IP地址相同,只修改目的mac地址。
分布式缓存和应用服务器集群伸缩性设计不同,不能使用简单的负载均衡手段来实现,必须要让新上线的缓存服务器对整个分布式缓存集群影响最小,也就是说新加入的缓存服务器后应使整个服务器集群中已缓存的数据尽可能还被访问到。
缓存数据<“Beijing”,DATA>通过API输入到路由算法模块,来得到一台服务器编号,进而得到机器的IP地址和端口,然后将数据写入到相应编号的缓存服务器中。
简单的路由算法可以使用余数Hash。但是当添加缓存服务器的时候会造成缓存不命中的情况。
使用一致性Hash环。
不同于缓存服务器伸缩性设计,数据存储服务器伸缩性主要保证的是数据的可用性和正确性。分为关系数据库集群的伸缩性设计和NoSql数据库的伸缩性设计。
比较成熟的支持数据分片的分布式关系数据库产品:Amoeba和Cobar。
最广泛使用的是HBase。
HBase的伸缩性主要依赖于可分裂的HRegion和可伸缩的分布式文件系统HDFS实现。
数据以HRegion为单位进行管理。HRegionServer是物理服务器,每个HRegionServer可以启动多个HRegion实例。所有的HRegion信息都会保存在HMaster上,也可能会同时启动多个HMaster,通过Zookeeper来选出一个主服务器。
应用程序通过Zookeeper获得主HMaster地址,数据Key值得到HregionServer服务器地址,然后请求该服务器上的HRegion获取数据。
典型的事件驱动架构就是生产者消费者模式。常用的手段就是分布式消息队列。
传统的关系数据库再设计表的结构的时候,就需要指定schema,数据结构难扩展困难。
解决方法:NoSql使用ColumnFamily(列族)。
- API接口:给开发者使用的一组API,其形式可以是RESTful、WebService、RPC等;
- 协议转换:将API输入转换成内部服务可以是别的形式,并将内部服务返回封装成API格式;
- 安全:除了一般安全手段(身份识别、权限控制等)还要分等级访问带宽限制。
- 审计:记录第三方应用的访问情况,并进行监控、计费;
- 路由:各种访问映射到具体的内部服务;
- 流程:将一组离散的服务组织成一个上下文相关的新服务,隐藏服务细节,提供统一接口供开发者调用。
XSS攻击、SQL注入攻击、CSRF、Session劫持。
XSS攻击:跨站点脚本攻击,指篡改网页,注入恶意脚本,当用户浏览网页时,控制用户浏览器进行恶意操作的一种攻击方式。
防御手段:
分为SQL注入攻击和OS注入攻击。
防御手段:
跨站点请求伪造。在用户不知情下,以用户身份伪造请求,其核心就是利用利用了浏览器Cookie或者服务器Session策略,盗取用户信息。
第3篇 案例(抽取的例子)
如何控制秒杀商品购买按钮的点亮?
购买按钮只有在活动开始的时候才会点亮,如果是动态页面可以在服务端构造响应页面输出,如果是静态页面,缓存在CDN、反向代理器上,甚至浏览器上,用户刷新页面,请求根本不会到达应用服务器。
解决办法:使用JS脚本控制,使用随机版本号,不会被浏览器、CDN和反向代理服务器缓存。
每次刷新都只是访问JS服务器,来重新加载JS文件。
如何只允许第一个提交的订单被发送到订单子系统?
控制进入下单页面的入口,只有少数用户进入下单页面,其他用户直接进入秒杀结束页面。
第4篇 架构师
2019-2-11 完