集中花两天时间把这本《大型网站技术架构》看完了,分章节来记录一些干货。本书可以作为架构师入门的第一本书,因为很少涉及到具体的编程或者系统设计,而是以一个宏观的角度来讨论大型网站的架构方案。可以让你从全局的角度了解架构师的工作和职责。做到心中有数。
一个好的网站架构需要具备以下几个特点:高可用、高性能、易扩展、可伸缩且安全。同时网站的访问特点符合二八定律,即:80%的业务访问集中在20%的数据上。网站的技术架构发展应该由其本身的业务发展来驱动,小型网站不应该过于关注高性能的网站架构,而应该从业务做起,当业务规模发展到一定程度时再考虑技术架构上的发展。大公司的技术架构只能作为参考,不应该盲目跟从,毕竟每家公司的业务流程都是不同的。有时要更加关注于业务层面是否得当,在确定业务流程合理的情况下再进行技术架构上的拓展。
上图为一个大型网站架构的最终演化结构图。一个网站架构的发展从最初的单机处理到最后的分布式集群、业务拆分细化,这整个过程中间经历着数次的迭代与重构:
初始版本:单机应用(数据库+应用系统+文件系统)
服务分离:多机系统(数据库、应用系统、文件系统各自独立到单独的机器)
增加缓存:解决 DB 压力(本地缓存+远程缓存集群,存放80%的常用数据)
应用集群:应用服务器使用集群(使用 Load Balance 调度集群压力)
读写分离:数据库读写分离,主从热备(分离读写操作,主从双备)
CDN/反向代理:提高静态文件传输效率(缓存在反向代理服务器或者CDN加速)
文件/数据集群:文件系统和数据库使用集群(全部服务化,对外部透明)
独立服务:比如微服务架构(独立出各个服务,服务内部单独架构,服务间通过 URL 通信)
常用的大型网站架构模式有:分层、分割、分布式、集群、缓存、异步(共享队列)、冗余、自动化、安全。其中分层和分割是进行后续架构模式的前提,同互联网的七层模型一样,高耦合低内聚的独立模块是灵活部署和弹性伸缩的前提。
分层:横向,从逻辑上分层,物理上可以在同一个机器上。比如将网站的结构分为视图层,控制层和数据层(MVC)。各个层次之间要保持相对独立的接口,职责单一明确。
分割:纵向,比如对业务的划分,大的业务部门分割成小的业务部门,每个部门独自负责本部门的业务以及技术架构。
分布式:分布式可以将网站架构的所有资源模块化,每个模块有自己单独的资源池来为自己提供计算,同时分布式也可以增加网站的可用性,增强和提高资源的处理效率。但带来的问题是数据一致性难以保证。
集群:将进行分布式架构后的各个模块以“多机集群”的方式代替“单机”模式,这样的好处是可以通过即时的灾备服务,同时大大提高计算效率。每个集群通过一个“Load Balance”透明化的对外提供服务。(*在使用集群的时候要注意对用户 Session 等信息的状态保存不应该与应用服务器放到一起)
缓存:主要包括 CDN、反向代理、分布式缓存和本地缓存。使用缓存的两个前提条件,第一是数据数据访问热点不均衡,把热点数据放入缓存;第二是数据在某个时间段内有效,不会很快过期。
异步(共享队列):这里我把异步改成“共享队列”,模块化后的业务组件在功能上没有任何的耦合,即组件直接没有功能的先后顺序。所有资源均放入一个共享队列中,由各个组件进行处理,处理后的资源仍会队列中。各个组件只需要负责队列中自己可以进行处理的资源即可。同时,“共享队列”还可以作为“压力容器”,在高并发访问时用来承载来不及处理的资源,防止资源的阻塞导致服务器宕机影响用户体验。
冗余:即一定程度上的服务器冗余运行,数据冗余备份。数据除了需要定期备份,存档的冷备份,还需要即时同步到从数据库实现热备份。
自动化:自动化部署、自动化代码管理、自动化安全检测、自动化监控和报警、自动化失效转移(将故障机器从集群中隔离出去)、自动化失效恢复、自动化降级(通过关闭一些不重要的服务来保证系统负载在一个安全的水平)、自动化资源分配。
安全:加密、验证码、防止攻击、风险控制。
高性能网站架构:下面我们主要围绕着如何从多个方面来进行“高性能网站架构”展开,性能优化小到一行代码的重构,大到服务器集群的重新架构。怎样通过各项数据指标来监控网站的实时性能?找出网站的性能“弱点”,并以此为目标进行迭代式的优化,才能逐渐将网站架构达到一个高性能的水平。
一、网站性能测试
网站的性能指标,既可以是开发人员客观的性能分析数据,测试指标。也可以是主观的终端用户体验感受。一般而言,我们用如下指一些标来衡定一个网站的性能水平:响应时间、并发数量、吞吐量、性能计数器。响应时间即从请求发出开始,到收到响应并解析成对应的可视化结果所花费的时间;并发数指系统能够同时处理的请求数量。吞吐量是指单位时间内系统能够处理的请求数量,常用的单位为TPS(每秒事务数)、HPS(每秒的 HTTP 请求数)、QPS(每秒数据库查询数);性能计数器为直观的数据指标,比如当前系统负载、对象与线程数、CPU /内存使用率、磁盘与网络IO等。理想的系统负载应该对应为系统的 CPU 数量,因为系统负载指当前正在排队被 CPU 处理的进程数量。
常见的测试方法分为:性能测试、负载测试、压力测试和稳定性测试。性能测试用来验证在资源可接受范围内,系统在一定压力下能否达到预期的性能指标。负载测试用来测试当系统资源满载时,系统能够达到的最大吞吐量。压力测试则是在超过系统安全负载的时候继续施加压力,测试直到系统奔溃所用时间的长短。而稳定性测试则是让系统在一定压力下运行一段时间,检测系统在不同时间点是否有资源的使用异常,以此来推断系统的稳定性。
在性能测试和负载测试中,系统的 TPS 随着压力的增加,值会不断增高。而在压力测试下,由于此时系统资源早已耗尽,更多的压力只会拖垮 CPU 的性能,因此此时的系统 TPS 会随着压力的不断增加而逐渐降低。而系统的响应时间在所有测试中都是随着压力的增加而逐渐增加的,不过在压力测试中,每对系统施加单位的压力,系统的响应时间会成倍的疯狂增长。
对于系统性能的优化,需要从一个请求出发,记录下请求达到每个关键节点(比如代理服务器,负载均衡服务器,应用服务器)的时间,分析哪段时间不合理,再通过分析监控数据来检测问题所在。
二、WEB 前端优化
合并资源、分离静态资源到独立域名(防止 Cookie 污染)、浏览器本地缓存、服务器 GZip 压缩、CDN 加速(ISP端)、使用反向代理(相当于 Gateway 主机,可缓存资源,还可以保护内部网络)等等。
三、应用服务器优化
应用服务器主要用来处理系统业务,是整个网站架构中的核心,也是最复杂变化最多的部分。常用的优化手段有:使用分布式缓存(网站优化第一定律:优先考虑使用缓存。缓存不适用于频繁修改的数据,不适用与没有热点访问的数据,并且容易产生脏读。系统的正常运行不应该依赖于缓存系统,初始的缓存系统可以先进行“缓存预热”,比如 Redis 在初始化时会从硬盘中读取数据放到内存。如果在日志中发现大量的无法命中缓存的请求,这可能是发生了“缓存穿透”,恶意用户持续高并发的访问系统不存在的资源,这些资源无法被缓存下来导致了穿透问题,一个简单的对策是将不存在的数据也缓存起来。常见的分布式缓存系统如 JBoss 和 Memcache,JBoss 的所有集群机器在数据改变时会在所有机器更新,而 Memcache 采用 Leader 的方式,各个主机间不进行通信,因此其线性伸缩不会影响缓存系统的性能)、使用共享队列、使用集群、代码优化(采用多线程,线程数一般与 CPU 数量成正比,使用无状态对象或者加锁来防止一致性问题,数据连接、通信连接等资源复用)
四、存储系统优化
使用 SSD 硬盘、使用 NoSql 数据库(由于 LSM 树的数据更新速度较 B+ 树更快)、 根据需求使用 RAID 磁盘阵列和 HDFS(RAID 可以在一定程度上改善磁盘的访问延迟同时增强可用性。但对于大量数据的存储需求,基于 MapReduce 可以进行并发任务处理的 HDFS 可能更加合适)
高可用的网站架构:下面主要围绕着如何从多个方面来进行“高可用的网站架构”展开,网站页面能够完整呈现在最终用户面前,需要经过很多个环节,任何一个环节除了问题,都可能导致网站页面不可访问。网络请求的情况千变万化,可能一个“突然来袭”的实时热点访问,就会把你的网站重重拖垮。
一、可用性度量
我们通常使用多少个9来衡量网站的可用性,比如4个9代表一个服务99.99%可用,即该需要保证在单位时间内只有0.01%的时间可以发生故障服务不可用。2个9与3个9的意思也同样如此。但对于网站整体而言,想要达到4个9甚至5个9的可用性,除了过硬的技术、大量的设备资金投入还需要有个好运气。
一般为了将网站的可用性指标转换成对应的责任度量下放到个人或者组织,我们一般使用“故障分”来对网站的单位时间故障进行加权计算,进而将责任分担下放到个人,加入其年度的绩效考核中。比如可以对不同级别的故障类型划分不同的权重分,再通过对应类型故障的发生时间进而得到该故障的故障分。
二、高可用的整体架构
我们一般将网站架构分为三层:应用层、服务层、数据层,应用层负责业务逻辑处理,服务层提供可复用的服务,数据层负责数据的封装与存储,各层之间相对独立。由于网站的架构资源中,硬件故障是最常见的问题。那么高可用架构的主要目的就是保证服务器在硬件故障时依然可用。主要手段是数据和服务的冗余备份以及失效转移。
位于应用层的服务器通常为了应对高并发的请求,会通过负载均衡(Load Balancer)组成集群对外透明的提供服务。负载均衡会通过心跳检测来监控服务器状态,当发现不可用机器时将其从集群中剔除,并将该机器的路由设置为不可用,同时所有请求将转发到集群内的其他机器。服务层的机器与应用层类似。位于数据层的服务器比较特殊,为了保证数据不丢失,我们需要在数据写入时同时对集群内的其他服务器同步复制数据,以保证数据的一致性和可用性。
三、高可用的应用
由于应用层主要负责对业务的处理,为了使用集群来提高应用服务的高可用性,我们将应用层设计成无状态的服务,即不在应用服务器本地保存用户的状态信息(比如 Session 信息)。这样的做的目的是为了让集群内的所有服务器对等,在负载均衡调度请求时可以无差别对待。而用户的状态信息我们会用专门的方式来进行管理。
由于业务总是有状态的,在单机情况下,我们将会话信息交由服务器上的 Web 容器来管理,但对于集群环境来说,我们通常用以下几种方式来处理:Session 复制(所有应用服务器在本地共享同一套会话信息,每一次新增的会话都会在整个应用集群内进行复制,只用于小型集群)、Session 绑定(负载均衡服务器将同一 IP 来源的请求绑定在固定的应用服务器上,又被称为“会话粘滞”,但不符合高可用的特性)、利用 Cookie(将用户的会话信息存储在客户端的浏览器中,安全性低、影响传输性能、受到浏览器限制)、Session 服务器(专门的 Session 集群,由负载均衡调度,可以利用分布式缓存/数据库来进行包装)。
四、高可用的服务
高可用的服务一定是独立的、可复用的。服务的设计同样需要遵循几个原则:分级管理(核心应用和服务优先使用更好的硬件,核心服务和数据需要部署在不同地域的数据中心,低优先级的服务甚至可以只使用多线程来隔离)、超时设置(设置服务的远程调用超时时间,某一台机器超过规定的响应时间即由集群 Leader 或负载均衡重新分配资源)、异步调用(将一次完整的业务流程拆分,比如发送成功邮件等任务,可以延后执行的步骤均放在消息队列中异步进行,即使用传统的消费者模式)、服务降级(在并发数较高的情况下,可以通过适当关闭不必要的低优先级服务来节约系统性能,或者通过随机拒绝服务的方式,将压力分散)、幂等设计(我们无法确定一次失败的服务请求是否真的失败了,为了避免服务的二次调用产生“非预期”的结构,我们需要将服务调用幂等化,即一次调用和多次调用产生的效果是一致的)。
五、高可用的数据
保证高可用数据的手段主要是数据备份和失效转移。一般为了保证数据高可用,我们肯能会牺牲另一个指标,即:”数据一致性“。高可用数据一般包括“数据持久性”,“数据可用性”和“数据一致性”三个指标。根据 CAP 原理,一个数据服务的存储系统是无法同时满足数据一致性(Consistency)、数据可用性(Availibility)和分区耐受性(Partition Tolerance)的。数据一致性即所有应用都能访问到相同的数据,可用性即任何时候,任何应用都可以进行数据读写,分区耐受性指系统可以跨网络分区线性的伸缩。由于 A 和 P 两个指标较 C 更为重要,我们既然放弃了数据的强一致性,退而求其次在不影响用户体验时,可以选择保障数据用户一致。
常用的数据备份方式分为热备份和冷备份,冷备份是一种古老而有效的数据保护手段,主要通过定期将数据复制到存储介质上并物理存档保存来保护数据。缺点是不能保证数据的最终一致(最弱的一致性,系统经过一段时间的自我恢复和修正最终达到一直),而且在数据备份时需要宕机。热备份是一种实时备份的数据保护方式,分为异步热备和同步热备。同时对于关系型数据库来说,热备机制的 Master-Slave同步机制还可以通过读写分离的方式来改善数据库性能。
失效转移一般通过心跳检测或者应用程序的访问失败报告来进行通知,控制中心在收到失败报告时会再次通过心跳检测来进行确认,如果确认失败则将该机器路由转移到其他可用机器上。
六、高可用的软件质量保证
比如我们在发布软件的新版本时一定要注意不能影响原有的线上正在运行中的服务。因此我们一般通过发布脚本每次只关闭一部分集群的机器,进行软件更新,然后启用。再关闭另一部分机器,重复上述过程。再软件发布之前还需要进行“预发布”,即先发布到线上集群中一台只有内网能够访问到的机器,但使用的是线上的数据,在通过内部测试无误后再发布到线上。
同时对于大型网站的软件发布,我们可以采用“灰度发布”的方式,即一段时间内只发布线上集群中的一部分机器,待观察一段时间没有问题后,再逐渐发布集群内的其他机器。会进行“灰度发布”的同时,我们甚至可以进行“AB测试”,新发布的机器作为对照组,查看新旧软件的用户反馈情况。
另一方面,也需要对网站进行全天候的实时的监控。比如监控服务端日志、客户端日志、运行数据报告(缓存命中率、平均响应延迟等)。设置要做到自动检测系统报警并向 Leader 机器反馈,可以即时做到系统失效转移,以及优雅降级(高并发高压力时自动关闭某些低优先级的服务)等操作。