网站的可用性强调的是对最终用户的使用价值。它牵动着人们的神经,直接影响着公司的形象和利益,许多互联网公司都将网站的可用性列入工程师的绩效考核,与奖金、升迁等利益直接挂钩。
1 可用性的度量与考核
1.1 可用性度量
网站不可用时间(故障时间) = 故障修复时间点 - 故障发现(报告)时间点
网站年度可用性指标 = (1 - 网站不可用时间/年度总时间 ) * 100%
业界通常用多个 9 来衡量网站的可用性:
- 2 个 9 - 基本可用,不可用时间小于 88 小时(Twitter 的可用性不足 2 个 9)。
- 3 个 9 - 较高可用,不可用时间小于 9 小时。
- 4 个 9 - 具有自动恢复能力的高可用,不可用时间小于 53 分钟(QQ 服务)。
- 5 个 9 - 极高可用,不可用时间小于 5 分钟。
影响可用性的因素很多:
- 技术是否过硬。
- 设备资金的投入。
- 工程师的责任心。
- 好运气 O(∩_∩)O~
1.2 可用性考核
对外是服务承诺,对内是考核指标。针对每一个工程师的考核,使用的是故障分。故障分是一种对网站故障进行分类加权计算故障责任的方法。下面是故障分类权重示例:
分类 | 描述 | 权重 |
---|---|---|
事故级 | 严重故障,网站整体不可用 | 100 |
A 类 | 网站访问不顺畅或核心功能不可用 | 20 |
B 类 | 非核心功能不可用,或核心功能只有少数用户不可用 | 5 |
C 类 | 其他故障 | 1 |
故障分的计算公式:
故障分 = 故障时间(分钟) * 故障权重
在年初或考核季度的起始,会根据网站的可用性指标计算总的故障分,然后根据团队和个人的职责角色分摊故障分,这个故障分是管理预期的故障分。发生故障后,根据故障分类和责任划分,把故障分划定给相关的责任人。年末或考核季度结束时,团队和个人实际承担的故障分如果超过了年初分摊的故障分,绩效考核就会受到影响。
有时候,一个故障责任可能会由多个部门或团队来承担,那么故障分也会按照相应的责任分摊到不同的团队和个人。
网站的可用性跟技术、运营等相关各方的绩效息息相关,所以在架构设计与评审会上,关于系统可用性的讨论总是最费精力和时间的。
不同的企业文化与市场策略,也会影响到系统可用性的架构决策:
- 崇尚创新和风险的公司,对可用性的要求会低一些。
- 业务快速增长的网站忙于应对指数级增长的用户,对可用性的要求也会低一些。
- 服务于收费用户的网站对可用性很敏感,因为服务的不可用或关键用户的数据丢失,都有可能会导致收费用户的投诉甚至引来官司。
2 高可用的架构
企业级应用为了提高系统的可用性,通常会采用较昂贵的软硬件设备(小型机、Oracle 数据库等)。而互联网公司则更多地采用 PC 级服务器、开源的数据库和操作系统,因为成本低嘛O(∩_∩)O~
这些廉价的设备出现硬件故障是常态,所以在高可用的架构上就要保证发生硬件故障时,服务依然可用、数据依然能够访问与保存。
实现上述功能的主要手段是数据和服务的冗余备份以及失效转移。一旦服务器宕机,就将服务切换到其他可用的服务器上;如果磁盘损坏,就从备份的磁盘中读取数据。
典型的网站设计遵循基本分层架构模型:
- 应用层:处理具体的业务逻辑。
- 服务层:提供可复用的服务。
- 数据层:数据的存储和访问。
中小型网站在部署时,通常将应用层和服务层一起部署,而数据层则另外部署。
复杂的大型网站架构,划分的粒度会更小、更详细,结构更复杂,但还是能够把这些模块划分到这三层架构中的:
不同的业务产品会部署在不同的服务器集群上。而这些产品又会依赖一些共用的可复用的服务(比如账户服务等),这些服务也是部署在各自的服务器集群上的。至于最底层的数据层,也是部署在服务器集群上的哦O(∩_∩)O~
大型网站的分布式部署,使得位于不同层次的服务器具有不同的可用性设计:
- 应用层 - 为了应对高并发的访问请求,通常会使用负载均衡设备把一组的服务器组合为一个集群,共同对外提供服务。当负载均衡设备监控到某台服务器不可用时,就将其从集群中剔除,并把请求转发到集群中的其他可用的服务器。
- 服务层 - 应用层通过分布式服务框架来调用这一层提供的服务。分布式服务框架会在应用层的客户端程序中实现软件级的负载均衡,并通过注册中心监控这些提供服务的服务器。一旦发现某个服务器不可用,会立即通知客户端程序修改服务访问列表,剔除不可用的服务器。
- 数据层 - 写入数据的同时同步复制数据,把数据写入多台服务器,实现数据的冗余备份。当数据服务器宕机时,会把针对数据的访问切换到拥有备份数据的服务器上。
下面我们会针对每一层究竟如何实现高可用,进行一一探讨哦O(∩_∩)O~
3 实现高可用的应用层
应用层是处理业务逻辑,这一层的特点是无状态性。
无状态性指的是:多个服务器之间是完全对等的,即把请求提交给任意一台服务器,处理的结果都是一样的。
3.1 通过负载均衡实现失效转移
在业务量和数据量都较高的情况下,通过负载均衡,可以将流量和数据分摊到一个集群所组成的多台服务器上,以提高整体的处理能力。目前的负载均衡软、硬件都提供了失效转移功能。所以,如果集群的服务是无状态的对等服务时,负载均衡技术可以保证系统的高可用:
当 Web 服务器集群中的服务器都可用时,负载均衡服务器会把访问请求分发给任意一台服务器进行处理。负载均衡服务器通过心跳检测机制,如果发现某台服务器(比如图示中的 10.0.0.1)失去响应,就会把它从服务器列表中剔除。
为了保证高可用,即使某个应用访问量非常少,也必须至少部署两台服务器,通过负载均衡技术来构建一个小型集群。
3.2 服务器集群中的 Session 管理
业务总是有状态的,比如交易类网站,需要有购物车记录用户购买的商品。在 web 应用中,这些状态就称为 Session。在单机的情况下,Session 可以由部署在服务器上的 Web 容器(如 Tomcat)进行统一管理。但在使用负载均衡的集群环境中,要保证每次请求都能够获得正确的 Session 就很复杂。
在集群环境中,有这些手段可以进行 Session 管理:
3.2.1 复制 Session
这是早期的企业应用系统使用较多的一种 Session 管理策略。应用服务器开启 Web 容器的 Session 复制功能,然后在集群中的几台服务器之间同步 Session 对象,使得每一台服务器都保存着所有用户的 Session 信息:
3.2.2 绑定 Session
利用负载均衡的源地址 Hash 算法,将源于同一 IP 的请求分发到同一台服务器上。这样在整个会话期间,用户的所有请求都是在同一台服务器上进行处理,即把 Session 绑定到某台特定的服务器上。这种方法又称为 “会话黏滞”。
绑定 Session 的方案不符合系统高可用的需求,因为一旦某台服务器宕机,那么该服务器上的 Session 就不存在咯。即使把用户的请求切换到其他服务器也会因为没有 Session 而导致业务无法正常处理。所以很少有网站使用绑定 Session 的方案。
3.2.3 利用 Cookie 记录 Session
这种方式是将 Session 记录在客户端,每次请求服务器时,把 Session 放在请求中发送给服务器,服务器处理后,再把修改过的 Session 发送回客户端。
网站可以利用浏览器支持的 Cookie 记录 Session:
利用 Cookie 记录 Session 也有一些缺点:
- Cookie 有大小限制,记录的信息有限。
- 每次请求响应都要传输 Cookie,影响性能。
- 如果用户关闭了 Cookie,访问会变得不正常。
但由于 Cookie 简单易用,能够支持应用服务器的线性伸缩;而且大部分需要记录的 Session 信息又比较小。所以许多网站都或多或少地使用 Cookie 记录 Session。
3.2.4 Session 服务器
利用独立部署的 Session 服务器或集群,来统一管理 Session。应用服务器每次读写 Session 时,都需要访问 Session 服务器。
这样的方案是把应用服务器,分为无状态的应用服务器与有状态的 Session 服务器。
对于有状态的 Session 服务器,可以这样简单实现:利用分布式缓存、数据库等组件的基础上进行封装,使其符合 Session 的存储与访问的要求。
4 实现高可用的服务层
可复用的服务模块为产品提供了基础公共服务。这些服务通常都是独立分布式部署,由具体应用进行远程调用。它们也是无状态的服务,因此可以使用类似负载均衡的失效转移策略实现高可用。
还有这些高可用的策略:
4.1 分级管理
运维层面对服务器进行分级管理,核心应用与服务优先使用更好的硬件。
在服务部署上进行必要的隔离,避免故障的连锁反应:
- 低优先级的服务通过启动不同的线程或部署在不同的虚拟机上进行隔离。
- 高优先级的服务需要部署在不同的物理机上,核心服务和数据最好是部署在不同地域的数据中心上。
4.2 超时设置
在应用中设置调用服务的超时时间,一旦超时,通信框架就抛出异常。应用根据服务调度策略,可以选择继续重试或者将请求转移到提供相同服务的其他服务器上。
4.3 异步调用
应用对服务的调用,通过消息队列的异步方式实现。应用程序会将用户注册信息发送给消息队列服务器后,立即返回用户注册成功的响应。而多个消费者任务(记录用户信息到数据库、发送注册成功邮件、开通权限)会从消息队列中获取用户注册信息后异步执行。
注意:不是所有的的服务调用都可以使用异步调用模式。比如 “获取用户信息的服务” 或 “必须确认服务调用成功才能继续下一步操作的服务” 等,都不适合使用异步调用模式。
4.4 服务降级
网站访问的高峰期,服务可能因为大量的并发调用而导致性能下降,严重时甚至会导致服务宕机。所以为了保证核心应用与服务的正常运行,我们必须对某些服务进行降级。降级有两种手段:
- 拒绝服务 - 拒绝低优先级应用的调用,减少并发数,以确保核心应用;或随机拒绝部分请求,以节约资源。
- 关闭功能 - 关闭部分不重要的服务,或关闭服务内不重要的功能,以节约资源。比如淘宝在 “双十一” 促销期间就使用了这种方法:它在系统最繁忙的阶段关闭了 “评价”、“确认收货” 等非核心服务,以保证核心的交易服务。
4.5 冥等性设计
应用调用服务失败后,会把请求发给其他的服务器,但这个失败可能不是真的!比如服务实际已处理成功,但因为网络故障,所以应用没有收到响应,那么这时应用所重新提交的请求,就是在重复调用服务!如果这是一个转账服务,那么后果就很严重啦。
重复调用服务是不可避免的,所以服务层必须保证重复调用服务与调用一次服务的结果是相同的,即服务具有冥等性。
比如转账交易的操作,就需要通过交易编号对调用的服务进行有效性校验,保证调用是有效的才能继续执行。
5 实现高可用的数据层
对网站而言,最高贵的资产是多年运营积累下来的数据(用户数据、交易数据、商品数据等)。所以保护了数据就是保护了企业的命脉。
保证数据高可用的手段是数据备份和失效转移:
- 数据备份 - 保证数据有多个副本,任意副本的失效不会导致数据的永久丢失。
- 失效转移 - 当一个数据副本不可访问时,可以快速地切换到其他副本。
注意:缓存服务不是数据存储服务,缓存服务器的宕机所引起的缓存数据丢失而导致的服务器负载过高的情况,应该通过其他手段进行解决。可以让整个网站共享一个分布式缓存集群,应用只需要向共享缓存集群申请缓存资源即可。这样任何一台缓存服务器宕机所引起的缓存失效,都只会影响到缓存数据的一小部分,而不会对应用的性能和数据库负载造成太大的影响。
5.1 CAP 原理
为了保证数据的高可用,网站通常会牺牲数据的一致性。
高可用的数据有这几层含义:
- 数据持久性 - 保证数据可持久存储,即把数据写入可持久性存储的硬件的同时,还需要把数据备份成一个或多个副本。
- 数据可访问性 -把多个数据副本存储在不同的存储设备下,如果一个存储设备损坏,就需要把对数据的访问切换到另一个数据存储设备上。这个过程要尽量快,让客户不可感知。
- 数据一致性 - 在数据有多个副本的情况下,如果网络、服务器或软件出现故障,会导致部分副本写入成功,部分写入失败。这样就会造成各个副本之间存在数据不一致的情况。
CPA 原理认为:一个提供数据服务的存储系统无法同时满足数据一致性(Consistency)、数据可用性(Availability )、分区耐受性(Patition Tolerance)这三个条件。
大型网站中,数据规模会快速扩张,因此可伸缩性(分区耐受性)必不可少;规模变大后,机器数量也会变得庞大,这时网络和服务器故障就会频繁出现,所以网站使用了分布式系统保证应用高可用。一般情况下,大型网站大都会强化存储系统的可用性(A)和伸缩性(P),在某种程度上放弃一致性(C)。
CPA 原理告诉我们:在系统设计开发的过程中,如果不恰当地迎合各种需求,就会使设计进入两难境地,难以为继。
数据的一致性有这几个级别:
- 数据强一致 - 各个副本的数据在物理存储中总是一致的。
- 数据用户一致 - 各个副本的数据可能是不一致的,但当用户访问时,通过纠错和校验机制,可以确定一个一致的、正确的数据返回给用户。
- 数据最终一致 - 这是数据一致性中最弱的一种。系统经过一段时间(比较短的时间)的自我修复,数据会达到最终的一致。
数据强一致很难达到,所以网站会综合考虑成本、技术、业务场景等因素,使用数据监控和纠错功能,达到“数据用户一致”。
5.2 备份数据
早期的数据备份主要是冷备份,即定期地将数据复制到存储介质中(磁带、光盘等)并物理存档保存。
冷备份的优点是简单、廉价。缺点是不能保证数据最终一致。因为数据是定期复制,所以备份设备中的数据比系统的数据来的旧,如果系统数据发生丢失,那么从上一个备份点开始后,更新的数据就会永久丢失,不能恢复。
冷备份作为一个传统的数据保护手段,依然在网站日常运维中使用。但还需要进行数据热备份,以提供更好的数据可用性。
数据热备份有两种:异步热备和同步热备。
5.2.1 异步热备
异步热备指的是,使用异步方式写入多个数据副本。应用收到存储系统的写操作成功响应时,实际上只成功写入了一份,其他副本由存储系统异步写入(可能会失败):
存储服务器分为主服务器(Master )和从服务器(Slave )。应用只连接到主服务器。写入数据时,由主服务器上的写操作代理模块,把数据写入本机后立即返回写操作成功响应,然后通过异步线程把写操作的数据同步到存储服务器。
关系型数据库的热备机制就是 Master-Slave 同步机制。这样不但实现了数据备份,还改善了性能。因为在实践中,通常采用读写分离的方法来访问数据库。写操作只访问 Master 数据库,读操作只访问 Slave 数据库。
5.2.2 同步热备
同步热备指的是,同步写入多个数据副本,都写入成功后,应用才会收到写成功响应。如果应用收到写失败响应时(网络或系统故障),可能有部分或全部副本都已写入成功,这时就要进行纠错咯。
为了提高性能,应用的客户端会并发地向多个存储服务器同时写入数据,然后等待所有的存储服务器都返回操作成功的响应后,才会通知应用写操作成功。
这里的存储服务器是完全对等,这样有利于管理与维护。总的写操作延迟是那个响应最慢的存储服务器。
不管是关系型数据库还是 NoSQL 数据库,都提供了数据实时备份功能。
5.3 失效转移
失效转移指的是,服务器集群中,如果任何一台服务器宕机,那么应用所针对这台服务器的所有读写操作,都需要重新路由到其他服务器,保证服务依然是可用的。
失效转移有这些部分:
1.失效确认
系统通过心跳检测和应用访问失败的报告来确认某台服务器是否宕机:
对于应用程序的访问失败报告,控制中心还需要再发送一次心跳检测对服务器进行确认,以免应用程序判断错误。因为一旦进行失效转移,就会出现存储的多份数据副本不一致,那么后续就要对这种情况进行一系列复杂的纠错操作。
2. 访问转移
确认服务器宕机后,就要把数据的读写访问重新路由到其他的服务器上。这又分为两种情况:
- 存储完全对等:直接切换到对等服务器上。
- 存储不对等:重新计算路由,选择健康的服务器。
3. 数据恢复
某台服务器宕机后,数据存储的副本就会减少,这时系统会从健康的服务器上复制数据,把副本的数目恢复到系统设定的值。
6 高可用质量保证
6.1 发布网站
网站的发布实际上与服务器的宕机效果相当,因为需要关闭服务器上原有的应用,然后重新部署启动新的应用。
但网站的发布毕竟是一次提前预知的“服务器宕机”,因为过程可以更柔和,对用户的影响可以更小。我们通常使用脚本来完成发布。
因为每次关闭的服务器只是集群中的一小部分,而且在发布完成后可以立即访问,所以整个发布过程并不影响用户的使用。
6.2 自动化测试
代码在发布到服务器之前需要进行严格的测试。即使发布的新功能都是在原有系统的功能上进行小幅的增加,也需要对整个网站功能进行全面的回归测试。此外,还要针对各种浏览器进行兼容性测试。
大部分网站都是采用 Web 自动化测试技术。比较流行的是 Selenium。它运行于浏览器,可以模拟用户的操作进行测试,因此 Selenium 可以同时完成 Web 功能测试和浏览器兼容性测试。
大型网站会开发自有的自动化测试工具,实现一键部署、生成测试数据、执行测试和生成测试报告等测试过程。测试工程师的编码能力毫不逊于软件工程师哦O(∩_∩)O~
6.3 预发布过程
即使经过了严格测试,部署到现网后还是有可能出现问题。因为测试环境与现网环境不同,特别是应用可能会依赖的其他服务,诸如数据库、缓存以及第三方的服务(短信网关、网银接口等)。
因此在发布时,可以先把包发布到 “预发布服务器” 上,这样开发与测试工程师就可以在预发布服务器进行验证咯,一般会测试一些典型的业务流程,确认系统没有问题后才会正式发布。
预发布服务器与正式服务器的不同之处是:外部用户无法访问(没有配置在负载均衡服务器上)。
预发布服务器与线上的正式服务器部署在相同的物理环境上(同一个数据中心或同一个机架,如果使用虚拟机,甚至可以部署在同一个物理服务器上),使用相同的线上配置,依赖同样的外部服务。工程师在开发机上配置 hosts 文件绑定域名,这样就可以访问到预发布服务器咯。如果预发布服务器执行的验证通过的话,基本上可以确保部署到线上的正式服务器没有问题。
但有可能会因为预发布验证而引入新的问题。因为预发布服务器连接的是真实的生产环境,所以,所有的预发布验证操作的都是真实有效的数据,这些操作会引起某些不可预期的问题。比如上架了一个商品,可能真的会有用户过来购买,如果不能发货,那就惨了哦。
注意:在应用中应该强调 “快速失败” 的错误理念。即如果在系统启动时发现问题就立刻抛出异常,让工程师可以尽快介入解决问题。
6.4 代码控制
代码控制的核心问题是如何进行代码管理,即能保证代码发布的版本稳定正确,又能保证不同团队的开发互不影响。
目前大部分网站的源代码版本控制工具是 SVN 与 Git。
SVN 的代码控制发布的方式有两种:
1. 主干开发、分支发布
修改都在主干(trunk)上进行,在需要发布的时候,从主干上拉出一个分支(branch)发布,这个分支就是一个发布版本,如果这个版本发现了 bug,就继续在该分支上修改发布,并将修改合并(merge)回主干,直到下一次发布。
2. 分支开发、主干发布
任何修改都不得在主干上进行。需要开发新功能或修复 bug 时,就从主干上拉下一个分支进行开发,开发完成并测试通过后,合并回主干,然后从主干上发布,主干上的代码永远是最新发布的版本。
“主干开发、分支发布” 的方式,主干代码反映的是目前应用的状态,便于管理和控制,也有利于持续集成。
“ 分支开发、主干发布” 的方式,各个分支独立进行,互不干扰,因此可以同时进行不同发布周期的执行过程。
目前网站应用主要使用的是 “ 分支开发、主干发布” 的方式:
同一个应用,有可能 A 项目发布时, B 项目还在开发中,那么“ 分支开发、主干发布” 的方式,只需要将 A 项目的分支合并回主干即可,这样可以不受 B 项目发布时间的影响。
Git 目前正逐步取代 SVN。虽然 Git 学习成本较高,但它对分布式开发和分支开发等有更好的支持,而且可以更容易在各个开发分支上及时反映出主干上的最新更新(代码变得更容易合并啦)。相信 Git 迟早会成为网站标准版本的控制工具。
6.5 自动化发布
很多网站选择周四作为发布日,这样一周的前三天可以做好发布准备,后面一天可以挽回错误。如果选择的是周五,那么如果发现了问题,就必须周末来加班咯。
使用 “火车发布模型” 可以有效地控制发布故障、减少发布日的加班问题。
火车发布模型是这样的:可以把应用的发布过程看做是一次火车旅行。旅行路线上有若干个站点,每一站都要进行例行检查。不通过的项目就下车,剩下的项目继续旅行,直到达到终点(应用发布成功)。
火车发布模型是基于规则驱动的流程,所以可以自动化。因为人工干预的越少,自动化程度就会越高,引入故障的可能性也就越小。
6.6 灰度发布
应用发布成功后,仍可能因为软件问题而引入故障。这时候就要做发布回滚,即卸载刚刚发布的软件,将上一个版本的软件包重新发布,复原系统。
大型网站的服务器集群规模非常大,有的甚至超过一万台。一旦发生故障,回滚也需要花费很长的时间才能完成。所以大型网站一般使用灰度发布模式:即把集群服务器分为若干部分,每天只发布一部分服务器,观察运行稳定没有出现故障,那么第二天再继续发布另一部分的服务器,这样持续几天才会把整个集群都发布完毕。期间如果发现问题,就只需要回滚那些已发布的那部分服务器就好啦O(∩_∩)O~
灰度发布模型也常用于用户测试,即在部分服务器上发布新版本,其余服务器保持旧版本,然后监控用户的操作行为,通过用户的体验报告,就可以比较出用户对新旧两个版本的满意度,以确定最终的发布版本。这也被称为 AB 测试。
7 运行监控
没有被监控的系统不能上线! 因为运行监控对于网站运维与架构设计优化至关重要。
7.1 采集监控数据
- 收集用户行为日志
用户行为日志指的是用户在浏览器所做的所有操作以及用户所在的操作环境(操作系统与浏览器的版本、IP 地址、页面访问路径、页面停留时间等)。
有两种收集对象:
- 服务端日志 - 开启 web 服务器的日志记录即可。缺点是可能出现信息失真,比如 IP 地址可能是代理服务器的地址。
- 客户端浏览器日志 - 嵌入专门的 JavaScript 脚本就可以收集用户真实的操作行为。缺点是麻烦,因为需要在页面中嵌入特定的脚本。
大型网站的用户日志数据量惊人,数据存储与计算的压力都很大,所以许多网站都逐步开发了基于实时计算框架 Apache Storm 的日志统计与分析工具。
2.监控服务器性能
运维人员在初始化系统时统一部署,收集服务器性能指标。根据监控到的数据,运维工程师可以合理安排服务器的集群规模,架构师可以及时改善系统的性能和调整伸缩性策略。
目前使用广泛的性能监控工具是 Ganglia,它支持大规模的服务器集群,并支持以图形的方式在浏览器中展示实时的性能曲线。
- 运行数据报告
应用需要在代码中加入采集运行数据的逻辑,汇总后统一显示。
7.2 监控管理
- 系统报警
如果监控的某项指标超过了阈值,那就意味着系统可能将要出现故障,这时就要对相关人员进行报警。
监控管理系统可以配置报警的阈值和值守人员的联络方式(通过邮件、即时通信工具、手机短信等方式),及时发出预警。
- 失效转移
监控系统在发现故障时应该主动通知应用,及时进行失效转移。
- 自动优雅降级
优雅降级指的是,网站为了应付突发的访问高峰,会主动关闭一部分功能,释放系统资源。
自动优雅降级的监控系统是一种柔性架构的理想状态:监控系统会实时监控所有的服务器,如果发现某部分的应用负载过高,那么就会适当卸载低负载应用的一部分服务器,重新安装启动高负载的应用,使得应用负载总体均衡。如果所有应用的负载都很高,那么就会自动关闭一部分非重要的功能,保证核心功能的正常运行。
对公司而言,可用性关系到了网站的生死存亡;对个人而言,可用性关系到了个人的绩效升迁。所以一定要重视呀O(∩_∩)O~