在微服务架构中,通信是关键要素,关于选择最有效的服务间交互方法的讨论也非常广泛。在这篇介绍性文章中,我们将探讨并总结微服务的最 佳通信策略,提供关于何时以及如何有效使用每种通信风格的见解。
交互风格
要有效理解服务在微服务架构中如何通信,首先必须熟悉可用的交互风格。每种风格都有其独特的优点和缺点。深入了解这些细微差别对于在选择适当的通信机制之前做出明智决策至关重要。这一基础知识确保所选方法能够很好地与系统的具体要求和挑战相契合。
交互风格可以分为两个维度,第一个维度是交互是 一对一 还是 一对多:
一对一 — 每个客户端请求由一个服务处理。
一对多 — 每个请求由多个服务处理。
第二个维度是交互是 同步 还是 异步。
同步 — 客户端期望服务及时响应,甚至可能会阻塞等待。
异步 — 客户端不会阻塞,响应(如果有的话)不一定会立即发送。
下表展示了不同的维度:
通信维度
让我们简要讨论每一种。
一对一交互:
请求/响应 — 服务客户端向服务发送请求并等待响应。客户端期望响应及时到达,甚至可能会阻塞等待。这种交互风格通常导致服务之间紧密耦合。
异步请求/响应 — 服务客户端向服务发送请求,服务异步回复。客户端不会阻塞等待,因为服务可能很长时间不发送响应。
单向通知 — 服务客户端向服务发送请求,但不期望或发送回复。
一对多交互
发布/订阅 — 客户端发布通知消息,感兴趣的服务消费消息。
发布/异步响应 — 客户端发布请求消息,然后等待一段时间,接收感兴趣的服务的响应。
请记住,一个服务可以有多种通信方式。
使用同步远程过程调用模式通信
客户端向服务发送请求,服务处理请求并发送响应。某些客户端可能会阻塞等待响应,而其他客户端可能具有反应式、非阻塞架构。但与使用消息传递不同,客户端假设响应会及时到达。
下图展示了RPI的工作原理。客户端的业务逻辑调用由 RPI代理适配器类 实现的 代理接口。RPI代理 向服务发出请求。
请求由 RPI服务器适配器类 处理,该类通过接口调用服务的业务逻辑。然后,它将回复发送回 RPI代理,后者将结果返回给客户端的业务逻辑。
代理接口 通常封装了底层通信协议。有很多协议可供选择,我们重点关注最流行的协议REST和gRPC。
1.REST API
REST的一个关键概念是资源,它通常代表一个业务对象(如客户或产品)或一组业务对象。REST使用HTTP动词来操作资源,这些资源通过URL引用。例如,GET请求返回资源的表示,通常是XML文档或JSON对象,尽管也可以使用其他格式(如二进制)。POST请求创建一个新资源,PUT请求更新一个资源。
REST API的挑战:
在单个请求中获取多个资源REST资源通常关注业务对象,如客户和订单,这给一次请求获取多个相关对象带来了挑战。例如,获取订单及其关联的客户通常需要多次API调用。常见的解决方法是增强API,使客户端可以在一次调用中获取相关资源,例如使用带有“expand”查询参数的GET请求来指定相关资源。虽然在很多情况下有效,但这种方法可能复杂且耗时,这促使了像GraphQL这样的替代技术的兴起,以实现更简化的数据检索。
将操作映射到HTTP动词*REST API设计中的一个显著挑战是如何将业务对象上的特定操作分配给正确的HTTP动词。例如,更新订单可能涉及各种操作,如取消或修改*,并非所有更新都符合使用HTTP PUT方法的幂等性要求。常见的方法是为不同的更新操作创建子资源,例如使用POST取消(POST /orders/{orderId}/cancel)或修改订单(POST /orders/{orderId}/revise)。另一种方法是在URL查询参数中包含操作。然而,这些方法可能并不完全遵循REST原则。这种将操作映射到HTTP动词的困难促使了gRPC等替代技术的流行。
使用REST有很多优点:
简单且熟悉。
可以在浏览器中使用插件(例如Postman)或在命令行中使用curl测试HTTP API(假设使用JSON或其他文本格式)。
直接支持请求/响应风格的通信。
HTTP当然是防火墙友好的。•不需要中间代理,简化了系统架构。
使用REST也有一些缺点:
仅支持请求/响应风格的通信。
可用性降低。由于客户端和服务直接通信,没有中介缓冲消息,它们必须在整个交换期间都在运行。
客户端必须知道服务实例的位置(URL)。在现代应用中,这是一个不小的问题。客户端必须使用所谓的服务发现机制来定位服务实例。
在单个请求中获取多个资源具有挑战性。•将多个更新操作映射到HTTP动词有时很困难。
2.使用gRPC
REST API通常在使用有限的HTTP动词处理多个更新操作时遇到困难。gRPC通过使用二进制消息协议提供了一个替代方案,强调API优先的方法。它利用谷歌开发的Protocol Buffers(Protobuf),这是一种语言中立的序列化系统,允许开发人员使用基于Protocol Buffers的接口定义语言(IDL)定义API。此设置使得可以使用Protocol Buffer编译器自动生成各种编程语言(如Java、C#、NodeJS和GoLang)的客户端和服务器代码。gRPC API运行在HTTP/2之上,支持简单的请求/响应和流式RPC,服务器可以向客户端发送消息流或反之亦然。此技术支持创建具有强类型方法的明确服务接口,为处理微服务架构中的各种复杂通信模式提供了强大的框架。
gRPC有几个优点:
设计具有丰富更新操作的API非常简单。
具有高效、紧凑的IPC机制,特别是在交换大型消息时。
双向流支持RPI和消息传递风格的通信。
使客户端和服务在各种编程语言之间的互操作性成为可能。
gRPC也有一些缺点:
JavaScript客户端使用gRPC API比使用REST/JSON API工作量更大。
较旧的防火墙可能不支持HTTP/2。
gRPC是REST的一个有力替代方案,但像REST一样,它也是一种同步通信机制,因此也存在部分失败的问题。
使用异步消息传递模式通信
使用消息传递时,服务通过异步交换消息进行通信。基于消息传递的应用通常使用消息代理,其作为服务之间的中介。服务客户端通过发送消息向服务请求。如果服务实例预期要回复,它会通过发送单独的消息回复客户端。由于通信是异步的,客户端不会阻塞等待回复。相反,客户端假设回复不会立即收到。
1.消息传递概述
根据Gregor Hohpe和Bobby Woolf的《企业集成模式》一书:
消息通过消息通道交换。发送者(应用程序或服务)将消息写入通道,接收者(应用程序或服务)从通道读取消息。让我们先看一下消息,然后再看通道。
2.关于消息
消息由消息头和消息体组成。
消息头 是一组名称-值对,以及描述所发送数据的元数据。除了由消息发送者提供的名称-值对外,消息头还包含名称-值对,例如由发送者或消息传递基础设施生成的唯一消息ID,以及一个可选的返回地址,指定应将回复写入的消息通道。
消息体 是以文本或二进制格式发送的数据。
消息有几种不同类型:
文档 — 包含仅数据的通用消息。接收者决定如何解释它。命令的回复是文档消息的一个例子。
命令 — 包含指示接收者执行某些操作的数据。客户端发出的消息是命令的一个例子。
事件 — 包含描述发生的事件的数据。发布/订阅消息通常是事件的一个例子。
3.关于消息通道
消息通过消息通道 发送。消息通道是消息传递基础设施的关键组成部分。虽然消息是逻辑上的概念,但消息通道通常是由消息代理实例化的具体、物理概念。消息通道有两种类型:点对点通道和发布-订阅通道。
下图展示了它们是如何工作的:
点对点通道 将消息从一个发送者传递到一个接收者。消息代理确保每条消息恰好被一个接收者消费。这种类型的通道适用于发送命令和发布单一消费者事件。消息代理通常通过将消息放入队列实现这一点。
发布-订阅通道 将消息从一个发送者传递到多个接收者。消息代理确保每条消息被所有接收者消费。这种类型的通道适用于发布事件。消息代理通常通过将消息放入主题实现这一点。
4.消息传递的优缺点
使用消息传递有几个优点:
它是异步的,不需要客户端和服务在通信期间都运行。
它使您能够实现发布/订阅和发布/异步响应风格的通信。
它解耦了客户端和服务。客户端通过写入通道请求服务,服务通过从通道读取消息提供服务。客户端和服务不直接通信,因此无需相互了解位置。
客户端可以将请求写入负载均衡器或消息代理上的虚拟队列,实现服务实例的负载均衡。
消息代理会自动将消息发送到服务实例,因此服务实例崩溃时消息不会丢失。
使用消息传递有一些缺点:
复杂性增加。使用消息传递时,您必须编写代码来处理消息发送和接收。
调试复杂。消息传递引入了一种新形式的通信,您必须跟踪消息的状态和流动来调试系统。
遇到传递消息的基础设施开销。消息传递基础设施可能会引入开销,导致某些消息传递方案的性能下降。
使用消息传递时,调试和测试系统变得更复杂。
最后
微服务通信方法的选择取决于系统的具体需求和设计考量。同步方法(如REST和gRPC)适用于需要及时响应的场景,而异步消息传递则在解耦服务、提高系统可靠性和扩展性方面表现出色。理解这些方法的优缺点以及适用场景,是设计高效、可扩展和可靠的微服务架构的关键。希望本文能为您的微服务通信方法选择提供有价值的指导和参考。