APP下载

使用服务隔离方法提高系统可用性

2015-05-30营春晓

计算机时代 2015年4期

营春晓

摘 要: 在大型网站和软件系统中,由于业务的复杂性,常采用服务组件化的架构策略来达到解耦和可扩展的目的。在服务按业务功能切分后,如何提高整个系统的可用性是一个非常重要的问题。文章在对大型网站服务组件化设计架构研究的基础上,结合服务组件之间强弱依赖关系的特点与典型场景,总结了服务隔离技术的基本思想和维度,提供了服务调用解耦与隔离方法、容器级别服务故障隔离方法、组件服务间接依赖故障隔离方法等来提高系统的可用性。

关键词: 系统高可用性; 强弱依赖; 故障隔离; 服务组件化

中图分类号:TP391.12 文献标志码:A 文章编号:1006-8228(2015)04-21-02

Abstract: Because of the complexity of business, the large web sites and software system use service component architecture to get ultra-scalable and highly decoupling purpose. According to the business service functional segmentation, how to improve availability of the whole system is a very important problem. Based on the research of large web site service component architecture, combined with the characteristics of strong-weak dependence and typical scene, this paper provides the service call decoupling method, the container services fault isolation method and the component services fault isolation method to improve the system availability.

Key words: high availability; strong-weak dependency; fault isolation; service component

0 引言

大型网站及软件系统,其高可用性直接影响客户体验,这是大型网站都需要面对的基础性课题。高可用性涉及到IT基础设施、软硬件架构、开发测试、运维等各个方面。目前,大型网站通常是领域业务多元化,面临高并发、高流量的挑战。为了获得更好的性能和可扩展性,按照服务组件化设计思想,以领域业务为功能单元做垂直切分,各模块之间提供服务接口关联起来,这样可以提高整个系统的可用性。然后随着应用规模的扩大,服务之间的依赖关系更为复杂,如何在系统出现故障或异常时,避免由“点故障”到“面故障”的扩散,避免不同领域业务相互影响,避免非核心影响核心,是开发者在做应用架构设计与物理部署架构设计时必须要考虑的问题。

本文结合日常项目中的实践经验,提出在服务组件化的过程中,如何做服务级故障隔离的原则和方法,提升网站可用性这一需求。

1 服务级隔离基本思想

形式上,系统与系统之间,服务与服务之间(无论是两个服务是否为同一业务组件)存在以下两种依赖关系。

⑴ 强依赖。所谓A系统强依赖于B系统是指,A系统必须依赖B系统的处理结果,才能正常的完成逻辑;简单的来说,如果B不能提供服务,A也无法正常工作。从高可用性设计的角度出发,在这种依赖关系下,A与B系统需要达到如下几点目标:对于B系统,A直接RPC调用,B在承诺的SLA基础上,做好自我保护;B系统宕机时,A尽管不能使用,但要保证机器不挂掉;B系统故障恢复时,A可以自动快速恢复;B故障时,A可以自动检测,自动降低或关闭对B的访问,防止情况恶化。

⑵ 弱依赖。所谓A系统弱依赖于B系统,是指B系统如果发生了故障,A系统可以继续提供正常的服务。弱依赖通常有这些特点:可以不等待B结果的返回(比如发送消息、ajax区域加载);B是非核心功能,结果不返回不影响A的关键流程(合理超时时间的控制),A、B的调用可以是异步(队列、线程的FutureTask、协程akka);对B的服务调用可通过功能开关实现降级。

针对强依赖与弱依赖的不同特点,在架构和设计时,为避免故障或异常时由“点故障”到“面故障”的扩散,我们考虑在区分核心与非核心(服务、组件、产品重要度分级)、按功能组与后台依赖隔离、同一容器内服务间隔离、按客户群体DNS层隔离、引入异步模式隔离服务调用者与提供者等层面和场景下提供服务故障隔离策略和方法。

2 具体隔离策略与方法的设计

本节根据服务之间的依赖关系以及物理部署结构等特点,总结服务间如何做隔离和解耦的策略和方法。

2.1 服务间依赖隔离与解耦

在服务A与B存在强依赖的情况下(如图1所示),描述RPC与基于Queue两种方式的区别。

正常情况:A系统对外承诺500的TPS,系统的平均响应时间为100ms,这时候只需要50个线程并行即可支撑。

故障情况:B系统变慢,导致A的平均响应时间变为1000ms,现在A系统的线程数是500。

B系统的异常,在直接RPC的调用情况会导致A系统宕机,同时B系统由于A系统的并发访问数由50变为500,B系统进一步恶化。由此可见,在RPC的调用方式下,无论是同步调用还是异步调用,对于服务器端都是直接压力传导,在A与B是强依赖的情况下,如果是同步RPC调用,超时控制在异常时刻是决定性问题。在强依赖的情况下,除了需要在采用RPC调用的时候合理的设置超时外,在架构时可用采用基于消息队列的方式,来达到服务间由于容量不匹配导致的强耦合(如图2所示)。图2中,如果调用者不需要服务方返回结果,则椭圆框的部分是不需要的。

与基于RPC的方式相比,队列模式有如下特点。

⑴ 为服务调用者与服务提供者解耦,队列模式尤其适合弱依赖情况下的异步单相消息模式。

⑵ 引入队列中间层可以对任务做优先级、丢弃策略、持久化等,同时由于中间节点的存在,也引入了复杂性,从响应时间来看,与RPC方式相比会有所增加。

⑶ 在应对突发尖峰流量时,服务端可以实现压力逐步释放的目标,保持稳定吞吐率,达到“稳定消化能够处理的量,快速丢掉不能消化的量”,客观上达到了服务组件间解耦的目的。

2.2 同一容器内服务间线程隔离

在系统服务化的切分过程中,通常是以业务领域的切分为依据,属于同一业务领域的服务在部署时基本部署在同一个容器中。由于各个服务对于资源的消耗不同,响应时间与关联组件也不相同,导致在容器线程总数固定情况下,其中一个服务突然变慢会占用大量线程,导致线程耗尽。同时线程数飙升,引起Context Switch[1]加剧,在JAVA平台下,还会引起对象生命周期变长,Full GC频繁,CPU Load Average和Usage高企[2],最终容器整体宕机。

为解决容器内的服务线程隔离问题,在实践中首先需要根据历史访问数据及系统容量规划的数据,计算出每个服务的峰值与均值并发数、响应时间、交易吞吐率等,具体的数据采集与分析过程在本文中不作详述。对于服务隔离策略的设计可以采用定额与弹性资源配置两种方式。

⑴ 悲观策略:对于每个服务设定固定的最大资源量,任何一种服务当访问量达到最大时,即使总资源存在富余也不能使用。

⑵ 乐观策略:在保证每种服务预留最低资源的情况下,允许任务依据一个弹性配额去争抢线程资源,达到线程利用率的最大化。

2.3 核心与非核心服务隔离

组件A与组件B都强依赖于组件C,同时组件C强依赖于组件D。其中组件A属于非核心业务组件,B属于核心业务组件。由于C组件总体处理能力是固定有限的(假定平均响应时间100ms,最大TPS为3000/s),当A组件由于突发流量的影响,对C组件访问量变大,或者A组件的某些请求会耗费C组件较多的资源时,导致C组件不能处理B组件的请求,级联导致B系统出现故障的情况发生。在这个场景下,虽然A与B在物理部署上已经做了隔离,但是复杂的关联组件依赖关系,间接导致因为非核心业务组件影响核心业务组件的事例。为避免上述问题的发生,可以考虑以下两种隔离策略。

⑴ 如果C组件及其关联组件水平伸缩后,可以支持更高的性能,推荐采用路由隔离方式进行,即A组件与B组件分别访问不同C组件服务群组。

⑵ C系统不具备进一步水平扩展的能力(比如瓶颈点不在C,而在于C所依赖的系统D);这个时候可以在C系统上设置流量控制和功能开关标志位,在异常情况下可以限制或关闭非核心系统对C的访问。

2.4 服务功能开关与降级的设计

在各种服务隔离策略中,当异常流量发生时,在了解全局服务依赖关系和服务重要度排列的基础上,架构设计中通常使用功能开关和服务降级的策略来分解流量,达到“丢卒保车”的目的来应对突发状况。在具体的实践中主要考虑三个方面。

⑴ 管理全局性服务组件的依赖关系,识别各服务调用链中的关键路径。在大型网站中,通常存在上千级别的组件,服务之间的依赖关系异常复杂,大部分网站都实现了基于Google的Dapper[3]系统的分布式系统监控与依赖分析系统。

⑵ 功能开关的设计。在设计实现时,存在单机与集群的区别。单机实现时,对于JAVA平台建议使用JMX标准实现,这样做的好处是可以纳入统一的监控体系,使用JConsole等通用JMX Client可以处理;集群实现时,由于需要对大量节点统一管控,建议使用Zookeeper之类的配置管理系统来实现。

⑶ 自动降级与手动降级的设计与运用。自动功能开关的设计主要是首先确定判断异常情况的阈值,比如单位时间内服务调用超时或失败比例超过70%,或者连续失败或超时达到20。此时系统可以把访问窗口按指数降低,直到降为1;服务恢复类似于TCP的慢启动[4],窗口逐渐打开。对于封闭点,应该尽可能提前;对于一些关键系统,如支付类系统,最好避免使用自动开关,在接到监控系统报警后,人工介入决定是否需要降级和关闭。

3 结束语

目前新华网要求整体可用性达到99.99%级别,这意味着全年计划停机时间加上非计划停机时间不到53分钟。系统的高可用性是一个系统性工程,除了IT基础设施外,在软件层面要具备快速发现定位的监控系统,要拥有完善的容量规划与评测方法,要有应对峰值流量的策略和手段等,这些会涉及到软件过程与基础软件建设的各个方面。本文提出了在实践中总结出来的方法与策略可以为有相似需求的系统提供参考与借鉴。

参考文献:

[1] DANIELP.BOVET&MARCO CESATI.深入理解LINUX内核[M].中国电力出版社,2007.

[2] 林昊.分布式Java应用基础与实践[M].电子工业出版社,2011.

[3] SIGELM, N B, BARROSO L, BURROWS M, et al. Dapper, ALarge-Scale Distributed Systems TracingInfrastructure.[R].Google,2010.

[4] W.Richard Stevens.TCP/IP Illustrated Volume 1:The Protocols[M]. Boston: Addison-Wesley,December 15,1993(1):233-240