02 | 微服务反模式与缺陷:超时反模式

Posted ACE1985

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了02 | 微服务反模式与缺陷:超时反模式相关的知识,希望对你有一定的参考价值。

译者简介:ASCE1885, 《android 高级进阶》作者。 本文首发于Source Code Chain开发者社区,欢迎使用我的专属邀请链接加入一起交流。

本系列是对《Microservices AntiPatterns and Pitfalls》一书的翻译,仅用于交流使用,谢谢!

微服务是分布式的架构模式,这意味其中所有的组件(即服务)是作为独立的应用部署的,并通过某种远程通信协议进行访问。任何分布式架构的挑战之一都是管理这些远程进程的可用性和响应性。虽然服务可用性和服务响应性都是跟服务通信相关,但两者是完全不同的概念。服务可用性指的是服务消费者能够连接到服务,并发送请求的能力,如图 2-1 所示。服务响应性则是指服务对某个给定请求的响应时间。

如果服务消费者连接不上服务(即可用性),那么服务消费者在毫秒级别时间内就能得到通知,如图 2-1 所示。此时,服务消费者可以选择将错误信息传递给客户端或者重试几次连接,但重试后依然连接不上时,那么就抛出某种类型的连接失败。假设服务可用了,服务消费者发送了一个请求,如果这时服务没有响应会发生什么呢?在这种情况下,服务消费者可以选择无限期的等待或者设置某个超时时间。

为服务响应性设置超时看起来是个不错的方案,但这可能会导致你进入一种被称为超时的反模式。

使用超时时间

到这里你可能会有一丝疑惑,难道设置超时时间不好吗?还真不一定,大多数情况下它会导致你走上错误的道路。假设你向某个服务发起一个请求,用来购买 1000 股苹果公司的股票(AAPL),作为服务消费者,你最不希望碰到的事情是,正当服务成功的处理完这笔交易并准备返回给你一个确认码时,你却因为设置的超时时间到而取消了这次请求。你可以重新提交一次请求,但服务为了判断这是一次新的交易请求还是重复的请求,需要增加很大的复杂度。而且,在第一次请求时,你没有成功获得交易确认码,因此,很难判断第一次交易是否成功。

因此,作为服务消费者,我们不想太早取消超时请求,那么应该如何设置超时时间呢?应对这个问题有几种方案。一个是计算服务所使用的数据库的超时时间,并以这个时间为基准来决定服务的超时时间。第二个解决方案,也是目前最流行的技术,是计算服务最大负载时间,并乘以 2,这样在某些情况下如果服务处理需要更长时间,那么会有一个足够的缓冲时间。

图 2-2 展示了这种技术,可以看到平均情况下服务进行一次交易所需响应时间是 2 秒。然而在高负载情况下,可以看到最长所需时间是 5 秒。因此,使用双倍时间的方案,服务消费者设置的超时时间应该是 10 秒。同样的,这种技术的目的是避免服务处理请求成功并正在发送确认码给消费者时,服务消费者由于超时时间到而取消这次请求。

到这里大家应该能看出为何这种方案是一种反模式了吧。虽然这似乎是超时问题的完美且合乎逻辑的解决方案,但它使得服务消费者每次请求都必须等待 10 秒钟才能确认服务没有响应。对于错误来说,10 秒等待时间太长了。大多数情况下,用户在重新点击提交按钮或者放弃等待关闭页面之前,不会等待超过 3 秒。因此,处理服务器响应性需要有更好的解决方案。

使用熔断器模式

与依赖超时时间来解决远程服务调用响应性问题相反,更好的方案是使用名为熔断器的模式。这种软件模式跟家里的断路器工作原理一样。当它关闭时,电流可以通过它,一旦它打开,那么电流就流通不过,除非它再次关闭。类似的,如果一个软件熔断器检测到一个服务没有响应,它会处于打开状态,并拒绝到这个服务的所有请求。一旦服务恢复响应,熔断器将会关闭,从而允许请求重新通过。

图 2-3 展示了熔断器模式的工作原理。熔断器持续不断的监听远程服务,确保服务是存活的并可响应的(后面会再讲到)。当服务保持可响应时,熔断器保持关闭,允许请求通过。如果远程服务突然变得不可响应,熔断器将会打开,从而防止请求通过,直到服务再次恢复响应为止。然而,和家里的断路器不同的是,软件熔断器可以持续不断的对服务进行监听,并在远程服务恢复响应时自动关闭(译者注:家里的断路器一旦断开,一般需要人工手动关闭)。

根据具体实现,服务消费者将首先检查熔断器是否打开,这通常可以通过拦截器模式来实现,这样服务消费者不需要知道请求路径中是否存在熔断器。无论哪种情况,熔断器模式相比超时时间机制的显著优点是当服务变得不可响应时,服务消费者能够立即知道,而不是等到超时时间到才知道。在前面的例子中,如果使用熔断器来代替超时时间,那么服务消费者将在毫秒级别时间内就能够知道服务不可响应,而不是要等待 10 秒后才知道。

熔断器监听远程服务的方式有几种。最简单的一种是使用简单的心跳检测机制(例如 ping)来判断远程服务是否存活。虽然这种方式相对简单和易于实现,但它的功能只限于让熔断器知道远程服务是否存活,对于实际服务请求的响应性一无所知。为了更好的得到关于响应性的信息,我们可以使用合成交易。一个合成交易指的是周期性的(例如每 10 秒)向服务发送一条虚假交易,这是熔断器常用的另一种监听技术。虚假交易能够覆盖并执行服务提供的所有功能,从而使得熔断器能够准确的测量服务的响应性。但合成交易可能是很复杂和难以实现的,因为这要求应用或者系统的所有部分都需要知道合成交易的存在(译者注:并作特殊的处理)。第三种类型的监听是实时的用户监听,也就是对真实的生产环境中的交易进行监听从而获得响应性信息。一旦响应性达到某个阈值,熔断器将进入所谓的半开状态,只允许一定数量的交易通过(例如十个交易只通过一个)。一旦服务响应性回归正常值,熔断器将再次关闭,从而允许所有交易通过。

目前开源社区有几种熔断器模式的实现,其中包括来自 Netflix 的 Hystrix,以及 Github 上其他很多的实现。作为框架的一部分,Akka 框架中的 CircuitBreaker 类也实现了熔断器的功能。

通过下面的资料,你可以获得更多关于熔断器模式的信息:

  • Michael Nygard 的经典好书《发布!软件的设计与部署》

  • Martin Fowler 关于熔断器的博文

  • Microsoft MSDN library

以上是关于02 | 微服务反模式与缺陷:超时反模式的主要内容,如果未能解决你的问题,请参考以下文章

03 | 微服务反模式与缺陷:代码共享反模式

04 | 微服务反模式与缺陷:直达式报告反模式

05 | 微服务反模式与陷阱:沙粒缺陷

03 | 微服务反模式与陷阱:代码共享反模式

04 | 微服务反模式与陷阱:直达式报告反模式

01 | 微服务反模式与陷阱:数据驱动架构迁移