微服务转型,雪崩效应是绕不过的一道坎

1.星火燎原

1.1农民眼中的微服务

本文讲的是微服务转型,雪崩效应是绕不过的一道坎,近年来,微服务就象一把燎原的大火,窜了出来并在整个技术社区烧了起来,微服务架构被认为是IT软件服务化架构演进的目标。为什么微服务这么火,微服务能给企业带来什么价值?

1.1.1 以种植农作物的思想来理解微服务

我们以耕种为例来看如何充分利用一块田地的:

先在地里种植了一排排玉米;
后来发现玉米脚下空地可以利用,再间隔一段距离再种上豆角,豆角长大后顺着玉米杆往上爬,最后紧紧地缠绕在玉米杆上;
再后来发现每排玉米之间的空隙地还可以再种些土豆,土豆蔓藤以后会交织在一起,肆虐在玉米脚下吞食营养物质;

表面看来一块土地得到了充分利用,实际上各农作物得不到充分的光照和适宜的营养,如此一来加大了后期除草、松土、施肥、灌溉及收割的成本。

下面的耕植思路是不是更好点呢? 一整块地根据需要分配为若干大小土地块,每块地之间清晰分界,这样就有了玉米地、土豆地、豆角地,再想种什么划块地再耕作就可以了。

这样种植好处很多,比如玉米、豆角和土豆需要的营养物质是不一样的,可由专业技术人员施肥;玉米,豆角和土豆分离,避免豆角藤爬上玉米,缠绕玉米不能自由生长。土豆又汲取玉米需要的营养物质等等问题。

软件系统实现与农作物的种植方式其实也很类似,传统的应用在扩展性,可靠性,维护成本上表现都不尽人意。如何充分利用大量系统资源,管理和监控服务生命周期都是头疼的事情,软件系统设计迫切需要上述的“土地分割种植法”。微服务架构应运而生:在微服务系统中,各个业务系统间通过对消息(字符序列)的处理都非常友好的RestAPI进行消息交互。如此一来,各个业务系统根据Restful架构风格统一成一个有机系统。

1.2 微服务架构下的冰山

泰坦尼克号曾经是世界最大的客轮,在当时被称为是”永不沉没“的,但却在北大西洋撞上冰山而沉没。我们往往只看到它浮出水面的绚丽多彩,水下的基础设施如资源规划、服务注册发现、部署升级,灰度发布等都是需要考虑的因素。

1.2.1 优势

复杂应用分解:复杂的业务场景可被分解为多个业务系统,每个业务系统的每个服务都有一个用消息驱动API定义清楚的边界。
契约驱动:每个业务系统可自由选择技术,组建技术团队利用Mock服务提供者和消费者,并行开发,最终实现依赖解耦。
自由扩展:每个系统可根据业务需要独自进行扩展。
独立部署:每个业务系统互相独立,可根据实际需要部署到合适的硬件机器上。
良好隔离:一个业务系统资源泄漏不会导致整个系统宕掉,容错性较好。

1.2.2 面临的挑战

服务管理:敏捷迭代后的微服务可能越来越多,各个业务系统之间的交互也越来越多,如何做高效集群通信方案也是问题。
应用管理: 每个业务系统部署后对应着一个进程,进程可以启停。如果机器掉电或者宕机了,如何做无缝切换都需要强大的部署管理机制。
负载均衡:为应对大流量场景及提供系统可靠性,同一个业务系统也会做分布式部署即一个业务实例部署在多台机器上。如果某个业务系统挂掉了,如何按需做自动伸缩分布式方案方案也需要考虑。
问题定位:单体应用的日志集中在一起,出现问题定位很方便,而分布式环境的问题定界定位,日志分析都较为困难。
雪崩问题:分布式系统都存在这样一个问题,由于网络的不稳定性,决定了任何一个服务的可用性都不是 100% 的。当网络不稳定的时候,作为服务的提供者,自身可能会被拖死,导致服务调用者阻塞,最终可能引发雪崩效应。

Michael T. Nygard 在精彩的《Release It!》一书中总结了很多提高系统可用性的模式,其中非常重要的两条是:使用超时策略和使用熔断器机制。

超时策略:如果一个服务会被系统中的其它部分频繁调用,一个部分的故障可能会导致级联故障。例如,调用服务的操作可以配置为执行超时,如果服务未能在这个时间内响应,将回复一个失败消息。然而,这种策略可能会导致许多并发请求到同一个操作被阻塞,直到超时期限届满。这些阻塞的请求可能会存储关键的系统资源,如内存、线程、数据库连接等。因此,这些资源可能会枯竭,导致需要使用相同的资源系统的故障。在这种情况下,它将是优选的操作立即失败。设置较短的超时可能有助于解决这个问题,但是一个操作请求从发出到收到成功或者失败的消息需要的时间是不确定的。

熔断器模式:熔断器的模式使用断路器来检测故障是否已得到解决,防止请求反复尝试执行一个可能会失败的操作,从而减少等待纠正故障的时间,相对与超时策略更加灵活。

一年一度的双十一已经悄然来临,下面将介绍某购物网站一个Tomcat容器在高并发场景下的雪崩效应来探讨Hystrix的线程池隔离技术和熔断器机制。

2.从雪崩看应用防护

2.1 雪崩问题的本质:Servlet Container在高并发下崩溃

我们先来看一个分布式系统中常见的简化的模型。Web服务器中的Servlet Container,容器启动时后台初始化一个调度线程,负责处理Http请求,然后每个请求过来调度线程从线程池中取出一个工作者线程来处理该请求,从而实现并发控制的目的。

Servlet Container是我们的容器,如Tomcat。一个用户请求有可能依赖其它多个外部服务。考虑到应用容器的线程数目基本都是固定的(比如Tomcat的线程池默认200),当在高并发的情况下,如果某一外部依赖的服务(第三方系统或者自研系统出现故障)超时阻塞,就有可能使得整个主线程池被占满,增加内存消耗,这是长请求拥塞反模式(一种单次请求时延变长而导致系统性能恶化甚至崩溃的恶化模式)。

更进一步,如果线程池被占满,那么整个服务将不可用,就又可能会重复产生上述问题。因此整个系统就像雪崩一样,最终崩塌掉。

2.2 雪崩效应产生的几种场景

流量激增:比如异常流量、用户重试导致系统负载升高;
缓存刷新:假设A为client端,B为Server端,假设A系统请求都流向B系统,请求超出了B系统的承载能力,就会造成B系统崩溃;
程序有Bug:代码循环调用的逻辑问题,资源未释放引起的内存泄漏等问题;
硬件故障:比如宕机,机房断电,光纤被挖断等。
线程同步等待:系统间经常采用同步服务调用模式,核心服务和非核心服务共用一个线程池和消息队列。如果一个核心业务线程调用非核心线程,这个非核心线程交由第三方系统完成,当第三方系统本身出现问题,导致核心线程阻塞,一直处于等待状态,而进程间的调用是有超时限制的,最终这条线程将断掉,也可能引发雪崩;

2.3 雪崩效应的常见解决方案

针对上述雪崩情景,有很多应对方案,但没有一个万能的模式能够应对所有场景。

针对流量激增,采用自动扩缩容以应对突发流量,或在负载均衡器上安装限流模块。
针对缓存刷新,参考Cache应用中的服务过载案例研究
针对硬件故障,多机房容灾,跨机房路由,异地多活等。
针对同步等待,使用Hystrix做故障隔离,熔断器机制等可以解决依赖服务不可用的问题。

通过实践发现,线程同步等待是最常见引发的雪崩效应的场景,本文将重点介绍使用Hystrix技术解决服务的雪崩问题。后续再分享流量激增和缓存刷新等应对方案。

3、Hystrix解决同步的两种方式:
隔离和熔断

Hystrix 是由Netflix发布,旨在应对复杂分布式系统中的延时和故障容错,基于Apache License 2.0协议的开源的程序库,目前托管在GitHub上。

Hystrix采用了命令模式,客户端需要继承抽象类HystrixCommand并实现其特定方法。为什么使用命令模式呢?使用过RPC框架都应该知道一个远程接口所定义的方法可能不止一个,为了更加细粒度的保护单个方法调用,命令模式就非常适合这种场景。

命令模式的本质就是分离方法调用和方法实现,在这里我们通过将接口方法抽象成HystricCommand的子类,从而获得安全防护能力,并使得的控制力度下沉到方法级别。

Hystrix核心设计理念基于命令模式,命令模式UML如下图:

可见,Command是在Receiver和Invoker之间添加的中间层,Command实现了对Receiver的封装。那么Hystrix的应用场景如何与上图对应呢?

API既可以是Invoker又可以是Reciever,通过继承Hystrix核心类HystrixCommand来封装这些API(例如,远程接口调用,数据库的CRUD操作可能会产生延时),就可以为API提供弹性保护了。

3.1 资源隔离模式

Hystrix之所以能够防止雪崩的本质原因,是其运用了资源隔离模式,我们可以用蓄水池做比喻来解释什么是资源隔离。生活中一个大的蓄水池由一个一个小的池子隔离开来,这样如果某一个水池的水被污染,也不会波及到其它蓄水池,如果只有一个蓄水池,水池被污染,整池水都不可用了。软件资源隔离如出一辙,如果采用资源隔离模式,将对远程服务的调用隔离到一个单独的线程池后,若服务提供者不可用,那么受到影响的只会是这个独立的线程池。

(1)线程池隔离模式:使用一个线程池来存储当前的请求,线程池对请求作处理,设置任务返回处理超时时间,堆积的请求堆积入线程池队列。这种方式需要为每个依赖的服务申请线程池,有一定的资源消耗,好处是可以应对突发流量(流量洪峰来临时,处理不完可将数据存储到线程池队里慢慢处理)。这个大家都比较熟悉,参考Java自带的ThreadPoolExecutor线程池及队列实现。线程池隔离参考下图:

线程隔离的优点:
请求线程与依赖代码的执行线程可以完全隔离第三方代码;
当一个依赖线程由失败变成可用时,线程池将清理后并立即恢复可用;
线程池可设置大小以控制并发量,线程池饱和后可以拒绝服务,防止依赖问题扩散。

线程隔离的缺点:
增加了处理器的消耗,每个命令的执行涉及到排队(默认使用SynchronousQueue避免排队)和调度;
增加了使用ThreadLocal等依赖线程状态的代码复杂性,需要手动传递和清理线程状态。

(2)信号量隔离模式:使用一个原子计数器来记录当前有多少个线程在运行,请求来先判断计数器的数值,若超过设置的最大线程个数则丢弃该类型的新请求,若不超过则执行计数操作请求来计数器+1,请求返回计数器-1。这种方式是严格的控制线程且立即返回模式,无法应对突发流量(流量洪峰来临时,处理的线程超过数量,其他的请求会直接返回,不继续去请求依赖的服务),参考Java的信号量的用法。

Hystrix默认采用线程池隔离机制,当然用户也可以配置 HystrixCommandProperties为隔离策略为ExecutionIsolationStrategy.SEMAPHORE。

信号隔离的特点:
信号隔离与线程隔离最大不同在于执行依赖代码的线程依然是请求线程,该线程需要通过信号申请;
如果客户端是可信的且可以快速返回,可以使用信号隔离替换线程隔离,降低开销。

线程池隔离和信号隔离的区别见下图,使用线程池隔离,用户请求了15条线程,10条线程依赖于A线程池,5条线程依赖于B线程池;如果使用信号量隔离,请求到C客户端的信号量若设置了15,那么图中左侧用户请求的10个信号与右边的5个信号量需要与设置阈值进行比较,小于等于阈值则执行,否则直接返回。

建议使用的场景:
根据请求服务级别划分不同等级业务线程池,甚至可以将核心业务部署在独立的服务器上。

3.2 熔断器机制

熔断器与家里面的保险丝有些类似,当电流过大时,保险丝自动熔断以保护我们的电器。假设在没有熔断器机制保护下,我们可能会无数次的重试,势必持续加大服务端压力,造成恶性循环;如果直接关闭重试功能,当服务端又可用的时候,我们如何恢复?

熔断器正好适合这种场景:当请求失败比率(失败/总数)达到一定阈值后,熔断器开启,并休眠一段时间,这段休眠期过后熔断器将处与半开状态(half-open),在此状态下将试探性的放过一部分流量(Hystrix只支持single request),如果这部分流量调用成功后,再次将熔断器闭合,否则熔断器继续保持开启并进入下一轮休眠周期。

建议使用场景
Client端直接调用远程的Server端(server端由于某种原因不可用,从client端发出请求到server端超时响应之间占用了系统资源,如内存,数据库连接等)或共享资源。

不建议的场景
应用程序直接访问如内存中的数据,若使用熔断器模式只会增加系统额外开销。
作为业务逻辑的异常处理替代品。

4、总结

本文从自己曾经开发的项目应用的分布式架构引出服务的雪崩效应,进而引出Hystrix(当然了,Hystrix还有很多优秀的特性,如缓存,批量处理请求,主从分担等,本文主要介绍了资源隔离和熔断)。主要分三部分进行说明:

第一部分:以耕种田地的思想引出软件领域设计的微服务架构, 简单的介绍了其优点,着重介绍面临的挑战:雪崩问题。
第二部分:以Tomcat Container在高并发下崩溃为例揭示了雪崩产生的过程,进而总结了几种诱发雪崩的场景及各种场景的应对解决方案,针对同步等待引出了Hystrix框架。
第三部分:介绍了Hystrix背景,资源隔离(总结了线程池和信号量特点)和熔断机制工作过程,并总结各自使用场景。

如Martin Fowler 在其文中所说,尽管微服务架构未来需要经历时间的检验,但我们已经走在了微服务架构转型的道路上,对此我们可以保持谨慎的乐观,这条路依然值得去探索。

时间: 2024-08-06 21:27:06

微服务转型,雪崩效应是绕不过的一道坎的相关文章

学霸君基于Docker的微服务架构设计

以下内容根据演讲PPT以及现场分享整理而成. 今天主要分享的是我们在实践微服务架构或者容器架构过程中踩过的坑,对于致力在容器技术方面进行探索的同学会有很大帮助.本次将站在整体的角度,分享如何去运维整个线上系统,如何看待整个微服务的架构.微服务能带来什么帮助以及微服务又有哪些缺点,还有重要的一点就是微服务架构如何去落地实施.虽然阿里云这样的服务商为我们做了大量的工作,但是将微服务架构真正地落地实施还需要做很多的工作.而对于任何技术而言,都是存在优缺点的,微服务架构也不是救世的良药. 一.学霸君的发

微服务应用容器化场景中常见问题总结

简介 云原生技术栈是下一代应用转型的必然选择,它包含了微服务架构,DevOps和容器技术.对于微服务架构来说,应用是"第一公民",他逐渐蚕食原来底层软件或者硬件的功能,例如服务注册与发现以及负载均衡:而对于容器平台来说,容器是"第一公民",他提供了容器注册与发现和负载均衡,同时容器技术将应用和外面的世界做了隔离,这样很多应用运行的假设就会失效.那当微服务应用运行在容器中的时候,我们会遇到哪些常见问题?我们又该如何解决呢? 企业应用在向微服务架构转型的过程中,微服务如

互联网转型需要微服务架构

微服务出现的时间不短了,但是为什么现在才这么重视它?互联网转型要转型什么? 第一,以职能为中心转向以用户为中心.我们过去的信息化更多的是依照部门职能,有什么样的工作内容,有什么样的流程,然后去做系统.下一步的信息化更多的是以用户为中心.为什么是以用户为中心?我们要看用户到底需要什么,在什么样的场景下需要什么样的信息支持.过去我们只在内部做很多系统,其实用户体验也非常的不好,用户需要的东西也没有. 第二,从流程驱动转向数据驱动.过去都是看业务流程是什么样的,流程中间需要什么样的数据来支持.随着移动

华为架构师8年经验谈:从单体架构到微服务的服务化演进之路

本次分享的大纲如下: 传统应用开发面临的挑战 服务化实践 服务化不是银弹 服务化架构的演进方向   一 .传统应用开发面临的挑战 挑战1-- 研发成本高   主要体现在如下几个方面:   代码重复率高   在实际项目分工时,开发都是各自负责几个功能,即便开发之间存在功能重叠,往往也会选择自己实现,而不是类库共享,主要原因如下:   从技术架构角度看,传统垂直架构的特点是本地API接口调用,不存在业务的拆分和互相调用,使用到什么功能就本地开发,非常方便,不需要过度依赖于其它功能模块: 从考核角度来

《Spring Cloud与Docker微服务架构实战》配套代码

不才写了本使用Spring Cloud玩转微服务架构的书,书名是<Spring Cloud与Docker微服务架构实战> - 周立,已于2017-01-12交稿.不少朋友想先看看源码,现将代码放出. 本次放出的代码: 共计70+个DEMO 覆盖Eureka.Ribbon.Feign.Hystrix.Zuul.Spring Cloud Config.Spring Cloud Bus.Spring Cloud Sleuth.Docker.Docker Compose等. 1-11章代码地址: ht

老司机带你玩PPmoney微服务【加强版】

前言 大家晚上好,今天给大家分享的内容是 PPmoney 微服务之路. 首先简单介绍一下,我是来自 ppmoney 的资深架构师 敖小剑,目前负责 ppmoney 的基础架构和服务化推进. 今天分享的内容主要有四个部分: 首先,介绍了一下为什么要选择微服务架构 其次,讲一下我们微服务框架的技术选型 第三,介绍微服务生态中的支撑体系 第四,旧有系统的迁移改造 第一部分 为什么要选择微服务架构 我们先开始第一部分的内容:为什么要选择微服务架构? 先简单介绍一下我们公司--PPmoney(万惠). 4

微服务熔断与隔离

微服务近年来很火很热,相关的文章汗牛充栋,关于架构设计本文就不作叙述了,只谈谈在分布式服务的容错方面怎么做. 1 什么是微服务 对于微服务,我们可以简单的理解成对一个服务解耦,以降低业务系统的复杂性,将服务系统中的功能进行拆分成多个轻量的子服务,各个自服务间通过RPC实现服务间的关联,这样做的好处是将业务简单化,每个子服务可以有自己独立的编程语言,模式等且能够独立维护,独立部署,功能复用. 2 为什么需要做服务隔离与熔断 由于微服务间通过RPC来进行数据交换,所以我们可以做一个假设:在IO型服务

微服务架构的核心要点和实现原理

微服务架构中职能团队的划分 传统单体架构将系统分成具有不同职责的层次,对应的项目管理也倾向于将大的团队分成不同的职能团队,主要包括:用户交互UI团队.后台业务逻辑处理团队与数据存取ORM团队.DBA团队等.每个团队只对自己分层的职责负责,并对使用方提供组件服务质量保证.如果其中一个模块化组件需要升级.更新,那么这个变更会涉及不同的分层团队,即使升级和变更的改变很小,也需要进行跨团队沟通:需求阶段需要跨团队沟通产品功能,设计阶段需要跨团队沟通设计方案,开发阶段需要跨团队沟通具体的接口定义,测试阶段

微服务基础

微服务基础篇 1: service consumer -> Proxy Server ->Load Balance -> Service Discovery -> Target Service 2: Broser curl Other -> Zuul -> Ribbon -> Euraka -> Restful API zuul: 是边缘服务,用来提供动态路由,监控,授权,安全,调度等功能,将权限控制等一些业务逻辑抽离出来,单独放到Zuul里,使得服务组件更