《微服务架构设计模式》笔记3—微服务架构中的进程间通信
三、微服务架构中的进程间通信
1、本章导读
- 通信模式的具体应用:
- 远程过程调用、断路器、客户端发现、自注册
- 服务端发现、第三方注册、异步消息、事务性发件箱、事务日志拖尾、轮询发布者
- 进程间通信在微服务架构中的重要性
- 定义和演化API
- 如何在各种进程间通信技术之间进行权衡
- 使用异步消息对服务的好处
- 把消息作为数据库事务的一部分可靠发送
2、概述
- 服务可以使用基于同步请求/响应的通信机制。比如:REST或gRPC。
- 也可以使用异步的基于消息的通信机制。比如:AMQP或STOMP。
- 消息格式也不尽相同,服务可以使用具备可读性的格式:JSON或XML。
- 也可以使用更加高效的、基于二进制的Avro或Protocol Buffers格式。
- 还有服务的交互方式、微服务架构中的API、消息格式。
1、交互方式
- 考虑交互方式将有助于你专注于需求。并避免陷入特定进程间通信技术的细节。并且交互方式的选择会影响应用程序的可用性。
- 有多种客户端与服务交互方式,可以分为两个维度:
- 第一个维度是请求处理对象。
- 一对一:每个客户端请求由一个服务实例来处理。
- 一对多:每个客户端请求由多个服务实例来处理。
- 第二个维度关注的是同步、异步模式:
- 同步模式:客户端请求需要服务端实时响应,客户端等待响应时可能导致堵塞。
- 异步模式:客户端请求不会阻塞进程,服务端响应可以是非实时的。
- 第一个维度是请求处理对象。
- 一对一交互方式有以下几种类型:
- 请求/响应:一个客户端向服务端发起请求,等待响应;客户端期望服务端很快就会发送响应。在一个基于线程的应用中,等待过程可能造成线程阻塞。这样的方式会导致服务的紧耦合。
- 异步请求/响应:客户端发送请求到服务端,服务端异步响应请求。客户端在等待响应时不会阻塞线程,因为服务端的响应不会马上就返回。
- 单向通知:客户端的请求发送到服务端,但是并不期望服务端做出任何响应。
- 一对多的交互方式有以下几种类型:
- 发布/订阅方式:客户端发布通知消息,被零个或者多个感兴趣的服务订阅。
- 发布/异步响应方式:客户端发布请求,然后等待从感兴趣的服务发回的响应。
2、在微服务架构中定义API
- 一个设计良好的接口会在暴露有用功能同时隐藏实现的细节。实现的细节可以被修改,接口保持不变。
- 服务的API由客户端结构可以调用的方法和服务发布的事件组成。
- 方法具备名称、参数和返回类型。
- 事件具有一个类型和一组字段。
- 如何定义API取决于你使用的进程间通信机制。例如:
- 使用消息机制:API由消息通道、消息类型和消息格式组成。
- 使用HTTP:API由URL、HTTP动词以及请求和响应格式组成。
3、API的演化
- 服务的API可能会随着应用功能的增减而发生变化,需要为API变更制定一些措施:
- 语义化版本控制
- 语义化版本控制规范为API版本控制提供了有用的指导。
- 它是一组规则。用于指定如何使用版本号,并且以正确的方式递增版本号。
- 语义化版本控制规范(Semvers)要求版本号由三部分组成:MAJOR.MINOR.PATCH。必须按如下方式递增版本号:
- MAJOR:当你对API进行不兼容的更改时。
- MINOR:当你对API进行向后兼容的增强时。
- PATCH:当你进行向后兼容的错误修复时。
- 有几个地方也可以在API中使用版本号:
- REST API:可以是通过主版本作为URL路径的第一个元素。
- 消息机制:在其发布消息中包含版本号。
- 处理次要并且向后兼容的改变:
- 可分为三种类型
- 添加可选属性
- 向响应添加属性
- 添加新操作
- 直接开发就行
- 可分为三种类型
- 进行主要并且不想后兼容的改变:
- HTTP进程通信机制:
- 在URL中嵌入主要的版本号,如:/v1/…为前缀。
- HTTP内容协商机制,并在MIME类型中包含版本号。如:
- HTTP进程通信机制:
客户端使用如下格式针对1.x版的服务API发起Order相关请求:
GET /orders/xyz HTTP/1.1
Accept: application/vnd.example/resource+json; version=1
...
此请求告诉了order service 客户端需要以版本1.x做出响应。
- 消息的格式:
- 进程间通信本质是交换消息。消息通常包括数据,因此一个重要的设计决策就是这些数据的格式。
- 消息格式的选择会对进程间通信的效率、API的可用性和可演化性产生影响。
- 消息的格式可以分为两大类:文本和二进制。
- 基于文本的消息格式:类似JSON和XML这类消息格式。
- 好处在于,他们的可读性很高,同时也是自描述的。这样的格式允许消息的接收方只挑选他们感兴趣的值,而忽略掉其他。因此,对消息结构的修改可以做到很好的向后兼容性。
- 弊端:消息往往过度冗长,解析文本引入额外开销。在对效率和性能敏感的场景下,可能需要考虑二进制格式消息。
- 二进制消息格式:Protocol Buffers和Avro。
- 这两种格式都提供了一个强类型定义的IDL(接口描述文件),用于定义消息格式。编译器会自动根据这些格式生成序列号和反序列化代码。
- 这两种二进制格式的区别在于,Protocol Buffers使用tagged fields(带标记的字段),而Avro的消费者在解析消息之前需要知道它的格式。
3、基于同步远程过程调用模式的通信
-
当使用基于远程过程调用(RPI)的进程间通信机制时,客户端向服务发送请求,服务处理该请求并发回响应。
-
下图显示RPI的工作原理:
-
代理接口通常封装底层通信协议。有许多协议可供选择。
-
主要介绍REST和gRPC。
1、使用REST
-
REST是一种(总是)使用HTTP协议的进程间通信机制。
-
REST中的一个关键概念是资源,他通常表示单个业务对象,例如客户或产品,或业务对象的集合。
-
RESR使用HTTP动词来操作资源,使用URL引用这些资源。
- 例如,GET请求返回资源的表示形式,该资源通常采用XML或JSON对象的形式。POST请求创建新资源,PUT请求更新资源。
-
REST成熟度模型,具体包含四个层次:
- Level 0:
- 0层级服务的客户端只是向服务端点发起HTTP POST请求,进行服务调用。
- 每个请求都指明了需要执行的操作、这个操作针对的目标(如,业务对象)和必要的参数。
- Level 1:
- 1层级的服务引用了资源的概念,要执行对资源的操作,客户端需要发出指定要执行的操作和包含任何参数的POST请求。
- Level 2:
- 2层级的服务使用HTTP动词来执行操作,比如get表示获取、post表示创建、put表示更新。
- 请求查询参数和主体制定操作的参数。
- 这让服务能够借助web基础设施服务,例如通过CDN来缓存GET请求。
- Level 3:
- 3层级的服务基于HAREOAS(Hypertext As The Engine Of Application State)原则设计。
- 基本思想是在由get请求返回的资源信息中包含链接,这些链接能够执行该资源允许的操作。
- 例如,客户端通过订单资源中包含的链接取消某一订单,或者发送get请求获取该订单等等。
- HATEOAS的有点包括无需再客户端代码中写入硬链接的URL。此外,由于资源信息中包含可允许操作的链接,客户端无需猜测在资源的当前状态下执行何种操作。
- Level 0:
-
定义REST API:
- 设计REST API的两个常见问题:
- 在一个请求中获取多个资源
- 把操作映射为HTTP动词时的混用挑战,如,put应该用作更新,但还可以用其他方法来完成该操作。
- REST好处:
- 简单,好用,
- 可以使用浏览器扩展(postman插件)或curl之类的命令行来测试HTTP API。
- 直接支持请求/响应方式的通信。
- HTTP对防火墙友好。
- 不需要中间代理,简化系统架构。
- REST弊端:
- 只支持请求/响应方式的通信
- 可能导致可用性降低。由于客户端和服务直接通信而没有代理来缓冲消息,因此他们必须在REST API调用期间都保持在线。
- 客户端必须知道服务实例的位置(URL),客户端必须使用所谓的服务发现机制来定位服务实例。
- 在单个请求中获取多个资源具有挑战性。
- 有时很难将多个更新操作映射到HTTP动词。
- 设计REST API的两个常见问题:
2、使用gRPC
- gRPC是REST的一种替代方案。解决HTTP有限数量的动词支持多个更新操作的API不是很容易问题。
- 这是一个用于编写跨语言客户端和服务端的框架。gRPC是一种基于二进制消息的协议。
- gRPC API由一个或多个服务和请求/响应消息定义组成。服务定义类似于java接口