本文首先介绍微服务架构存在的风险,而后针对如何避免微服务架构的故障,提出了多种有效的微服务架构中的方法和技术,其中例如服务降级、变动管理、健康检查和修复、断路器、限流器等。面试
目录算法
一、微服务架构的风险数据库
二、优雅的服务降级浏览器
三、变动管理缓存
四、健康检查和负载均衡性能优化
五、自我修复服务器
六、故障转移缓存(Failover Caching)网络
七、重试逻辑(Retry Logic)架构
八、限流器和负载开关(Rate Limiters and Load Shedders)并发
九、快速且单独失效(Fail Fast and Independently)
十、舱壁模式(Bulkheads)
十一、断路器(Circuit Breakers)
十二、故障测试(Testing for Failures)
1三、总结
1四、要点
微服务架构经过定义明确的服务边界,能有效地隔离故障。 和其余分布式系统同样,微服务在网络、硬件和应用层上都会存在更多的问题。因为服务之间是互相依赖,所以任何组件均可能出错致使用户不能访问。为尽量减小部分中断带来的影响,咱们须要构建容错能力强的服务,以从容应对发生的某些中断。
本文介绍了构建和运维高可用的微服务架构系统中最经常使用的技术和架构模式。若是读者不熟悉上述的模式,那并没什么大碍。构建可靠的系统不是一踞而就的。
一、微服务架构的风险
微服务架构将应用逻辑拆分红服务,服务之间经过网络交互。因为是经过网络调用,而不是在进程中调用,所以这给须要在多个物理和逻辑组件间进行协做的系统带来了潜在的问题和复杂性。分布式系统变得愈来愈复杂,也致使网络特定故障发生的可能性增大。
相比传统应用庞大的结构,微服务架构最大的一个优势是团队能独立地设计、开发和部署各自的服务。团队能掌控各自服务的整个生命周期。这也意味者团队没法控制服务的依赖关系,由于这些依赖的服务多是由其余团队管理。在微服务架构体系下,咱们要牢记提供的服务因为是其余人控制,所以可能会因为发布、配置、和其余变动等缘由,从而致使服务暂时不可用,并且组件之间互相独立。
二、优雅的服务降级
微服务架构最大的优势之一就是当组件出现故障时,能隔离这些故障而且能作到优雅地服务降级。好比,在图片分享应用中,当出现故障时,用户可能没法上传图片,但他们依然能浏览、编辑和分享已上传的图片。
微服务故障独立(理论上)
在大多数状况下,是很难实现上图这种优雅地服务降级的,由于在分布式环境下,应用都是互相依赖的,开发者须要实现若干错误处理的逻辑(该部分在本文稍后部分讨论)去应对短暂的故障和中断。
服务互相依赖,若是无端障转移的逻辑,则会同时失效
三、变动管理
Google的网站可靠性团队发现大概70%的故障都是因为变动而引发的。当对服务进行修改时……例如发布代码的新版本或者改变一些配置,则总会有可能引发故障或者引入新的错误。
在微服务架构中,服务是互相依赖的。这就是为何你须要减小故障而且尽量下降它们的负面影响。为了应对变动带来的问题,你能够实施变动策略管理而且实现其自动回滚。
好比,当部署新的代码或者修改配置时,应该分步将这些变动部署到服务实例群中的部分实例中,而且进行监控,若是发现关键指标出现问题则能自动进行回滚。
变动管理-回滚部署
另外一个解决方案是运行两套生产环境。部署的时候只部署变动的应用到其中一套环境中,而且在验证了新发布的版本符合预期后,才将负责均衡的流量指向新的应用,这种方法称为“蓝-绿发布”或者“红-黑发布”。
回退代码并非坏事情。你不该该在生产环境中部署有问题的代码,而且应该琢磨哪里出错了。当必要时候应该果断回退代码,这越早越好。
四、健康检查和负载均衡
由于故障或部署、自动扩展等缘由,服务实例会不停启动,从新启动及中止。这使得服务暂时或一直停用。为了不发生这些问题,在负载均衡中应该在路由中设置忽略这些实例,由于它们没法为子系统或用户提供服务。
咱们能够经过外部观察去判断应用实例是否健康。你能够屡次调用Get/health的端点(endpoint)或者经过自身服务的报告得到相关信息。如今的服务发现解决方案会持续从实例中收集健康信息,而且设置负载均衡的路由,让其只指向健康的实例组件。
五、自我修复
自我修复能帮助恢复应用。咱们讨论下当应用遇到崩溃状态后,如何经过相关的步骤去自我修复。在大多数状况下,是经过外部系统监控实例的状态,当服务出现故障一段时间后则会重启服务。在大多数状况下,自我修复的功能是至关有用的,然而,在某些状况下因为不断地重启服务会带来相关的问题。例如当服务过载或者数据库链接超时,则会致使应用不能反馈正确的服务健康状态。
对于一些场景-好比数据库连接丢失,这个时候实现高级的自我修复功能是颇为棘手的。在这种状况下,须要为应用添加额外的逻辑去处理这些特例,而且让外部系统知道服务的实例不须要当即从新启动。
六、故障转移缓存(Failover Caching)
由于网络问题和系统中的变动,服务一般会出现故障。然而,这些故障中断大可能是暂时的,这要归功于自我修复和高级负载平衡的功能,咱们应该找到一个解决方案,能使服务即便在出现故障的时候也能工做。这就是故障转移缓存(Failover Caching),它能帮助为咱们的应用提供必需的数据。
失效转移缓存一般使用两个不一样的过时日期:其中更短的日期指示在正常状况下能使用缓存的时间,而更长的一个日期则指示在故障失效的时候,能使用缓存中的数据时长。
故障转移缓存
特别须要提醒的是,只有当提供过期的数据比没有数据更好的状况下,才能使用故障转移缓存。
要设置缓存和故障转移缓存,能够在HTTP中使用标准响应头。
例如,使用max-age头能够指定某个资源为新资源的最大时间(译者注:意即设定max-age后,浏览器再也不发送请求到服务器)。可使用stale-if-error 头去肯定在出现故障的状况下,从缓存获取资源的时间长短。
如今的CDN和负载均衡器提供了各类缓存和故障转移的解决方案,可是你也能够在你的公司中创建一个共享库,其中包括这些标准的可靠性解决方案。
七、重试逻辑(Retry Logic)
在某些状况下,咱们可能没法缓存数据,或者想对数据进行变动,可是操做最终失败了。在这种状况下,咱们就能够选择重试操做,由于咱们能够预期资源将在一段时间后恢复,或者负载均衡会将请求发送到健康的实例上。
你应该当心地为应用程序和客户端添加剧试逻辑,由于更大量的重试操做可能会使事情变得更糟,甚至阻止应用程序恢复。
在分布式系统中,微服务系统重试可能会触发多个其余请求或重试操做,并致使级联效应。为减小重试带来的影响,你应该减小重试的数量,并使用指数退避算法(exponential backoff algorithm)来持续增长重试之间的延迟时间,直到达到最大限制。
因为重试是由客户端(浏览器,其余微服务等)发起的,而且客户端在处理请求先后是不知道草走失败的,你应该为你的应用程序提供幂等处理能力。例如,当你重试购买操做时,不该该向客户收两次钱。给每一个事务使用惟一的幂等键(idempotency-key)是解决重试问题的方法。
八、限流器和负载开关(Rate Limiters and Load Shedders)
限流是指在一段时间内,定义某个客户或应用能够接收或处理多少个请求的技术。例如,经过限流,你能够过滤掉产生流量峰值的客户和微服务,或者能够确保你的应用程序在自动扩展(Auto Scaling)失效前都不会出现过载的状况。
你还能够阻止较低优先级的流量,以便为关键事务提供足够的资源。
限流器能够阻止流量峰值
另外有一种限流器,称为 “并发请求限流器(concurrent request limiter)”。当你有一些比较昂贵和重要的端点(endpoint),但愿它不该该被调用超过指定的次数,但仍然想要提供流量服务时,这个限流器就十分有用了。
使用负载开关能够确保对于关键的事务总能提供足够的资源保障。它为高优先级的请求保留一些资源,而且不容许低优先级的事务去占用这些资源。负载开关会根据系统的总体状态作出决定,而不是基于单个用户的请求桶(request bucket)大小。负载设备有助于你的系统恢复,由于它们在持续发生故障事件时,依然能保持核心功能正常工做。
九、快速且单独失效(Fail Fast and Independently)
在微服务体系架构中,咱们但愿服务能够快速、单独地失效。为了在服务层面隔离故障,咱们可使用隔板模式(bulkhead pattern)。能够在本文稍后看到相关介绍。
咱们也但愿咱们的组件可以快速失效(fail fast),由于咱们不但愿等到断开的实例直到超时。没有什么比挂起的请求和无响应的界面更使人失望。这不只浪费资源,并且还会让用户体验变得更差。咱们的服务是互相调用的,因此在这些延迟叠加前,应该特别注意防止那些超时的操做。
你想到的第一个办法,多是对每一个服务的调用都定义超时的级别。这种作法的问题是,你不能真正知道到底什么是恰当的超时值,由于当网络故障和其余问题发生时,某些状况下只会影响一两次操做。在这种状况下,若是只有其中一些发生超时,你可能不想拒绝全部这些请求。
咱们能够说,经过使用超时(timeout)来实现微服务中的快速失败是一种反模式,这是应该避免的。可使用基于操做的成功/失败统计次数的熔断模式,而不是使用超时。
十、舱壁模式(Bulkheads)
在工业领域中,常使用舱壁将划分为几个部分,以便在有某部分船体发生破裂时,其余部分依然能密封安然无恙。
舱壁的概念也能够在软件开发中用于隔离资源。
经过使用舱壁模式,咱们能够保护有限的资源不被用尽。例如,若是咱们有两种类型的操做的话,它们都是和同一个数据库实例进行通讯,而且数据据库限制链接数,这时咱们可使用两个链接池而不是使用一个共享的链接池。因为这种客户端和资源分离,超时或过分使用池的操做不会令全部其余操做失效。
泰坦尼克号沉没的主要缘由之一是其舱壁设计失败,水能够经过上面的甲板倒在舱壁的顶部,最后整个船淹没。
泰坦尼克号故障的舱壁
十一、断路器(Circuit Breakers)
为了限制操做的持续时间,咱们可使用超时。超时能够防止挂起操做并保证系统能够响应。然而,在微服务架构通讯中使用静态、微调的超时是一种反模式,由于咱们处于高度动态的环境中,几乎不可能肯定在每种状况下都能正常工做的准确的时间限制。
咱们可使用断路器来处理错误,而不是使用小型和特定基于事务的静态超时机制。断路器以现实世界的电子元件命名,由于它们的行为是都是相同的。你能够保护资源,并经过使用断路器协助它们进行恢。断路器在分布式系统中很是有用,由于重复的故障可能会致使雪球效应,并使整个系统崩溃。
当在短期内屡次发生指定类型的错误,断路器会开启。开启的断路器能够拒绝接下来更多的请求 – 就像防止真实的电子流动同样。断路器一般在必定时间后关闭,以便为底层服务提供足够的空间来恢复。
请记住,并非全部的错误都应该触发断路器。例如,你可能但愿忽略客户端问题,好比4xx响应代码的请求,但要包括5xx服务器端故障。一些断路器还能够有半开关状态。在这种状态下,服务发送第一个请求以检查系统的可用性,同时让其余请求失败。若是这个第一个请求成功,则将断路器恢复到关闭状态并继续接受流量。不然,保持打开状态。
断路器
十二、故障测试(Testing for Failures)
你应该持续地测试系统的常见问题,以确保你的服务可各种故障环境下运行。你应常常测试故障,以让你的团队对可能发生的事故有所准备。
关于测试,你可使用外部服务来识别服务实例组,并随机终止运行组中的一个实例。经过使用这个方法,能够针对单个实例故障进行测试,你甚至能够关闭整个服务组来模拟云提供商层面的故障中断。
1三、总结
实施和运维可靠的服务并不容易。这须要你付出不少努力,还要花费公司更多的成本。
说到这里顺便给你们推荐一个Java架构方面的交流学习社群:650385180,里面不只能够交流讨论,还有面试经验分享以及免费的资料下载,包括Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。相信对于已经工做和遇到技术瓶颈的码友,在这个群里会有你须要的内容。
可靠性有不少层次和方面,所以针对你的团队找出合适的解决方案是至关重要的。你应该将可靠性成为业务决策流程中的一个因素,并为此分配足够的预算和时间。
1四、要点
①、动态环境和分布式系统-如微服务将致使更高的故障机会。
②、服务应单独失效,实现优雅的服务降级以提高用户体验。
③、70%的问题是由变动引发的,恢复可用代码并不老是坏事。
④、快速,单独地失败。团队没法控制其服务依赖关系。
⑤、架构模式和技术,如缓存、隔离技术、断路器和限流器有助于构建可靠的微服务