1 微服务带来的挑战
在第2篇《微服务2:微服务全景架构 》中,我们曾经分析过微服务化后所面临的挑战,有过如下的结论:

1.1 分布式固有复杂性
微服务架构是基于分布式的系统,而构建分布式系统必然会带来额外的开销。
性能: 分布式系统是跨进程、跨网络的调用,受网络延迟和带宽的影响。
可靠性: 由于高度依赖于网络状况,任何一次的远程调用都有可能失败,随着服务的增多还会出现更多的潜在故障点。因此,如何提高系统的可靠性、降低因网络引起的故障率,是系统构建的一大挑战。
分布式通信: 分布式通信大大增加了功能实现的复杂度,并且伴随着定位难、调试难等问题。
数据一致性: 需要保证分布式系统的数据强一致性,即在 C(一致性)A(可用性)P(分区容错性) 三者之间做出权衡。这块可以参考我的这篇《分布式事务》。

1.2 服务的依赖管理和测试
在单体应用中,通常使用集成测试来验证依赖是否正常。而在微服务架构中,服务数量众多,每个服务都是独立的业务单元,服务主要通过接口进行交互,如何保证它的正常,是测试面临的主要挑战。所以单元测试和单个服务链路的可用性非常重要。

1.3 有效的配置版本管理
在单体系统中,配置可以写在yaml文件,分布式系统中需要统一进行配置管理,同一个服务在不同的场景下对配置的值要求还可能不一样,所以需要引入配置的版本管理、环境管理。

1.4 自动化的部署流程
在微服务架构中,每个服务都独立部署,交付周期短且频率高,人工部署已经无法适应业务的快速变化。有效地构建自动化部署体系,配合服务网格、容器技术,是微服务面临的另一个挑战。

1.5 对于DevOps更高的要求
在微服务架构的实施过程中,开发人员和运维人员的角色发生了变化,开发者也将承担起整个服务的生命周期的责任,包括部署、链路追踪、监控;因此,按需调整组织架构、构建全功能的团队,也是一个不小的挑战。

1.6 更高运维成本
运维主要包括配置、部署、监控与告警和日志收集四大方面。微服务架构中,每个服务都需要独立地配置、部署、监控和收集日志,成本呈指数级增长。服务化粒度越细,运维成本越高。

2 迫切的治理需求
正是因为有这些弊端,所以对微服务来说,有了更迫切的服务治理需求,以弥补弊端产生的问题。
可以看看下面的这张图,这是一个典型的微服架构,他包含4层的Load Balance,7层的GateWay,计算服务,存储服务,及其他的一些中间件系统。
实际上,但凡有需要微服务化的系统,都是具备一定规模了。一般会有很多模块构成,相应的部署节点也会非常多,这样故障的概率就会大幅增加,比如磁盘故障、网络故障,机器宕机,触发一些内核bug或者是运行环境漂移等。
本质上也是微服务细粒度拆分后提升了出问题的概率,正如上面说的,分布式系统有它固有的复杂性,相比于单体服务错误会显著的增多,需要高可用方案来保证复杂通信链路的健壮性。

3 如何进行服务可用性治理
我们有很多种方法对服务进行治理来保障服务的高可用。但总的来说有4类:

流量调控:方法主要是金丝雀发布(灰度发布)、ABTesting、流量染色。
请求高可用:方法主要有超时重试、快速重试以及负载均衡。
服务的自我保护:主要包括限流、熔断和降级。
应对故障实例:主要分为异常点驱逐和主动健康检查。
3.1 流量调控
3.1.1 金丝雀发布、ABTesting

流量调度中典型的金丝雀场景,你可以先放行一部分流量到一个新的服务实例中,这个新的服务实例只有你的研发和测试团队可以接入。可以在上面试用或者测试,直到你确认你的服务是健康的,没有bug的,再把流量逐渐的迁移过去。
这个的好处是减少发布新功能存在的风险,而且全程是无停服发布,对用户是透明无感知的,大大提高了可用性。

3.1.2 流量染色

流量染色也是一种典型的场景。如果你想让不同的用户群体(比如这边的Group A、Group B、Group C)使用的功能也是不同的,那流量染色是一个不可缺少的功能。
它可以把符合某些特征的用户流量调控到对应的服务版本中。比如GroupA是学生群体,对应到V1版本,GroupB是老人群体,对应到V2版本。需要注意的是,如果是一条完整的链路,那链路上的各个服务包括数据存储层都应该有不同的版本,这样才能一一对应。

3.2 请求高可用
3.2.1 超时

假设你有两个服务,服务A和服务B,服务A向服务B请求数据。但是B服务由于非常繁忙,在给定的时间周期内(红色时间线)都无法响应。而这个红色时间线是A服务固定的超时时间,如果这个时间之后还没有等到B服务的响应,A服务就不等了,去执行其他的任务。
这个其实就是一个超时的基本概念。它的意义在于可以避免一些长时间的无意义的等待,因为这个时候下游可能是处于故障或者有请求堆积,短时间内可能是无法返回正常的结果的。
因此,服务A在超时之后,可以及时释放自己的一些资源,比如线程或者是请求相关的其他资源。
在实践中,超时时间的设置通常要比正常的请求时间稍微大一些(正常的返回时间可以根据平响进行分析),这样可以避免请求还没有来得及返回就触发超时。当然这个超时时间也不能设置太大。如果太大的话,在服务B出现异常的时候,服务A不能够及时的释放资源,会导致请求堆积,降低自身服务的吞吐能力。

3.2.2 重试

跟上面一样,A服务向B服务获取数据,由于B服务非常繁忙,在给定的超时时间内无法获得响应数据。于是A设置了重试机制,在超时时间结束之后,重新获取一下B服务的数据,这时候B服务已经不忙碌了,很快就把正常数据响应给A服务。
可以看到,重试的意义在于可以提升一次请求的成功率。通常重试不仅可以配合超时,也可以配合一些其他种类的失败。
比如B服务5xx错误了,但可能是有概率的错误,所以重试一次就可能获取到想要的结果。当然重试也有一些注意事项,避免重试带来其他灾难。

重试尽量避开之前已经选择过的失败实例,因为这个时候再重试,大概率还是错误的,意义不是特别大。
其次重试的次数也不能太多,否则很容易对服务B造成数倍的压力,导致服务B发生一些雪崩。
对于多次重试,我们通常可以配合一些类似于退避重试的策略来减缓对服务B的压力。退避策略说明,算法
3.2.3 快速重试(backup request)

同上面一样,服务A向服务B发起一个正常的请求,服务B工作繁忙,A服务在给定的超时时间之前都无法获得响应。按照之前的做法就是等超时时间达到的时候,再发起一次请求。
但是可以在超时时间到达之前做一次更智能的处理,比如超时时间线的中间点,再请求一次服务B。可以看到,重试其实就是一次backup request。正是由于它在正常的超时之前就触发了,所以我们叫它快速重试。
这次快速重试,刚好服务B可能已经缓过来。他就收到了这个请求,给服务A返回了这个结果。
服务A在正常的超时触发之后,就是红色这条线,他也会发起一次正常的标准重试,这个时候服务B也有可能会再给他返回一次信息,快速重试的返回和正常返回,他们的时序是有可能不一定的。通常我们的处理方法是服务A先收到哪一个回复,就以哪个为准。后面收到了就会被抛弃掉。
这边需要注意的是,多一次重试会有一定的额外资源开销,所以在使用的时候,需要注意快速重试和正常重试合理设置,避免总的重试次数过多导致服务可用性反受影响。

3.2.4 负载均衡

假设服务B有多个实例,对于服务A的请求,我们希望它是比较均匀的流向B服务的各个实例,这样才能真正做到负载均衡,提供更稳定的服务。比较常用的一些策略比如随机轮询、RR顺序轮询等。

我们在实际的生产实践中会有一些比较高级的负载均衡策略,比如说Least Conn、Least Request,他会观测你后端集群中连接数、请求数最少的实例进行分配。
还有如LA负载均衡策略,它可以动态计算后端的压力,比如说,可以根据qps和延迟来算一个权重。那些qps很高,延迟又很低的后端实力,我们可以认为它是一个比较优秀的后端实例,处理能力比较强,我们会给它比较高的权重,反之就给一个稍微低一些的权重。

另外一个比较常用的负载均衡策略就是一致性哈希。一致性哈希指的是说保证相同来源的请求能够落入相同的后端实例。这在一些后端实例有缓存,或者是有一些类似的场景的话,可以大幅提升请求的性能。

3.3 服务的自我保护
3.3.1 限流

正常一个长期稳定运行的服务,他们的请求是正常波形状且符合预期的,你可以观察他的流量峰值、平均值来判断服务真正的吞吐。

如果你的服务突然遇到持续性的、高频率的、不符合预期的突发流量。你需要检查一下服务是否有被错误调用、恶意攻击,或者下游程序逻辑问题。参考我的这一篇,就是典型的下游疯狂调用。
这种超出预期的调用经常会造成你的服务响应延迟,请求堆积,甚至服务雪崩。而雪崩会随着调用链向上传递,导致整个服务链的崩溃,对我们的系统造成很大的隐患。
限流通常指的是上游服务(服务B)这一侧,任何服务处理能力总是有限的,所以在超过他的处理能力之后,我们需要一些保护行为来避免服务过载。通常的做法就是让服务B快速返回失败来进行自我保护,否则大量请求在服务B堆积,在请求队列里阻塞,会造成大量资源损耗,也导致正常的请求无法被有效处理。
一些常见的限流方法,比如QPS限流、连接数限流,并发请求数限流等,都可以有效的对服务进行保护,下面列举几种常见的限流算法。

时间窗:简单易用

时间窗实现非常简单,大概原理是我们有一个统计的时间窗,比1分钟之内,我们只允许通过1000个请求。但是这种方法有一个比较大的缺陷,就是不太稳定。比如刚好在前10秒,就来了1000个请求,而后面50秒就不能够再接受任何请求了,非常的不均匀。所以在要求严谨的生产环境中比较少使用。

漏桶算法:定速流出

对于漏桶来说,由于它的出水口的速度是恒定的,也就是消化处理请求的速度是恒定的,所以它可以保证组件以恒定的速率来处理请求,这对一些对处理速度或者资源有严格要求的系统是非常实用的。

令牌桶:定速流入

令牌桶配了一个蓄水池,这个蓄水池通常如果请求比较少的话,那么它一直往蓄水池里面放水,就会导致这个蓄水池的容量会稍微多一些。那这样的话,在接下来的一个瞬时的流量高峰,它可以允许系统经过比这个平均速率更高一些的请求高峰。所以它有一定的弹性,在实践中也得到了非常广泛的使用。

3.3.2 熔断和降级

假设服务B非常繁忙,对于服务A正常的请求未能及时的返回结果,一直在Pending。而服务A在触发超时时间之后,按照重试策略又发了一次请求,服务B依然没有给返回。服务A经过多次重试,觉察到服务B有一些异常,他就自己做决策,认为服务B可能已经无法提供服务了,这个时候继续发出重试请求,可能意义不太大。所以它主动发起熔断(注意,是服务A发起的,因为B服务可能已经死了),就是一段时间内不再请求服务B。

A既然发起了熔断,那他总得返回对应的信息给用户,不能让用户或者更下游的服务一直等待。

这时候的处理方式就是fall back 到默认的处理信息,比如跳到一个预设的函数,指定返回默认设置的静态信息。或者对返回值进行降级,比如说是之前请求成功的一些信息,或者一些缓存的旧值,这样比什么都不返回好很多。
笔者这边的做法是制定一个特定的返回结构(包含状态码、错误信息、flag),带有特定信息标志,让前端可以识别出是正常的返回还是熔断自动返回。

3.4 实例故障后的离群检测
3.4.1 异常点驱逐

当集群中的某个服务实例发生故障的时候,其实我们最优先的做法是先进行驱逐,然后再检查问题,处理问题并恢复故障。所以,能否快速的对异常实例进行驱逐,对提高系统的可用性很重要。
下面的是ServiceMesh中Istio的异常驱逐配置,表示每秒钟扫描一次上游主机,连续失败 2 次返回 5xx 错误码的所有主机会被移出负载均衡连接池 3 分钟,并且上游被离群的主机在集群中占比不应该超过10%。

outlierDetection:
consecutiveErrors: 2
interval: 1s
baseEjectionTime: 3m
maxEjectionPercent: 10

微服务:服务治理来保证高可用相关推荐

  1. Eureka的初理解【服务注册与发现、高可用集群、自我保护机制、与Zookeeper的比较】

    Eureka的初理解 什么是Eureka? Eureka[读音要知道怎么读] Netflix 在设计 Eureka 时,遵循的就是AP原则(CAP文章下面有介绍). Eureka是Netflix的一个 ...

  2. rocketmq 如何保证高可用_RocketMq容灾、高可用方案

    一.实现分布式集群多副本的三种方式 1.1 M/S模式 即Master/Slaver模式.该模式在过去使用的最多,RocketMq之前也是使用这样的主从模式来实现的.主从模式分为同步模式和异步模式,区 ...

  3. rocketmq 如何保证高可用_如何保证消息队列是高可用的

    为什么写这篇文章? 博主有两位朋友分别是小A和小B: 小A,工作于传统软件行业(某社保局的软件外包公司),每天工作内容就是和产品聊聊需求,改改业务逻辑.再不然就是和运营聊聊天,写几个SQL,生成下报表 ...

  4. MySQL 基础技术(四)—— MySQL 如何保证高可用?

    一.引子 上一篇文章,我们讲述了<MySQL 如何保证数据不丢失?>,介绍了 binlog 和 redo log 的工作流程. 那么,MySQL 怎么保证高可用呢? 为了提高 MySQL ...

  5. Spring Cloud构建微服务架构(六)高可用服务注册中心

    前言 在Spring Cloud系列文章的开始,我们就介绍了服务注册与发现,其中,主要演示了如何构建和启动服务注册中心Eureka Server,以及如何将服务注册到Eureka Server中,但是 ...

  6. springcloud微服务多节点高性能、高可用、高并发部署

    1. 共有三个服务 discovery服务,domain服务,gateway服务. discovery服务是用来注册其他服务的,作为服务治理用. domain服务是主业务服务. gateway服务是所 ...

  7. [Linux基础与服务管理——常用集群高可用软件 Keepalived]

    1.Keepalived 简介 Keepalived是Linux下的一个免费的.轻量级的高可用解决方案.是一个由C语言编写的路由软件,主要目标是为Linux系统和基于Linux的基础架构提供简单而强大 ...

  8. TCC分布式实现原理及分布式应用如何保证高可用

    一.业务场景介绍 咱们先来看看业务场景,假设你现在有一个电商系统,里面有一个支付订单的场景. 那对一个订单支付之后,我们需要做下面的步骤: 更改订单的状态为"已支付" 扣减商品库存 ...

  9. 保证高可用Java服务化系统高效运行的必备工具箱

    来自:云时代架构 无论是技术开发人员还是架构设计人员都是在实践中成长起来的,他们通过实践进行总结,总结后把经验升华并再次应用到实践中去,进一步提供生产效率. 本文介绍笔者在互联网公司里线上应急和技术攻 ...

  10. 【Kafka】三.Kafka怎么保证高可用 学习总结

    Kafka 的副本机制 Kafka 的高可用实现主要依赖副本机制. Broker 和 Partition 的关系 在分析副本机制之前,先来看一下 Broker 和 Partition 之间的关系.Br ...

最新文章

  1. PHP artisan
  2. redis 自减命令_Redis 实战 —— 04. Redis 数据结构常用命令简介
  3. 简单的MYSQL数据库
  4. 关于抠图的一些文章方法收集
  5. Oracle的SQL基础之用户与表空间
  6. 【原创】.NET Core应用类型(Portable apps Self-contained apps)
  7. PAT甲级1024 ASCII码与整数转换
  8. c# pdf转html,C# 将PDF转为Word、Html、XPS、SVG、PCL、PS——基于Spire.Cloud.PDF
  9. word文档怎样删除最后一页空白页
  10. win11家庭中文版 安装docker 步骤
  11. ​杨利伟:在《太空一日》:我没有看到长城,但祖国的各个省份我大都看到了...
  12. Android studio: The number of method references in a .dex file cannot exceed 64K. Learn how to resol
  13. 如何从“固定型”思维模式转化为“成长型”思维模式
  14. 美女画廊(点击上面的图片下面进行显示)
  15. Linux常用的远程工具
  16. ps把白底图片改为透明
  17. 手机拍摄全景图并且使用Threejs实现VR全景,超简单WebVR
  18. ?nocache=+new Date().getTime();中的nocache是什么意思
  19. 软件开发好用的工具推荐
  20. 安东尼罗宾--激发你的无限潜能[连载]--17 18章

热门文章

  1. python 评论分析_python分析评论内容是积极的还是消极的(应用朴素做分词处理及情感识别)...
  2. 重装系统后,一直卡在天涯若比邻蓝屏
  3. bat脚本——提取多个文件夹到指定路径
  4. excel多列多行堆叠成多列一行_如何将多行多列数据转置成列-Excel中如何将多行多列数据转置成列...
  5. 微信支付接口调用之统一下单(一)
  6. input隐藏变显示
  7. VGA显示器屏幕文字模糊(稍有重影)的解决方案
  8. 抖音iOS基础技术大揭秘!
  9. “指付通”还是“支付痛”?-【软件和信息服务】2014.10
  10. 为什么我卸载了微博?