Chris Richardson 微服务系列翻译全7篇连接:html
原文连接:Building Microservices: Inter-Process Communication in a Microservices Architecturenginx
在单体应用中,模块间使用编程语言级别的方法或函数彼此调用。而基于微服务架构的本质是是运行在多台机器上的分布式应用,每一个服务都是一个进程。以下图所示,微服务之间必须使用进程间通讯(IPC)的机制实现交互:web
稍后咱们将讨论 IPC 技术,先看下设计相关的问题。编程
当为某个服务选择 IPC 机制时,首先要考虑服务间如何交互。client 和 server 端有不少交互的方式,能够按两个维度分类:浏览器
第一个维度是一对一仍是一对多:缓存
第二个维度是交互是同步仍是异步:安全
下面表格展现了两种方式的不一样:网络
一对一 | 一对多 | |
同步 | 请求/响应 | |
异步异步 | 通知 | 发布/订阅 |
请求/异步响应 | 发布/异步响应 |
下面有几种一对一的交互模式:架构
下面有几种一对多的交互模式:app
每一个服务都是以上几种模式的组合,对某些服务来讲,一个 IPC 机制就能知足了,另一些服务可能须要多个 IPC 机制的组合。下图展现了用户叫车应用中,用户请求行程时,服务是如何交互的:
上图服务使用了通知、请求/响应、发布/订阅的方式。例如:乘客在移动端向『行程管理服务』发送接送需求的通知;『行程管理服务』使用 请求/响应 模式 调用『乘客服务』来验证乘客帐号是否有效;而后『行程管理服务』建立行程并使用 发布/订阅 模式来通知其余服务(定位可用司机的『调度服务』等)。
咱们讨论了交互风格,下面看下如何定义 API。
API 是服务端和客户端的契约。不管选择选择哪一种 IPC 机制,都须要使用接口定义语言(IDL)来定义 服务的API。开发服务前,先定义服务接口,并与 client端开发者一块儿 review,后续再对 API 进行迭代。这样设计能帮助你构建更符合客户需求的服务。
文章后半段你会发现,API 的定义依赖选择的 IPC 机制。若是使用消息机制,API 则由消息频道和消息类型组成。若是使用 HTTP, API 则是由 URL 和 request/response 格式组成。后面咱们将讨论 IDL 的细节。
服务的 API 不可避免的随着时间进化。单体应用中,能够直接修改 API 并更新全部的调用者。但在微服务应用中,即时 API 的全部调用者都在一个应用中,去更新其余服务也是很困难的,一般不能强制让全部 client 升级来保持和 server 端一致。此外,你可能还会增长部署新的服务版本,与老版本同时运行。了解处理这些问题的策略是很是重要的。
如何根据更改的大小来处理 API 呢?有的变化很小,一般能够与旧版本作到向后兼容,例如:为请求或响应添加了一个属性。对此,设计服务时考虑鲁棒性是颇有必要的:使用旧版本 API 的 client 在新版本的 API 下能正常工做;server 为缺失的属性提供默认值;client 忽略响应中额外添加的属性。
有时候 API 不得不作一些大的、不兼容的变更,此时又不能强制让全部 client 当即升级,所以,旧版本 API 还须要运行一段时间。若是使用的是基于 HTTP 的 IPC,能够在 URL 里嵌入服务版本,每一个服务实例能够同时处理多个版本。另外一种方式也能够选择为每一个版本单独部署。
分布式系统广泛存在局部失败的问题,因为 client 和 server 是运行在独立的进程中,server 可能由于挂了或维护而暂时不可用,不能及时响应 client 的请求,或者由于过载而致使响应很慢。
以上篇文章提到的商品详情页场景为例,假设推荐服务没有响应,client 可能无限期的等待服务响应而致使阻塞,这不只致使用户体验很糟糕,并且会占用线程等宝贵资源,就像下图所示,运行时线程耗尽,而没法响应任何请求:
为解决此类问题,设计时须要考虑局部故障的问题:
Netfilix 提供了较好的解决方案:
Netflix Hystrix 是一个实现相关模式的开源库。若是使用 JVM,那么推荐使用 Hystrix。若是使用的非 JVM 环境,也可使用相似的库。
如今有不一样的 IPC 技术可选择:基于 请求/响应 的同步通讯模式,例如基于 HTTP 的 Rest 或 Thrift;也能够选择异步的、基于消息的通讯模式,例如AMQP、STOMP。这些通讯有着不一样的消息格式,服务能够选择基于文本、方便阅读的 JSON 或 XML格式,或者效率更高的二进制格式(例如 Avro、Protocol Buffers)。
使用消息模式时,进程间经过异步消息的方式来通讯,client 发送消息来请求 server,若是指望 server 响应,则 server 会发送另一条消息给 client。因为通讯是异步的,client 不会由于等待响应而阻塞,同时 client 编程时也以服务不会当即响应来处理。
消息由消息头(元数据和发送者)和消息体组成,消息经过频道进行交换,任意数量的生产者均可以往频道里发送消息,一样,任意数量的消费者均可以从频道里消费消息。频道分为点对点、订阅/发布两种:
下图展现了打车软件中如何使用 发布/订阅 模式:
行程管理服务向『订阅-发布』频道写入『建立行程』的消息,通知调度服务有新的行程请求。调度服务查找空闲的司机,并经过『发布-订阅』频道写入『推荐司机』的消息,通知其余服务。
有多种消息系统供咱们选择,固然咱们尽量选择支持多种编程语言的。一些消息系统支持 AMQP和 STOMP 这样的标准协议,有的则支持专有的协议。开源的消息系统例如:RabbitMQ、Apacha Kafka、Apache ActiveMQ 和 NSQ。统一来看,他们都支持一些消息和频道,都致力于高可用、高性能和高可扩展性。
使用消息系统有不少优势:
固然,消息系统也有缺点:
使用同步、请求/响应的 IPC 时,client 请求 server 时有可能因为等待 server 响应而被阻塞。另一些client 会使用异步、事件驱动的代码,例如封装好的 Future 或者 Rx Observable。这个模式最多见的协议是 Rest 和Thrift。
当前流行开发 RESTful 风格的 API。 Rest 是基于 HTTP 的 IPC 机制,其核心概念是使用 URL 来表示资源(用户或产品的一组业务对象)。例如:GET 请求会返回一个资源的信息,多是 XML 文档 或 JSON 对象格式;POST 请求会建立新的资源;PUT 请求会更新资源。REST 之父 Roy Fielding 曾经说过:
REST provides a set of architectural constraints that, when applied as a whole, emphasizes scalability of component interactions, generality of interfaces, independent deployment of components, and intermediary components to reduce interaction latency, enforce security, and encapsulate legacy systems.
Rest 提供了一些列架构系统参数做为总体使用,强调组件交互的扩展性、接口的通用性、组件的独立部署、减小交互延迟的中间件,他强化安全,也能封装遗留系统。
下面展现打车软件使用 Rest 的场景:
Leonard Richardson 为 REST 定义了一个成熟度模型,分为以下四个层次:
基于 HTTP 协议的优势:
HTTP 不足之处:
Apache Thrift 是 REST 的一个有趣的替代品,实现了跨语言的客户端和服务端RPC通讯的框架,Thrift 提供了 C 语言风格的接口定义语言来定义 API,能够经过编译生成客户端Stub 和 服务端的骨架,能够生成多种语言的代码(包括 C++、Java、Python、PHP、Ruby、Erlang、Node.js)。
Thrift 接口一般包含一个或多个服务,服务定义与 Java 接口相似,是一组强类型方法的集合。Thrift 能返回值,也能够定义为单向通讯。若是须要返回值就须要实现 请求/响应风格的交互,客户端等待响应时能够抛出异常;单向通讯就是通知模式,服务端不须要返回响应。
Thrift 支持 JSON、二进制、压缩二进制等不一样的消息格式。二进制解码比 JSON 更快,更为高效;压缩二进制比 JSON 空间利用率更高; JSON 则更易读。Thrift 也支持不一样的通讯协议:TCP 或 HTTP,TCP 比 HTTP 更加高效,而 HTTP 对防火墙、人及浏览器更加友好。
选择一种支持多语言的消息格式很是重要,哪怕你只用一种语言实现微服务,谁又能保证之后不会使用新的语言呢?
目前有文本和二进制两种格式。文本格式包括 JSON 和 XML。这种格式优势不只可读,并且是自描述的。JSON中,对象的属性是键值对的集合;XML中,属性表示为命名的元素和值。消费者能选择感兴趣的值而忽略其余部分,对格式的修改也能容易的向后兼容。
XML文档的结构是 XML Schema 定义的,随着时间的发展,开发者意识到 JSON 也须要一个相似的机制,方法一是使用 JSON Schema,要么独立使用,要么做为 Swagger 这类 IDL的一部分使用。
文本格式的一大缺点是消息会变的冗长,尤为是 XML:由于消息是自描述的,每条消息除了值以外还包括属性的名称。另外一大缺点是解析文本的开销略大,此时能够考虑二进制格式。
二进制格式也不少,若是使用 Thrift,那么能够用二进制Thrift;若是使用其余消息格式,经常使用的还包括 Protocol Buffers 和 Apache Avro,二者都提供了 IDL 来定义消息结构。差别之处在于 Protocol Buffers 使用标记字段,而 Avro 消费者须要了解 Schema 来解析消息,使用 Protocol Buffers 时,API进化比 Avro 更容易。Martin Kleppmann 的 博客文章 对Thrift、Protocol Buffers 和 Avor 进行了详细的比较。
微服务须要使用进程间消息通讯机制来交互,设计服务的通讯模式时,须要考虑一下几个问题:服务如何交互、如何定义 API、如何升级 API,如何处理局部故障。微服务架构有两种 IPC 机制可用:异步消息机制和同步请求/响应机制。下篇文章中,咱们会讨论微服务架构中的服务发现问题。