`

读书笔记:《分布式JAVA应用 基础与实践》 第六章 构建高可用的系统

阅读更多

对于互联网或企业中的大型应用而言,多数要求做到 7*24 小时不间断运行。实际上要完全做到不太可能,但可尽量接近,各大网站或大型应用在总结一年的运行状况时,通常会有当年的可用性为 99.9% 这样的内容。

为实现类似的高可用,要避免系统中出现单点。

6.1 避免系统中出现单点

单点是指系统部署在单台机器上,一旦这台机器出现问题 ( 硬件损坏,网络不通等 ) ,系统就不可用。解决单点最常见的方法是采用集群。采用集群则要系统支持水平伸缩。如何做到水平伸缩,第 7 单会详细阐述。除水平伸缩外,还需做到以下两点:

1. 保证均衡使用集群内的机器

2. 保证当机器出现问题时,用户不会访问到问题机器

6.1.1 负载均衡技术

       为实现上述两点,通常采用负载均衡技术,其中又分为硬件负载均衡和软件负载均衡。前者由硬件芯片完成功能,后者则由软件来实现。

       无论是硬件还是软件负载均衡,都在系统中增加了负载均衡机器,负载均衡机器为避免自己成为单点,通常由两台购成,一台提供服务,一台 standby, 一旦提供服务的机器出现问题, syandby 这台自动接管。

选择实际业务处理机器的方式

负载均衡机器选择实际业务处理机器主要有如下几种方式

1.       随机 (Random) 选择

实现最简单,性能最高。可基本保证均衡。适用于处理各种请求时所需消耗的资源相差不是特别大的情况。

2.       Hash 选择

对应用层的请求信息做 hash ,再分发到相应的机器上,典型的应用场景是图片的加载。对请求的图片的 url hash ,基本可保证每次请求同一台机器,命中缓存,提升性能。不过,由于它要读应用层的信息,也要做 hash ,故需要消耗更多的 CPU 并导致些许的性能下降

3.       (Round-Robin) 选择

即根据地址列表按顺序选择。比随机多了一个同步操作,但这个同步操作非常短,性能损失较少。这种方式应用场景和随机方式差不多,目前的硬件和软件负载都支持,实际使用较多。

4.       按权重 (weight) 选择

根据机器配置不同分配不同权重,以更好地利用机器性能。

5.       按负载 (Load) 选择

即根据实际业务处理机器的负载来选择。适合于业务处理机器在处理多种请求时所需资源相差较大的情况。这种方式要求负载均衡机器定时采集业务处理机器的负载情况,给负载均衡机器增加了负担,且万一采集过程中出现较大延时或采集不到时,很可能造成严重不均衡,实际使用较少。

6.       按连接 (Connection) 选择

根据业务处理机器中连接数的多少来选择,应用于连接数不均衡的场景。这种方式要注意的是,一旦其中一台业务机器重启,且重启瞬间请求量巨大,如 10000 个,那么,这 10000 个请求会同时发到新启动的机器上,导致机器宕掉,因此这种方式实际应用较少

 

除以上的选址方式外,通常还会支持按 cookie 信息绑定访问相同的机器的方式,这样可把用户相关信息绑定到指定服务器上,避免引入分布式缓存等复杂技术,但带来的问题是一旦机器出现故障,在该机器上登录过的用户就要重新登陆了。

此外,实际场景中还有一个典型问题: web server 接到负载均衡机器分派过来的请求后,如可以处理,则分派线程来处理,如已达到最大线程数,则放入队列等待。如果前面的请求处理缓慢,后面分派过来的请求就有可能被丢弃或超时。 Twitter 在此时采用 unicorn 来解决。 Unicorn 可理解为到银行办事时的统一叫号,多窗口办事的方式。 Lighthttpd 也有一个支持这种方式的均衡算法 : Shortest Queue First.

响应返回方式

通常支持以下两种方式将响应返回求用户

1.       通过负载均衡机器返回

最常见的简易方式。这种方式,随着请求量上涨,负载均衡机器所受到的压力会迅速上升,特别是对于请求包小、响应包大的 Web 应用。

2.       直接返回至请求发起方

响应直接返回至请求发起方,以分散负载均衡压力,支持更大请求量。要达到这种效果,须要采用 IP Tunneling DR Direct Routing, 硬件负载均衡中又简写为 DSR:Direct Service Routing )方式。 IP Tunneling 方式要求负载均衡机器和实际业务处理机器都支持 IP Tunneling Direct Routing 方式要求负载均衡机器和实际的业务处理机器在同一个物理网段中,并且不响应 ARP

 

目前大多数系统 都支持 IP Tunniling Direct Routing 对环境要求较高,故通常 IP Tunniling 更适合。

 

除以上通用实现外,硬件负载和软件负载在实现时有一些细节的不同。

硬件负载均衡

硬件负载设备中最为知名的是 F5 Netscalar ,他们实现自动接管的方式是采用心跳线的方式。

硬件负载设备大部分操作都在硬件芯片上直接做(例如 HASH 计算),所以能支撑的量非常高,运行非常稳定,不过有以下两点需要注意

1.       负载设备的流量

一旦负载设备的流量达到上限,就会出现大量访问出错等异常问题,一此要注意监测,一旦接近上限,就必须立即扩充带宽或增加负载设备

2.       长连接

长连接方式的应用采用硬件负载设备很容易出现问题,当某台实际业务服务器重启同时又有大量请求进来时,所有的请求都发到健康的服务器上,并建立起长连接,导致重启后的机器只有很少请求或没有请求,造成严重的负载不均衡。可采用手工断开负载设备上某 VIP 所有连接的方法来避免,或只允许每个长连接执行一定次数的请求,当达阀值后即关闭此连接。

软件负载方案

最常用的为 LVS Linux Virtual Server ),多数情况下采用 LVS+Keepalived 来避免负载均衡机器的单点,实现负载均衡机器的自动接管。也可采用类似硬件负载设备的方式来实现,即采用心跳线 + 高可用软件来实现,其中应用最广的高可用软件为 heartbeat,       默认情况下 heartbeat 通过 UDP 方式来监测。

LVS 外,还用 HAProxy 应用较广。

去中心化实现负载均衡

无论是硬件还是软件负载均衡,都需要在请求的过程中引入了一个中间点,这会对性能、可用性造成一定影响,且由于负载设备在做水平伸缩时,要修改调用方应用,比较麻烦,于是业界出现了基于 Gossip 实现无中间点的软件负载。 Facebook 开源的 Cassandra 就是一个基于 Gossip 实现的无中心的 NOSQL 数据库。

去掉中间点后,请求 / 响应可直接进行,对于性能、可用性都会带来提升。同时,无中间点后,还可以将路由策略放在访问端,从而在不影响性能的情况下实现复杂的路由策略。

以上都是实现本地机器的负载均衡,当系统规模扩大到一定程度后,会要在不同地域建设机房,在不同地域的集群实现负载均衡的技术通常称为全局负载均衡 (Global Load Balance) 。各负载设备的硬件厂商通常提供了一种称为 GSLB(Global Server Load Balance) 的设备来实现,可避免由于一个地方的机房出现故障导致系统不可用,同时 GSLB 还可根据地理位置来选择最近的机器,这也在一定程度上提高了性能。

6.1.2          热备

集群要求系统本身支持水平伸缩,对于系统而言成本偏高,可采用热备作为替代方案。热备的情况下对外服务的机器只有一台,其它机器处理 standby 状态, standby 机器通过心跳机制检查对外服务机器的健康情况,当出现问题时, standby 机器即进行接管。

  除单机故障外,还须考虑整个机房出现不可用的情况,故引入多机房方案

使用多机房

       多机房对技术上的要求较高,难点主要为跨机房的状态同步。包括持久数据的同步、内存数据的同步和文件的同步。当机房不在同一个地方时,面临的最大问题是网络延时和各种异常情况,对实时要求高的应用是一个很大的挑战。

数据库数据的同步通常采用单 master slave 或多 master 方案。多 mashter 方案有多个写入点,实现较为复杂,通常采取两阶段提交、三阶段提交或基于 Paxos 的方式来保持多 master 数据的一致性。此外,还可选择 PNUTS 方式来实现,对多机房方案感兴趣的读者可进一步参考 google 工程师介绍 GAE 后端数据服务的 PPT

文件的同步和内存数据的同步方案和数据库数据同步的方案基本相同。

 

6.2 提高应用自身的可用性

应用通常要满足多种多样的功能要求,尤其是互联网应用在不断添加新功能的同时还必须保持高速发展,应用中难免出现 bug 。在这里介绍一下保障应用自身高可用的常用方法

 

6.2.1 尽可能地避免故障

       要做到这点,一方面要深入理解 JAVA 类库和框架,另一方面则是经验。以下是一些常见故障点形成的可用性设计原则,这些原则如下。

明确使用场景

一种是设计时过多地考虑复用,没仔细分析使用场景的不同而导致出现故障。另一种是设计时没有从场景去考虑,更多的是从纯技术角度去考虑,设计了很多不必要的功能,如不必要的系统扩展、系统功能等,将系统复杂化,容易造成故障。因此设计时应贴近使用场景,保持系统的简单。对复杂的功能,应分解为多个阶段来完成,保持每个阶段的简单。

设计可容错的系统

       要使系统具备高度的容错能力,主要从两方面进行掌控,一是 Fail Fast 原则,二是保证接口和对象设计的严谨。

       Fail Fast 原则是当主流程的任何一步出现问题时,都应快速结束整个流程,而不是等到后续才来处理。如系统启动阶段要从本地加载一些数据放入缓存,如加载失败,则直接退出 JVM ,避免启动后不可用;又如代码中判断传入参数非法时直接抛错。

       接口和对象设计的严谨最容易被忽略。通常我们在对外提供接口或对象时,总是假定一定会按照要求去使用,不采取任何的保护措施,这种情况下非常容易产生问题

设计具备自我保护能力的系统

对所有第三方依赖 ( 如对数据库的依赖、存储设备的依赖、其它系统提供的功能的依赖等 ) 的地方都应保持怀疑的态度,对这些地方做一些保护措施,使第三方出现问题时,对应用本身不产生太大的影响,如只是某功能暂时不可用。

限制使用资源

1.       限制内存的使用

主要是注意对 JVM 内存的使用限制,避免出现频繁 Full GC 或内存溢出的现象。一是注意避免集合过大,二要注意释放已经不用的对象引用,如使用线程池的同时使用了 ThreadLocal, 在线程使用结束后,要进行 threadLocal.set(null) 操作,否则 ThreadLocal 里的对象就会一直等待线程退出后才能回收。

2.       限制文件的使用

典型的是日志文件的使用,一是要控制日志文件的大小,二是要控制写日志的频率。

3.       限制网络的使用

对分布式 JAVA 应用而言,网络的使用是其中重要的一环,对网络资源的限制使用也是非常重要的,具体反映在以下两方面

连接资源

客户端基本都会采用连接池方式避免对服务端创建过多连接,服务端自身也必须控制避免过多连接,以避免出现资源不足导致服务不可用。

操作系统 sendBuffer 资源

在向服务器发送流时,都是先放入操作系统的 sendBuffer 区,而 sendBuffer 区是有限的,因此要适当控制往操作系统 sendBuffer 区写入的流数量,避免出现问题。

另一方面,由于在发送数据时都要先序列化流,流在发送到操作系统 sendBuffer 区前会在 jvm 中存活,因此要注意不要有太多这样的流存在,避免 JVM 内存耗光。

通常采用流控的方法来避免 sendBuffer 区资源和 JVM 内存消耗过多,通常做法有当到达某阀值时直接拒绝发送,或延迟发送。

4.       限制线程的使用

通常使用线程池避免无限制的使用线程。

从其它角度避免故障

除了以上从设计角度的考虑外,编码过程中,还须确保对所用到的框架及 JAVA 类库的实现都有较深的掌握,同时,还要不时地组织代码 review ,结合知识库及团队的智慧尽可能地避免代码中的潜在 bug

另外,测试阶段也要注意功能测试和压力测试,保障系统的基本可用性及高压力下的可用性。

部署阶段,也要注意不要对系统的可用性产生影响,这个说易行难,如一个典型的部署步骤为先停集群中一半的机器,更新应用包,然后重启。如果更新或启动的过程太长,另一半提供服务的机器可能就无法支撑全部的流量,最终导致宕机,因此要注意尽量平滑部署。另外,尽量做到自动化部署,虽然这点比较困难。

 

6.2.2 及时发现故障

无论系统怎样完善,要完全避免故障基本不太可能,因此及时发现故障就至关重要。因此,需要对系统的日志的记录和分析来做报警,包括单机状况的报警、集群状况的报警及关键数据的报警。

单机状况的报警用于报告产生了致使影响的点,如 CPU 使用率过高,某功能点失败率过高,依赖的第三方系统连接出现问题等。

集群状况的报警,通常是在集群的访问状况、响应状况等指标和同期或基线指标对比出现大偏差时发现。

关键数据的报警通常针对系统中的关键功能,如交易类的系统,其最关键的数据是实时交易额,因此当交易额数据和基线指标对比出现大偏差时,就需要报警。

即时发现故障在各大互联网公司中都非常重视,从 Twitter Facebook 、盛大、 51.com 等公开的 PPT 来看,可以看到它们都拥有一套强大的监测系统,通常具备以下特征:

1.       有系统依赖关系的分析,便于找到故障的 root cause

2.       有系统全局状态图显示,既便于找到故障的 root cause ,也能根据故障点找到目前所影响的功能点

3.       能根据报警规则、级别进行报警,对单机直接处理,避免单机扩散到全局

4.       根据报警信息的记录,跟进记录分析报警的原因及后续的处理步骤,方便为将来再次出现时更快速的处理。

 

6.2.3 及时处理故障

       常见的故障快速处理措施有:

1.       自动修复

通过学习总结发生过的各类故障处理措施,将来再发生时自动智能化处理。难度较大,可先只让系统只提出建议,再逐步完善。

2.       执行风险点应对措施

手工修复是最保险的方式,但机器数量太多的情况下比较麻烦,可通过一个集中点发送命令给所有机器,快速修复故障。

3.       全局资源调整

当某集群的压力过大时,可通过调整资源来重新平衡全局的资源,保障系统的可用性。更先进的是根据系统 QoS Quality of Service )来自动平衡全局资源,所谓 QoS 通常是指每秒需要支撑多少的请求量,但 QoS 也可能是随时段不同是变化的指标。

4.       功能降级

在无法快速修改的情况下,先把系统的某些功能关闭,以保证核心系统的可用。

5.       降低对资源的使用量

如正常情况下数据库连接池最大是 10 个,出故障时调整为 5 个,同时缩短数据库连接的等待时间,又或者是在资源紧张的情况下关闭消耗资的操作

 

处理故障后,应注重分析和总结故障发生的根本原因,放入知识库,一方面是为了再碰到类似故障时能自动处理,另一方面是为了积累经验,形成更多的保障可用性的设计原则, review 原则等,尽可能地避免故障再次发生。

 

6.2.4 访问量及数据量不断上涨的应对策略

对于访问量不断上涨的情况,通常应对的措施是拆分系统和水平伸缩,拆分系统后简化了各个系统的功能,并且使其拥有更多的资源,从而提升其所能支撑的用户访问量。通常系统依照功能进行拆分,例如 eBey 将系统拆分为交易、商品、评价等。

随着数据量的不断上涨,通常采取的是拆分数据库、拆分表及读写分离。这些技术将在下一章中详细介绍。

 

分享到:
评论

相关推荐

    《Java高手真经:Java Web高级开发技术》读书笔记模板.pptx

    本资源为《Java高手真经:Java Web高级开发技术》读书笔记模板,涵盖了Java EE开发中的各种分布式与业务核心技术。该资源分为三部分,分别是Java高手真经(高级编程卷):Java Web高级开发技术读书笔记模板、Java ...

    Java_se基础毕向东老师全程笔记

    ### 第六章:常用类API - **字符串处理**:`String`类和`StringBuilder`类。 - **日期时间操作**:`Date`类、`Calendar`类以及`java.time`包中的类。 ### 第七章:集合框架(容器) - **集合接口**:`Collection`、...

    java基础知识笔记

    Java基础是编程世界中至关重要的一个领域,尤其对于那些想要...通过深入学习和实践这些"Core Java"知识点,开发者可以构建出高效、稳定的Java应用程序,并为进阶学习Java高级特性、框架以及分布式系统打下坚实的基础。

    传智播客Java SE基础毕向东老师全程笔记

    ### 传智播客Java SE基础毕向东老师全程笔记知识点概览 #### 第一章:编程基础 **Java的特性和优势:** - **简单性:** Java的设计初衷是为了简化编程,减少编码负担。 - **面向对象:** Java是完全面向对象的语言...

    传智播客Java_SE基础毕向东老师全程笔记

    本章节详细介绍了Java的基础知识,强调了Java的特性与优势,包括简单性、面向对象、可移植性(write once,run anywhere)、高性能、分布式、动态性、多线程、安全性、健壮性等。通过解析Java的运行机制,我们了解到...

    Java+JDK+6学习笔记.pdf

    ### Java+JDK+6 学习笔记知识点详解 #### 一、Java 概述 - **起源与发展:** - Java 最初是由 Sun Microsystems 的 Green Project 开发出来的编程语言,最初是为了创建一个名为 Star7 的应用程序。 - 1995 年 5 ...

    Java基础笔记MarkDown版4万字肝吐血

    ### Java基础笔记知识点详解 #### 一、Java概述与历史 - **编程语言的发展历程**: - **第一代:机器语言**:直接通过二进制指令与计算机交互,无须任何转换。 - **第二代:汇编语言**:比机器语言更接近人类的...

    《良葛格的Java学习笔记》

    14. **Java EE**:对于进一步的学习,笔记可能还会提及Java企业版(Java EE),它为构建分布式、基于Web的应用提供了服务器端框架,包括Servlet、JSP、EJB等技术。 15. **实战项目**:学习笔记通常会包含一些简单的...

    JAVA SE应用程序设计 1-19章代码、课件

    Java SE(标准版)是Java平台的核心部分,用于开发和运行桌面应用、服务器应用以及分布式系统。本资源包包含了从第1章到第19章的完整Java SE应用程序设计课程内容,包括课件、源代码和PDF文档。这些资料对于初学者和...

    java基础笔记

    通过这份"Java笔记大全",你可以系统地学习Java的基础知识,加深对Java编程的理解,并为后续深入学习Java高级特性、框架及开发实践打下坚实基础。无论你是初学者还是希望巩固基础的开发者,这份笔记都是一份宝贵的...

    毕向东_Java基础课堂笔记.pdf

    #### 第六章 常用类API - Java提供了丰富的类库支持,如String类、Math类等。 - 这些类提供了各种实用的功能,简化了开发工作。 #### 第七章 集合框架(容器) - 集合框架是Java中用于存储和操作一组对象的标准...

    传智播客毕向东Java基础课堂笔记

    ### 传智播客毕向东Java基础课堂笔记 #### 第一章:编程基础 1. **Java的特性和优势** - **简单性**:Java的设计使得语法清晰、简洁,易于学习和理解。 - **面向对象**:支持封装、继承、多态等面向对象编程特性...

    第二代微服务电商项目实战笔记

    【第二代微服务电商项目实战笔记】主要涵盖了构建一个基于SpringBoot2.x和SpringCloud2.x的微服务架构电商项目的全过程。以下将详细介绍项目中的关键技术和实现方式。 **技术选型** 1. **SpringBoot2.x**: 作为...

    java学习笔记.doc

    - 第6天至第7天:可能会涵盖数组和字符串的使用,这两个在Java编程中非常常见且重要的概念。 - 第8天至第9天:异常处理是Java编程的重要部分,通过try-catch语句块来捕获和处理程序运行时可能出现的问题。 - 第10...

    java学习笔记

    - **J2EE (Java Platform Enterprise Edition)**: 企业版Java开发平台,专为Web应用和服务端应用设计,支持大型分布式系统的开发。 #### 二、Java程序运行机制 Java程序的运行依赖于Java虚拟机(JVM),这意味着只要...

    毕向东Java SE视频教程整理笔记

    #### 第六章:常用类API - **常用类概述**:这部分涵盖了Java标准库中广泛使用的类,如String、Integer等。了解这些类的使用对于高效编程至关重要。 #### 第七章:集合框架(容器)+其他类对象使用 - **集合框架*...

    JAVA学习笔记(林信良 编著 教程)

    - **Java EE**:企业版,基于 Java SE,扩展了用于构建大型分布式系统的高级服务和 API,如 JSP、Servlet、EJB 和 RMI 等。 - **Java ME**:微型版,用于移动设备和嵌入式系统的轻量级平台。 **三、Java平台详解*...

    大数据学习笔记

    - **概述**:Kafka是一种高性能的消息队列系统,用于构建实时数据管道和流应用。 - **应用场景**:广泛应用于日志收集、监控数据聚合、在线/离线消息处理等领域。 - **13.1 KAFKA的基本组成** - **组成部分**:...

Global site tag (gtag.js) - Google Analytics