应用架构的演进 | 使用无服务器构建业务弹性

发布时间 2023-10-27 16:31:04作者: 亚马逊云开发者

在亚马逊全面转向微服务后,微服务所带来的敏捷性让亚马逊的在快速出现的竞争对手面前展现出绝对的优势。请回顾本系列文章的第一篇《应用架构的演进 | 亚马逊的微服务实践》了解详情。当同时生产和运维团队都迎来了新问题,他们快速意识到:

微服务应用架构有很多优势,但是并不代表它适合所有的业务模型。选择单体应用架构还是微服务应用架构需要依据业务模型。同时,构建和运行一个微服务应用并不简单,会面临新的挑战。

亚马逊云科技开发者社区为开发者们提供全球的开发技术资源。这里有技术文档、开发案例、技术专栏、培训视频、活动与竞赛等。帮助中国开发者对接世界最前沿技术,观点,和项目,并将中国优秀开发者或技术推荐给全球云社区。如果你还没有关注/收藏,看到这里请一定不要匆匆划过,点这里让它成为你的技术宝库!

图片
图 1

微服务之间采用面向服务的松耦合架构,同时微服务之间存在复杂的依赖关系,这样会导致出现:

  • 一个微服务的变更影响依赖它的其他微服务。
  • 进程间通信会因为一个请求故障而使得消息丢失或处理不一致,影响整个应用架构的可靠性。
  • 微服务出现大量请求,系统会被 FLOOD 来的流量压垮。
  • 没有事件追踪和监控机制,可观测性差,难以处理故障排查。
  • 请求无法保证消息顺序,可能会导致数据不一致。

为了保证业务的敏捷性,系统的可靠性,数据的一致性,松耦合的微服务架构需要事件中间件或事件总线,用于在不同组件之间进行事件驱动的通信。无服务器架构就擅长利用事件驱动架构和 FaaS(函数即服务)来实现应用构建。

无服务器架构的主要特征包括:

  • 函数即服务(FaaS):把应用拆分成个别的函数,完全由第三方服务运维。
  • 事件驱动:函数之间使用事件触发调用,而不是直接调用。
  • 无状态:函数不维护任何状态信息。
  • 瞬时可缩放:函数会根据事件负载自动扩缩容。
  • 无服务端:不需要自己管理服务器,完全依赖第三方服务。
  • 微服务化:应用被拆分成松耦合的小型函数。

让我们具体了解一下无服务器事件驱动通过哪些技术模式来实现微服务架构的进程间通信的可靠性,数据一致性,以及整个业务架构的敏捷性,可靠性,可观测性。

进程间通信模式

与单体应用架构不同,微服务进程间通信依赖服务发现来定位服务,基于网络进行通信,如 REST API、RPC 等。常见的通信模式有两种:“同步远程访问模式”和“异步消息传递模式”。

同步远程访问模式

同步远程访问模式指的是客户端向服务发送请求,服务处理请求并发回响应。

图片
图 2 来源于:《Microservices Patterns》作者:Chris Richardson

图中所示,客户端的业务逻辑调用由 RPI 代理适配器类实现的接口。RPI 代理向服务发出请求。RPI 服务器适配器通过调用服务的业务逻辑来处理请求。然后将回复发送给 RPI 代理,RPI 代理再将结果返回给客户端的业务逻辑。这是一个较为典型的用封装了底层通信协议的代理接口来调用服务的业务逻辑。因为封装了传输细节,使得服务间调用更简单,但同时对于网络质量和延迟要求却非常高。在高并发下,容易产生大量等待和阻塞,成为性能瓶颈。

异步消息传递模式

异步的信息传递是微服务进程间通信的另一种模式。服务间异步通信通过中间件比如消息队列来实现。

图片
图 3 来源于:《Microservices Patterns》作者:Chris Richardson

云原生的异步消息传递

如果你在云上创建异步远程信息传递,可以选择云原生服务更轻松的实现。比如亚马逊云科技的 SNS 和 SQS 服务。

  • SNS 是亚马逊云科技提供的发布/订阅的消息服务。
  • SQS 提供了消息队列服务。

图片
图 4

工作原理如图所示,SNS 将运行在 EC2 上的 microservice A 提供的服务消息,通过事件驱动的方式,发布到多个 SQS 队列,被多个接收方订阅和消费。我们称之为消息扇出。用 SQS 做消息路由,SNS 做消息存储的消息扇出给用户带来了异步消息传递模式的新思路:

  • 消息生产者只需要发布一条消息,而不需要单独发送给每个消费者,解耦了生产者和消费者。
  • 消费者可以根据需要选择订阅相关主题的消息,实现按需接收。
  • 单个消息可以被多个消费者处理,提升效率。

实现异步消息传递模式的云原生服务还有 Amazon EventBridge。Amazon EventBridge 是亚马逊云提供的一个无服务器事件总线服务,让不同的应用、数据源和服务无缝连接,构建可靠、弹性的事件驱动架构。它是亚马逊云生态系统事件管理的中央枢纽。

我们用乐高的真实案例来了解一下具体的工作方式:

图片
图 5

如同所示,这是乐高使用 Amazon EventBridge 构建的结账事件处理。它是一个事件驱动的通信模型。订单等客户事件发生时,事件会被发送给 EventBridge,相关的微服务会监听这些事件,并在它们有事件发生时采取行动。

早在 2017 年,乐高就有一个在 prem 上运行的单体应用,但在一次黑色星期五促销活动导致整个系统崩溃后,他们开始寻找可扩展性更强的系统。他们开始解耦后端,构建了一个事件驱动的无服务器应用程序,最终取得了成功。

熔断模式

微服务众多,且彼此之间有各种依赖关系,任何一个微服务故障可能会造成级联故障。同时,微服务之间存在大量的服务调用,网络问题等也会导致调用失败和超时。因此需要设置隔离机制、限流和熔断机制来防止故障扩散和服务调用的可靠性。

图片
图 6 改编于:《Microservices Patterns》作者:Chris Richardson

如图所示,当服务 A 出现问题,为了减小或避免对下游的服务的影响,使用 circuit breaker(熔断)来实现服务的降级。我们称之为熔断模式,在微服务架构中是保证微服务可靠性和稳定性很重要的模式。

熔断模式的工作方式是:

  • 当一个服务调用失败(异常或超时)达到一定阈值时,断路器会切断对这个服务的调用,避免导致整体服务崩溃。
  • 在一定时间内所有对该服务的调用会直接失败,不会导致线程阻塞。当服务恢复时,断路器会动态地将连接再次闭合。
  • 微服务中的熔断模式可以通过多种技术来实现。
  • 代码实现--我们可以使用现有的容错库,针对每个客户端代码实现。比如 Java 的容错库 resilience4j,Python 中的熔断器库 circuit breaker 等等;开发者可以根据语言选择合适的库来利用熔断器提高系统稳定性。
  • 在微服务应用架构中使用分布式的 circuit breaker 服务,比如 lambda circuit breaker;EventBridge circuit breaker, Queue processor circuit breaker。

Lambda Circuit Breaker

Lambda Circuit Breaker 通过 Lambda 函数实现熔断,无需修改目标服务代码。

图片
图 7

如图所示,这是一个开源项目 cdk-pattens 图示中的 DynamoDB 用于监控和记录下游请求的状态。当 DynamoDB 中记录的下游请求失败率超过设定阈值时,触发熔断开关。circuit break 就会 enable。Lambda 将不再调用目标服务。client 就会收到 fallback function 直接返回错误响应。避免导致级联故障。在熔断一段时间后,允许一定流量请求通过,如果请求成功则关闭熔断器。

此外 Lambda Circuit Breaker 还可以:

  • 提供配置的错误阈值、熔断时间等参数。可以细粒度控制熔断器行为。
  • 自动生成客户端 SDK,使得从应用程序调用熔断器断路逻辑变得简单。
  • 能够同时为多个服务实例添加熔断器。
  • 提供实时的监控指标和报警。

EventBridge Circuit Breaker

亚马逊云生态系统事件管理的中央枢纽 Amazon Event Bridge 的一个重要特性,就是 EventBridge Circuit Breaker。它可以帮助构建更可靠和容错的服务。

图片
图 8

通过使用 EventBridge Circuit Breaker,可以构建出对下游服务故障更为容错的事件驱动架构,提高整体系统的可靠性。如图所示:

  1. EventBridge Circuit Breaker 自动检测目标服务的故障,并把故障信息发送给 EventBridge。
  2. EventBridge 的总线上创建规则,来触发另外一个lambda 函数,来更新 DynamoDB 中的“故障值”,
  3. 当检测到目标持续返回错误达到设定阀值时,Circuit Breaker 会自动切断对该目标的调用,避免不必要的失败调用占用资源。
  4. 当目标服务恢复时,Circuit Breaker 会自动恢复对其的调用。

相对于 Lambda Circuit Breaker 直接在数据库中查询请求状态,并根据阀值启动熔断相比,这是一个分布式的断路器解决方案。所有的应用程序可以共享这个断路器的状态。

Queue Processor Circuit Breaker

当服务调用通过消息队列来实现时,我们设计了一个消息解耦的系统,允许上游服务把消息放进队列里供下游服务来消费。如果下游服务由于某种原因不停消费某个消息,就产生大量无用的调用,不仅造成了计算资源的浪费,还会产生了海量的日志,甚至有可能把日志系统拖垮了。

如何设计一个断路器来解决以上可能出现的问题?

图片
图 9

如图所示,在亚马逊云上可以通过多个云原生服务,利用错误日志来设计消息队列进程的断路器。

  1. 当调用下游服务失败,会产生错误日志,亚马逊云的日志监控服务 CloudWatch 会 filter 出错误日志(Errors, Timeout 的日志)并生成 Metrics。
  2. Metric 超过设定阀值,CloudWatch 通过 Alarm 触发 lambda EvenSource mapping 断开 SQS 的调用。不再消费消息队列里面上游服务放进来的消息。
  3. 当下游服务不再被调用,没有持续的错误日志产生,CloudWatch 的 Alarm 恢复为图中的“OK”状态。
  4. OK 状态会触发 Step Function(half open-半开)来判断下游服务是否已经恢复。
  5. Setp function 半开的状态中,即使错误日志数量低于设定的阀值,但不能完全确认下游服务是否回复,因此在一段时间内仍然保持服务调用为“等待”状态。
  6. 等待状态的同时,调用另外一个 Trail Message Poller lambda 函数,也就是从 SQS 中拉一条消息给 SQS Message processiong function 来测试。如果得到回复,获得成功,circuit breaker 才被 disable,恢复上下游服务调用。如果失败,就继续等待下一次 trail。

小结

无服务器架构下,功能由事件驱动的 Function 组成。这些 Function 之间很少有进程内直接调用,主要通过事件触发机制进行通信,如 SNS、SQS、事件总线等。这种松耦合方式提高了弹性。当流量激增时,可以自动扩展订阅 SNS/SQS 的 Function 数量,而不会影响其他 Function。

熔断机制由于无状态的 Function 部署迅速,可以实现细粒度的弹性缩放。当后端 Function 因流量激增而响应变慢时,前端 Function 可以检测到并快速失败,从而实现熔断。亚马逊云科技提供的云原生无服务计算 Lambda 本身也支持配置 Reserved Concurrency 以防止冷启动导致的熔断。

无服务器架构天然适应流量峰谷,实现了高弹性、高可用的系统架构。这正是亚马逊大规模采用它的重要原因。

请持续关注 Build On Cloud 专栏,了解更多面向开发者的技术分享和云开发动态!

图片

 

文章来源:
https://dev.amazoncloud.cn/column/article/6537106dcbe45c60663a6595?sc_medium=regulartraffic&sc_campaign=crossplatform&sc_channel=bokey