设为首页 加入收藏

TOP

微服务从设计到部署(三)进程间通信(一)
2017-10-10 12:34:37 】 浏览:4487
Tags:服务 从设计 部署 进程 通信

链接https://github.com/oopsguy/microservices-from-design-to-deployment-chinese
译者Oopsguy

本书的第三章主要是关于使用微服务架构构建应用程序。第一章介绍了微服务架构模式,将其与单体架构模式进行对比,并讨论了使用微服务的优点和缺点。第二章描述了应用程序客户端通过扮演中间人角色的 API 网关与微服务器进行通信。在本章中,我们来了解一下系统中的服务是如何相互通信的。第四章将详细探讨服务发现方面的内容。

3.1、简介

在单体应用程序中,组件可通过语言级方法或者函数相互调用。相比之下,基于微服务的应用程序是一个运行在多台机器上的分布式系统。通常,每个服务实例是一个进程。

因此,如图 3-1 所示,服务必须使用进程间通信(IPC)机制进行交互。

稍后我们将了解到多种 IPC 技术,但在此之前,我们先来探讨一下涉及到的各种设计问题。

使用进程间通信交互的微服务

3.2、交互方式

当为服务选择一种 IPC 机制时,首先需要考虑服务如何交互。有很多种客户端-服务交互方式。它们可以分为两个类。第一类是一对一交互与一对多交互:

  • 一对一 - 每个客户端请求都由一个服务实例处理。
  • 一对多 - 每个请求由多个服务实例处理。

第二类是同步交互与异步交互:

  • 同步 - 客户端要求服务及时响应,在等待过程中可能会发生阻塞。
  • 异步 - 客户端在等待响应时不会发生阻塞,但响应(如果有)不一定立即返回。

下表展示了各种交互方式。

- 一对一 一对多
同步 请求/响应 -
异步 通知 发布/订阅
异步 请求/异步响应 发布/异步响应

表 3-1、进程间通信方式

一对一交互分为以下列举的类型,包括同步(请求/响应)和异步(通知与请求/异步响应):

  • 请求/响应 - 客户端向服务发出请求并等待响应。客户端要求响应及时到达。在基于线程的应用程序中,发出请求的线程可能在等待时发生阻塞。
  • 通知(又名单向请求) - 客户端向服务发送请求,但不要求响应。
  • 请求/异步响应 - 客户端向服务发送请求,服务异步响应。客户端在等待时不发生阻止,适用于假设响应可能不会立即到达的场景。

一对多交互可分为以下列举的类型,它们都是异步的:

  • 发布/订阅 - 客户端发布通知消息,由零个或多个感兴趣的服务消费。
  • 发布/异步响应 - 客户端发布请求消息,然后等待一定时间来接收消费者的响应。

通常,每个服务都组合着使用这些交互方式。对于一些服务,单一的 IPC 机制就足够了,但其他服务可能需要组合多个 IPC 机制。

图 3-2 显示了当用户请求打车时,打车应用中的服务可能会发生交互。

使用了多种 IPC 机制的服务交互

服务使用了通知、请求/响应和发布/订阅组合。例如,乘客的智能手机向 Trip Management 微服务发送一条通知以请求一辆车。Trip Management 服务通过使用请求/响应来调用 Passenger Management 服务以验证乘客的帐户是否可用。之后,Trip Management 服务创建路线,并使用发布/订阅通知其他服务,包括用于定位可用司机的 Dispatcher。

现在我们来看一下交互方式,我们先来看看如何定义 API。

3.3、定义 API

服务 API 是服务与客户端之间的契约。无论您选择何种 IPC 机制,使用接口定义语言(interface definition language,IDL)严格定义服务 API 都是非常有必要的。有论据证明使用 API 优先(API?first)法定义服务更加合适。在对您需要实现的服务的 API 定义进行迭代之后,您可以通过编写接口定义并与客户端开发人员进行审阅来开始开发服务。这样设计可以增加您构建出符合客户端需求的服务的机率。

正如您将会在后面看到,定义 API 的方式取决于您使用何种 IPC 机制。如果您正在使用消息传递,则 API 由消息通道和消息类型组成。如果您使用的是 HTTP,则 API 由 URL、请求和响应格式组成。稍后我们将详细地介绍关于 IDL 方面的内容。

3.4、演化 API

服务 API 总是随着时间而变化。在单体应用程序中,更改 API 和更新所有调用者通常是一件直截了当的事。但在基于微服务的应用程序中,即使您的 API 的所有消费者都是同一应用程序中的其他服务,要想完成这些工作也是非常困难的。通常,您无法强制所有客户端与服务升级的节奏一致。此外,您可能需要逐步部署新版本服务,以便新旧版本的服务同时运行。制定这些问题的处理策略还是很重要的。

处理 API 变更的方式取决于变更的程度。某些更改是次要或需要向后兼容以前的版本。例如,您可能会向请求或响应添加属性。这时设计客户与服务时遵守鲁棒性原则就显得很有意义了。使用较旧 API 的客户端应继续使用新版本的服务。该服务为缺少的请求属性提供默认值,并且客户端忽略任何多余的响应属性。使用 IPC 机制和消息格式非常重要,可以让您轻松地演化 API。

但有时候,您必须对 API 作出大量不兼容的更改。由于您无法强制客户端立即升级,服务也必须支持较旧版本的 API 一段时间。如果您使用了基于 HTTP 的机制(如 REST),则一种方法是将版本号嵌入 URL 中。每个服务实例可能同时处理多个版本。或者,您可以部署多个不同的实例,每个实例用于处理特定版本。

3.5、处理局部故障

正如第二章中关于 API 网关所述,在分布式系统中存在局部故障风险。由于客户端进程与服务进程是分开的,服务可能无法及时响应客户端的请求。由于故障或者维护,服务可能需要关闭。也有可能因服务过载,造成响应速度变得极慢。

例如,请回想第二章中的产品详细信息场景。我们假设 Recommendation Service 没有响应。客户端天真般的实现可能会无限期地阻塞以等待响应。这不仅会导致用户体验糟糕,而且在许多应用程序中,它将消耗如线程之类等宝贵资源。以致最终,在运行时将线程用完,造成无法响应,如图 3-3 所示。

因无响应服务引起的线程阻塞

为了防止这个问题出现,您必须设计您的服务以处理局部故障。以下是一个由 Netflix 给出的好方法。处理局部故障的策略包括:

  • 网络超时 - 在等待响应时,不要无限期地阻塞,始终使用超时方案。使用超时方案确保资源不被无限地消耗。
  • 限制未完成的请求数量 - 对客户端拥有特定服务的未完成请求的数量设置上限。如果达到了上限,发出的额外请求可能是毫无意义的,因此这些尝试需要立即失败。
  • 断路器模式 - 追踪成功和失败请求的数量。如果错误率超过配置阈值,则断开断路器,以便后续的尝试能立即失败。如果出现大量请求失败,则表明服务不可用,发送请求将是无意义的。发生超时后,客户端应重新尝试,如果成功,则关闭断路器。
  • 提供回退 - 请求失败时执行回退逻辑。例如,返回缓存数据或者默认值,如一组空白的推荐数据。

Netflix Hystrix 是一个实现上述和其他模式的开源库。如果您正在使用 JVM,那么您一定要考虑使用 Hystrix。如果您在非 JVM 环境中运行,则应使用相等作用的库。

3.6、IPC 技术

有多种 IPC 技术可供选择。服务可以使用基于同步请求/响应的通信机制,比如基于 HTTP 的 REST 或 Thrift。或者,可以使用异步、基于消息的通信机制,如 AMQP 或 STOMP。

还有各种不同的消息格式。服务可以使用人类可读的、基于文本的格式,如 JSON 或 XML。或者,可以使用如 Avro 或 Protocol Buffer

首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Linux系统运维与架构设计 下一篇2017-5-3/高性能网站架构

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目