怎么保证微服务应用的高可用性?

对接三方接口问题

之前的文章 中介绍了微服务架构中关于 熔断、降级、限流、压测、隔离、超时控制 的内容,主要涉及到“自我一方”的优化方案。在微服务架构中,还有很关键的一个环节,那就是要跟第三方系统打交道。比如:

  • 登录注册要跟微信开放平台打交道,接入扫码登录。
  • 支付业务要跟微信支付/支付宝 打交道,比如在线支付和退款。
  • 常用的系统登录需要发验证码,要跟短信服务商打交道。

三方服务监测和告警

由于三方接口我们无法完全控制和掌握,那么,如何保证调用第三方接口的可用性?

大多数人对接三方 API 的时候只是简单实现了功能,并没有考虑可用性和容错之类的问题。首先需要清楚 对接第三方平台API的是一个独立的模块还是一个独立的服务,这取决于你维护的是一个单体应用还是微服务应用。

对于自己系统内这样一个第三方模块或者第三方服务来说,它要解决的问题也很直观:

  • 提供一个一致性抽象,屏蔽不同第三方平台 API 之间的差异。
  • 提供客户端治理,即提供调用第三方平台 API 的重试、限流等功能。
  • 提供可观测性支持。
  • 提供测试支持。

一致性抽象

什么是一致性抽象?举个例子,如果你调用的是第三方支付平台,你们公司支持多种接入方式,包括微信支付、支付宝支付、抖音支付、xxx。

在这种情况下,业务方只希望调用你的某个接口,然后告诉你支付所需要的基本信息,比如说金额和方式,业务方完全不需要关心其中的任何细节。你这个接口的实现就能根据具体的支付方式发起调用。
在这里插入图片描述
这种一致性抽象会统一解决很多细节问题。比如不同的通信协议、不同的加密解密算法、不同的请求和响应格式、不同的身份认证和鉴权机制、不同的回调机制等等。这会带来两个好处。

  1. 研发效率大幅提高, 对于业务方来说他们不需要了解第三方的任何细节,所以他们接入一个第三方会是一件很简单的事情。
  2. 高可扩展性, 你可以通过扩展接口的方式轻松接入新的第三方,而已有的业务完全不会受到影响。

提供了一致性抽象之后,你就可以在这种一致性抽象上做很多事情,比如说客户端治理。

客户端治理

前面我们在讲熔断、降级、限流的时候,实际上都是在服务端或者网关进行的,那么这一次要讲的就是在客户端进行治理了。一般来说,客户端治理有两个关键措施: 限流重试

就拿限流来说,大部分的第三方平台 API 为了保护自己的系统,是不允许你频繁发送请求的。比如说某些银行的接口只允许你一秒钟发送十个请求,多了就会拒绝服务。那么自然地,你其实可以在你发起调用之前就开启限流,这样就可以省去一次必然失败的调用。
在这里插入图片描述
另外一个重要的措施是重试。当你调用第三方平台超时的时候,业务方肯定不希望你直接返回超时响应,因为他们还要自己处理超时,比如说发起重试等。所以你可以提供重试机制,并且可以对业务方保持透明。但你要小心的是,只有当第三方接口是 幂等 的时候你才能发起重试。

所谓幂等,就是无论Http或者RPC接口在入参不变的情况下,无论请求多少次,结果都是一样的,请求结果不会因为请求次数不同而改变。比如说设定要更新的金额为100元,那么无论更新多少次最终结果都是100元;但是如果传递的是个增量,比如增加20元,那么每多请求一次,就会多加20元。

幂等接口常见的设计方案:

  • 客户端按钮提交限制,每次提交一个请求时,按钮置为不可用。
  • 后台系统逻辑层处理,生成保存唯一ID,每次请求先校验这个ID是否已经存在,存在则表示重复操作,直接返回上一次操作结果。
  • token 校验机制,客户端请求前先申请 token,同一个 token 只处理一次,无 token 或者相同 token 不做处理。
  • 分布式锁,如引入 Redis 分布式锁(set+nx),防止其他请求重复操作。
  • 请求队列,引入 MQ 排队的方式让请求有序处理。

当你完成客户端治理之后,一般是不会出问题的。但万一业务出问题了怎么办?这时候你就需要可观测性支持。

可观测性支持

第三方接口一般都不在你的控制范围内,所以你一定要做好监控,比如说接入 Prometheus 和SkyWalking 等工具。同时,你还要考虑提供便利的查询工具,让你自己和你的业务方都能够快速定位问题。

告警也是必不可少的。这些告警分成两类,一类是给你和你共同维护这个功能的同事使用的,另外一类是给业务方用的。例如,当监控系统发现第三方平台突然不可用了,那么它会发出两个告警,一个是告诉你出事了;另外一个则是通知业务方第三方平台目前不稳定,那么业务方就需要确认对他们业务的影响范围,以及他们是否需要启动一些容错措施。记住:监控没告警,等于没监控!
在这里插入图片描述

【案例】早期某个系统调用第三方接口的时候,缺乏监控和告警,只有等用户出现问题联系客服的时候,或者业务方发现出现故障报告过来的时候才知道出问题了,这样对用户体验很不好。后面接入了监控和告警之后,在第三方接口出问题的短时间内,就能得到通知,然后快速启动各种容错预案,并且通知业务方和第三方。很大程度的提升了用户体验。

可观测性做得好,定位和解决问题就会变得很简单。但是能不能进一步降低一点出问题的概率呢?当然是可以的,你把测试支持做好,让你的业务方多测测,省得出了问题甩锅给你。

测试支持

测试支持的核心是你要提供 mock服务。例如正常情况下,业务方调用你的接口,你会真的调用第三方API。但是在测试环境下,你就要考虑返回mock响应。

如果第三方平台还有回调机制,并且你在收到回调之后还要通知业务方,那么你还需要模拟这个回调。比如说微信支付接口后面会回调你的一个接口,告知你支付结果。

使用 mock 服务有很多好处。

  • 没有额外开支。比如说发短信之类的,短信是收费的,那么测试服务如果能避免真的发送短信,多少也能省一点。
  • 不受制于第三方平台。有些第三方平台的认证和鉴权机制非常复杂,在测试环境要发起一次调用几乎不可能,那么只能用 mock 服务。
  • 你可以返回业务方任何预期的响应,包括成功响应、失败响应,甚至于你还能返回模拟第三方平台超时的响应。

如果考虑到压测之类的问题,那么这个 mock 功能就更加必不可少了,毕竟第三方是不可能配合你做压测的。

出现故障的容错方案

在任何跟第三方打交道的场景之下,都要考虑好第三方崩溃的时候自己的系统怎么容错。公司或者部门内部的调用出现问题了,还可以推动同事快速修复。但是第三方是推不动的,只能是我们调用者考虑容错。一般来说,可以从以下方面去考虑容错: 同步转异步、自动替换第三方、压测支持。

同步转异步

在一些不需要立刻拿到响应的场景,如果你发现第三方已经崩溃了,你可以将业务方的请求临时存储起来。等后面第三方恢复了再继续调用第三方处理。这种方案一般用于对时效性要求不高的业务。比如业务方只是要求你上报数据,不要求你立刻成功,那么你就可以采用这种方案。这个容错方案的关键词就是 同步转异步

其实,这种容错机制完全可以做成利用消息队列来彻底解耦的形式。在这种解耦的架构下,业务方不再是同步调用一个接口,而是把消息丢到消息队列里面。然后我们的服务不断消费消息,调用第三方接口处理业务。等处理完毕再将响应通过消息队列通知业务方。

自动替换第三方

调用一个第三方的接口失败的时候,你可以考虑换一个第三方。

举例来说,你们公司有 A、B、C 三个短信供应商。现在你在选择使用 A 的时候,发现 A 一直返回失败的响应,或者说响应时间很长,那么你就可以考虑自动切换到 B 上。

如果全部可用的第三方都崩溃了怎么办?从概率上分析,一个服务出故障是小概率,多个同时出故障那就更是小概率事件了,在这种情况下只能是告警和人工介入修复了。也就是所谓的尽人事,听天命。

压测支持

正常来说,你不能指望第三方会配合你的压测。你可以设想,类似于微信之类的开放平台是不可能配合你搞什么压力测试的。甚至即便你是非常强硬的甲方,你想让乙方配合你做压力测试,也是不现实的。所以你只能考虑通过 mock 来提供压测支持。和正常的测试支持比起来,压测你需要做到三件事。

  • 模拟第三方的响应时间。可以在代码里面睡眠一段时间,这段时间是第三方接口的平均响应时间加上一个随机偏移量计算得出的。
  • 模拟触发你的容错机制。如果你采用了同步转异步这种容错机制,那么你需要确保在流量很大的情况下,你确实转异步了;如果你采用的是自动切换第三方,那你也要确保真的如同你设想的那样真的切换了新的第三方。
  • 流量分发。如果是在全链路压测的情况下,压测流量你会分发到 mock 逻辑,而真实业务请求你是真的调用第三方。

微服务高可用方案

一般衡量可用性,都是用 SLA(Service Level Aggrement)指标,通常用 N 个九来说明。例如,当我们说微服务的可用性是三个九,是指系统在一段时间内(一般是一年)正常提供服务的时间超过了 99.9%。

那么高可用究竟有多高?一般是指可用性需要达到三个九,当然有些人会认为需要达到四个九,这并没有硬性的标准。那么怎么做到高可用呢?

核心策略

核心策略可以从下面四个方面考虑:

  • 容错;
  • 限制故障影响范围;
  • 出现故障可以快速发现,快速修复;
  • 规范变更流程。

容错

容错是指不管发生了什么,你的系统都能正常提供服务,也就是所谓的 凑合用

比如让你设计一辆自行车,如果要考虑到高可用,那么你可以在自行车后轮上面加两个脚蹬子,当自行车前轮坏掉了,还可以把这个自行车变为独轮车使用。

在这里插入图片描述

系统中可能出问题的组件包括你的服务本身、你依赖的服务,还包括你依赖的硬件基础设施和软件基础设施。因此关键点是怎么保证自己的服务即便在遇到了一些故障的情况下,整个系统也能继续为用户提供服务。

软件基础设施如果出问题了,你是否能保证你的服务还能正常运作。如果你的服务不能正常运作,系统整体也要能运作,这主要考虑两个点。

  1. 在公司内使用软件基础设施的高可用方案。比如说在使用 Redis 的时候就不再是使用单机 Redis,而是改成 Redis Cluster 或者直接使用云厂商的 Redis 服务。
  2. 做好万一软件崩溃的容错手段。比如说如果 Redis 崩溃了,你可以使用限流来保护数据库。

容错的问题就是不管你怎么容错,最终都有可能出错,所以到了真出错的时候,你就要考虑限制故障影响范围。

限制故障影响范围

限制故障影响范围是指万一真的出现了故障,也要尽可能减轻它的影响范围。影响范围可以从三个角度来考虑,尽可能使故障造成的业务损失更小、被影响的用户更少,还有被影响的其他组件更少。

限制影响范围的最佳策略就是 隔离。一个复杂的系统被划分成独立的不同的服务,服务内部再进一步细分模块、核心服务与非核心服务,尽量降低相互之间的影响。

就像是商场里面的防火门,一旦着火,降下防火门,避免更多空间受到影响。

但是普遍来说,想要缩小影响范围总是面临两个难点。

  • 服务互相依赖, 这种依赖一部分源自业务本身的复杂度,另外一部分则源自设计不合理。我们可以通过改进设计来降低服务之间的依赖,但是不可能做到彻底没有依赖。比如使用 解耦方案,通过改进设计来降低服务之间的依赖。

比如说在一个简单的创建订单的场景中,创建订单、支付是必须要同步执行成功的。但是另外一些部分,比如说发邮件通知用户下单成功、给用户增加积分 这种就可以延迟执行。

  • 服务共享一部分基础设施。理论上只要你有足够多的钱,能够为每一个服务提供完全独立的基础设施,就可以彻底解决这个问题。但是本着“降本增效”的原则,很多公司都会共享一些基础设施和服务资源。

快速发现和快速修复故障

快速发现 强调的是 完备的观测和告警系统。观测不仅要观测服务本身,也要对各种基础设施、第三方依赖进行观测,尤其是在你核心链路上依赖的东西,你都需要进行全方位地观测。有了观测之后,还要设置合理的告警。 没有告警的观测是没有灵魂的

快速恢复 则是尽可能减少服务不可用的时间,当服务出现故障的时候,相关负责人收到了告警信息,需要快速修复。

规范变更流程

规范变更流程是指任何一个人都不能随意发布新版本,也不能随意修改配置。任何一个变更都要经过 review,并且做好回退的准备。

实际上,我们在实践中最害怕的就是发布新版本或者新配置。因为原本系统都运作得非常好,但是一旦上线新功能或者变更配置,就很容易出现线上故障。特别是有些时候因为急着修 Bug,根本没有测试就直接发布新代码或者配置,导致不仅已有的 Bug 没修好,还造成了新的问题。正所谓:变更不规范,背锅两行泪!

因此变更流程是一定要搞好的,搞好了变更流程,可用性就能大幅提升。

全方位考虑

要想保证系统尽可能的高性能运转,需要准备一个 从前端到后端全方位的、完整的 高可用方案,而且要仔细思考其中的几个环节。

  • 所有面向前端用户的接口有没有限流之类的措施,防止攻击者伪造大量请求把你的系统搞崩。
  • 你所依赖的第三方组件,包括缓存(如 Redis)、数据库(如 MySQL)、消息队列(如 Kafka)是否启用了高可用方案。
  • 如果你依赖的某个第三方组件崩溃了,你维护的服务会发生什么事情,整个系统是否还能正常提供服务。
  • 你的所有服务是否选择了合适的负载均衡算法,是否有熔断、降级、限流和超时控制等治理措施。
  • 你所在公司的上线流程、配置变更流程等和研发息息相关的流程,或者说你认为会对系统可用性产生影响的各种流程。

整个思路可以拆解成几个部分,分别是 发现问题、计划方案、落地实施、取得效果、后续改进。 下面通过一个案例来分析一下整个过程。

发现问题

某某业务是公司的核心业务,它的核心困难是需要保证高可用。起初这个系统的可用性还是比较低的。某天,别的业务组突然上线了一个功能,带来了非常多的 Redis 大对象操作,以至于 Redis 响应非常慢,把我们的核心服务搞超时了。

后面经过调研,总结下来,系统可用性不高主要是这三个原因导致的。

  1. 缺乏监控和告警,导致难以发现问题,难以定位问题,难以解决问题。
  2. 缺乏服务治理,导致某一个服务出现故障的时候,整个系统都不可用了。
  3. 缺乏合理的变更流程。如果有更加合理的变更流程的话,那么大部分事故都是可以避免的。

计划方案

针对这些具体的问题点,可以将可用性改进计划分成如下几个步骤:

  1. 引入全方位的监控与告警,这一步是为了快速发现问题和定位问题。
  2. 引入各种服务治理措施,这一步是为了提高服务本身的可用性,并且降低不同服务相互之间的影响。
  3. 为所有第三方依赖引入高可用方案,这一步是为了提高第三方依赖的可用性。
  4. 拆分核心业务与非核心业务的共同依赖。这一步是为了进一步提高核心业务的可用性。
  5. 规范变更流程,降低因为变更而引入 Bug 的可能性。

在这里插入图片描述

落地实施

  • 【监控与告警】就监控来说,既要为业务服务添加监控和告警,又要为第三方依赖增加监控,比如说监控数据库、Redis 和消息队列。而告警则要综合考虑告警频率、告警方式以及告警信息的内容是否足够充足,减少误报和谎报。本身这个东西并不是很难,就是非常琐碎,要一个个链路捋过去,一个个业务查漏补缺。

  • 【服务治理】就第二个步骤来说,服务治理包括的范围比较广,可以使用的方案也比较多,比如说限流熔断等等。

  • 【第三方高可用】第三个步骤可能会遇到比较大的阻力,主要是大部分第三方依赖的高可用方案都需要资金投入。比如说从单机 Redis到引入 Redis Cluster 的时候,就需要部署更多的实例。

  • 【拆分依赖】为了尽可能少的出现故障,可以对新的核心业务启用新的第三方依赖集群,比如说 Redis 集群,但是老的核心业务就保持不动。

  • 【规范流程】比如说制定新的规范,主要是代码review,上线规范(包括上线流程、回滚计划)等内容。

取得效果

经过改进之后,现在服务的可用性从原来不足两个九提升到了三个九(99.9%),Bug 数量也减少了。Nice!!!

后续改进

一个问题从发现到找出临时应对方案、再到付诸实施,一不留神一个小时就过去了。所以在达成三个九以后,如果你还想进一步提升可用性,要么降低出事故的概率,要么提高反应速度。

人本身做不到长时间精神紧绷 24 小时待命,并且同一个项目组的项目也很难说了如指掌,所以自动故障处理机制的重要性不言而喻。甚至可以说,如果没有自动故障处理机制,是不可能达到四个九的可用性的。

举个例子:微服务集群自动扩容,它是指对整个微服务集群进行监控,如果发现集群负载过高那么就会自动扩容。

为了进一步提高整个集群服务的可用性,让运维同学配置了自动扩容。整个设计方案是允许不同的业务方设置不同的扩容条件,满足条件之后运维就会自动扩容。比如说给某个服务设置了 CPU 90% 的指标。如果这个服务所有节点的 CPU 使用率都已经超过了 90%,并且持续了一段时间,那么就会触发自动扩容,每次扩容会新增一个节点。

还有一些常见的方案可以参考:

  • 自动修复数据,最常见的就是有一个定时任务比对不同的业务数据,如果数据不一致,就会发出告警,同时触发自动修复动作。
  • 自动补发消息,也是通过定时任务等机制来比对业务数据,如果发现某条消息还没发,就会触发告警,同时触发补发消息动作。
  • 18
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浮尘笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值