《微服务架构设计模式》读书笔记---第三章:微服务架构中的进程间

2020-05-24  本文已影响0人  白板时钟

微服务架构将应用程序构建为一组服务。这些服务必须经常协作才能处理各种外部请求。

当前有多种进程间通信机制供开发者选择。比较流行的是REST(使用JSON)。但需要牢记“没有银弹”这个大原则。

一个理想的微服务架构应该是在内部由松散耦合的若干服务组成,这些服务使用异步消息相互通信。

微服务架构中的进程间通信概述

交互方式

考虑交互方式将有助于你专注于需求,并避免陷入特定进程间通信技术的细节。

交互方式可以分为两个维度。第一个维度关注的是一对一和一对多。

交互方式的第二个维度关注的是同步和异步。

image.png

在微服务架构中定义API

服务的API是服务与其客户端之间的契约(contract)。设计良好的接口会在暴露有用功能同时隐藏实现的细节。

服务的API由客户端结构可以调用的方法和服务发布的事件组成。方法具备名称、参数和返回类型。事件具有一个类型和一组字段,发布到消息通道。

API优先设计。服务和它的客户端并不会一起编译。首先编写接口定义,然后与客户端开发人员一起查看这些接口定义。只有在反复迭代几轮API定义之后,才开始具体的服务实现编程。这种预先设计有助于你构建满足客户端需求的服务。

API的演化

语义化版本控制规范http://semver.org )为API版本控制提供了有用的指导。它是一组规则,用于指定如何使用版本号,并且以正确的方式递增版本号。

语义化版本控制规范(Semvers)要求版本号由三部分组成:MAJOR.MINOR.PATCH。必须按如下方式递增版本号:

如果你正在实现REST API,则可以使用主要版本作为URL路径的第一个元素。
如果你要实现使用消息机制的服务,则可以在其发布的消息中包含版本号。

消息的格式

进程间通信的本质是交换消息。消息通常包括数据。使用跨语言的消息格式尤为重要。

消息的格式可以分为两大类:文本和二进制。我们来逐一分析。

基于文本的消息格式
第一类是JSON和XML这样的基于文本的格式。
好处是:可读性很高,同时也是自描述的。这样的格式允许消息的接收方只挑选他们感兴趣的值,而忽略掉其他。因此,对消息结构的修改可以做到很好的后向兼容性。
弊端是:消息往往过度冗长(特别是XML),且解析文本引入的额外开销(尤其是在消息较大的时候,或者在对效率和性能敏感的场景下需要特别关注)。
二进制消息格式
有几种不同的二进制格式可供选择。常用的包括Protocol Buffers(https://developers.google.com/Protocol-buffers/docs/overview )和Avro(https://avro.apache.org )。
这两种格式都提供了一个强类型定义的IDL(接口描述文件),用于定义消息的格式,对于格式要求严格。编译器会自动根据这些格式生成序列化和反序列化的代码。因此你不得不采用API优先的方法来进行服务设计。此外,如果使用静态类型语言编写客户端,编译器会强制检查它是否使用了正确的API格式。

基于同步远程过程调用模式的通信

image.png

代理接口通常封装底层通信协议。有许多协议可供选择以REST和gRPC为例说明。

使用REST

REST是一种(总是)使用HTTP协议的进程间通信机制。
REST中的一个关键概念是资源,它通常表示单个业务对象,例如客户或产品,或业务对象的集合。
EST使用HTTP动词来操作资源,使用URL引用这些资源。

可能遇到的挑战
在一个请求中获取多个资源
REST资源通常以业务对象为导向,例如Consumer和Order。因此,设计REST API时的一个常见问题是如何使客户端能够在单个请求中检索多个相关对象。
此问题的一个解决方案是API允许客户端在获取资源时检索相关资源。例如,客户可以使用GET/orders/order-id-1345?expand=consumer检索Order及其Consumer。请求中的查询参数用来指定要与Order一起返回的相关资源。这种方法在许多场景中都很有效,但对于更复杂的场景来说,它通常是不够的。实现它也可能很耗时。
这导致了替代技术的日益普及,例如GraphQL(http://graphql.org)和Netflix Falcor(http://netflix.github.io/falcor),它们旨在支持高效的数据获取。

把操作映射为HTTP动词的挑战
另一个常见的REST API设计问题是如何将要在业务对象上执行的操作映射到HTTP动词。 REST API应该使用PUT进行更新,但可能有多种方法来更新订单,包括取消订单、修改订单等。此外,更新可能不是幂等的,但这却是使用PUT的要求。
一种解决方案是定义用于更新资源的特定方面的子资源。例如,Order Service具有用于取消订单的POST/orders/{orderId}/cancel端点,以及用于修订订单的POST/orders/{orderId}/revise端点。
另一种解决方案是将动词指定为URL的查询参数。可惜的是,这两种解决方案都不是特别符合RESTful的要求。
映射操作到HTTP动词的这个问题导致了REST替代方案的日益普及,例如gPRC,

REST的好处和弊端
好处:

弊端:

使用gRPC

gRPC是一种基于二进制消息的协议,这意味着需要采用API优先的方法来进行服务设计。

好处:

弊端:

使用断路器模式处理局部故障

当服务试图向另一个服务发送同步请求时,永远都面临着局部故障的风险。

image.png

要通过合理地设计服务来防止在整个应用程序中故障的传导和扩散。解决这个问题分为两部分:

开发可靠的远程过程调用代理,应对一下问题:

使用JVM:Netflix Hystrix
使用非JVM:Polly库(.NET)

从服务失效故障中恢复

每个服务的数据对客户来说重要性可能不同。Order Service的数据至关重要。如果此服务不可用,API Gateway应返回其数据的缓存版本或错误。来自其他服务的数据不太重要。例如,即使送餐状态不可用,客户也可以向用户显示有用的信息。如果Delivery Service不可用,API Gateway应返回其数据的缓存版本或从响应中省略它。

使用服务发现

在物理硬件上运行的传统应用程序中,服务实例的网络位置通常是静态的。
但在现代的基于云的微服务应用程序中,服务实例具有动态分配的网络位置。此外,由于自动扩展、故障和升级,服务实例集会动态更改。

服务发现,其关键组件是服务注册表,它是包含服务实例网络位置信息的一个数据库。

实现服务发现有以下两种主要方式:

服务及其客户直接与服务注册表交互
应用程序的服务及其客户端与服务注册表进行交互。这种服务发现方法是两种模式的组合。第一种模式是自注册模式(注册)。第二种模式是客户端发现模式(调用)。

Netflix开发并开源了几个组件,包括:
Eureka,这是一个高可用的服务注册表;Eureka Java客户端;
Ribbon,这是一个支持Eureka客户端的复杂HTTP客户端。

平台层服务发现模式

许多现代部署平台(如Docker和Kubernetes)都具有内置的服务注册表和服务发现机制。但是有限制,基于Kubernetes的发现仅适用于在Kubernetes上运行的服务。

基于异步消息模式的通信

使用消息机制时,服务之间的通信采用异步交换消息的方式完成。

消息由消息头部和消息主体组成。消息通过消息通道进行交换。


image.png

两种类型的消息通道:点对点和发布-订阅。

服务的异步API规范必须指定消息通道的名称、通过每个通道交换的消息类型及其格式。

使用消息代理

image.png

无代理消息
ZeroMQ(http://zeromq.org )是一种流行的无代理消息技术。它既是规范,也是一组适用于不同编程语言的库。它支持各种传输协议,包括TCP、UNIX风格的套接字和多播。

好处:

弊端:

基于代理的消息
消息代理是所有消息的中介节点。发送方将消息写入消息代理,消息代理将消息发送给接收方。
好处:

有许多消息代理可供选择。流行的开源消息代理包括:

好处

弊端

基于消息的架构的挑战

使用异步消息提高可用性

应该尽可能选择异步通信机制来处理服务之间的调用。同步消息会降低可用性。
消除同步交互
在必须处理同步请求的情况下,尽最大限度地降低同步通信的数量。

上一篇下一篇

猜你喜欢

热点阅读