微服务之集成(四)

1. 寻找理想的集成技术

微服务之间通信的方式的选择非常多样化,但哪个是正确的呢?SOAP ? XML-RPC ? REST ?  Protocol Buffers?后面会逐一讨论。

首先,我们要考虑的是,我们到底希望从这些技术中得到什么。

1.1 避免破坏性修改

有时候,对某个服务做的一些修改会导致该服务的消费方也随之发生改变。但是,我们希望选用的技术可以尽量避免这种情况的发生。

1.2 保证API的技术无关性

保证微服务之间的通信方式的技术无关性是非常重要的。这就意味着,不应该选择哪种对微服务的具体实现技术有限制的集成方式。

1.3 使你的服务易于消费方使用

消费方应该很容易的使用我们的服务。理想情况下,消费方应该可以使用任何技术来实现,从另一方面来说,提供一个客户端库也可以简化消费方的使用。但是通常这种库与其他我们想要得到的东西不可兼得。例如,使用客户端库对于消费方来说很方便,但是会造成耦合的增加。

1.4 隐藏内部实现细节

我们不希望消费方与服务的内部实现细节绑定在一起,因为这会增加耦合。所以,所有倾向于暴露内部实现细节的技术都不应该被采用。

2.为用户创建接口

3.共享数据库

目前业界最常见的集成形式应该就是数据库集成了。使用这种方式时,如果其他服务想要从一个服务获取信息,可以直接访问数据库。如果想要修改,也可以直接在数据库中修改。

这种方式看起来非常简单,而且可能是最快的集成方式,这也是它这么流行的原因。

但是它有一些缺点。

如图,使用数据库集成来访问和修改数据信息

缺点一,首先,这使得外部系统能够查看内部实现细节,并与其绑定在一起。存储在数据库中的数据结构对所有人来说都是平等的,所有服务都可以完全访问数据库。如果我决定为了更好的表示数据或者增加可维护性而修改表结构的话,我的消费方就无法进行工作。

数据库是一个很大的共享API,但同时也非常不稳定。为了不影响其他服务,我必须非常小心的避免修改与其他服务相关的表结构。这种情况下,通常需要做大量的回归测试来保证功能的正确性。

缺点二,其次,消费方与特定的技术选择绑定在了一起。可能现在来看,使用关系型数据库做存储是合理的,所以消费方会使用一个合适的驱动(很有可能是与具体数据库相关的)来与之一起工作。说不定一段时间之后我们会意识到,使用非关系型数据库才是更好的选择。

如果消费方和客户服务非常紧密的绑定在了一起,那么能轻易替换这个数据库吗?答案肯定是不能。所以,正如前面所提过的,隐藏实现细节非常重要,因为它可以让我们的服务拥有一定的自治性,从而可以轻易的修改其内部实现。这关系到松耦合。

缺点三,最后,我们考虑一下行为。肯定会有一部分逻辑负责对客户进行修改。那么这个逻辑应该放在什么地方呢?如果消费方直接操作数据库,那么它们都需要对这些逻辑负责。对数据库进行操作的相似逻辑可能会出现在很多服务中。就是说,如果仓库,注册用户界面,呼叫中心都需要编辑客户的信息,那么当修复一个bug的时候,你需要修改三个不同的地方,并且对这些修改分别做部署。这关系到内聚性。

我们知道,关于好的微服务的核心原则就是高内聚和松耦合。但是,使用数据库集成使得这两者都很难实现。服务之间很容易通过数据库集成来共享数据,但是无法共享行为。内部表示暴露给了我们的消费方,而且很难做到无破坏性的修改,进而不可避免的导致不敢做任何修改,所以无论如何都要避免这种情况。

在后面的部分中,我们会介绍服务之间不同风格的集成方式,这些方式都可以保证服务的内部实现得以隐藏。

4. 同步和异步

在介绍具体的技术选择之前,我们先讨论一下服务之间如何协作的问题。服务之间的通信应该是同步还是异步呢?这个基础选择的不同,会引导我们使用不同的实现。

如果使用同步通信,发起一个远程服务调用后,调用方会阻塞自己并等待整个操作的完成。如果使用异步通信,调用方不需要等待操作完成就可以返回,甚至可能不需要关心这个操作是否完成。

异步通信对于运行时间比较长的任务来说比较有用,否则就需要在客户端和服务器之间开启一个长连接,而这时非常不实际的。

这两种不同的通信模式有着各自的协作风格,即请求/响应或者基于事件对于请求/响应来说,客户端发起一个请求,然后等待响应。这种模式能够与同步通信模式很好的匹配,但异步通信也可以使用这种模式。我们可以发起一个请求,然后注册一个回调,当服务端操作结束之后,会调用该回调。

对于使用基于事件的协作方式来说,情况会颠倒过来。客户端不是发起请求,而是发布一个事件,然后期待其他的协作者接收到该消息,并且知道该怎么做。基于事件的系统天生就是异步的。基于事件的协作方式耦合性很低。也就是说,客户端发布一个事件,但并不需要知道谁或者什么会对此做出响应,这也意味着,你可以在不影响客户端的请求下对该事件添加新的订阅者。

5. 编排与协同

在开始对越来越复杂的逻辑进行建模时,我们需要处理跨服务业务流程的问题,而使用微服务时,这个问题会来的更快。

当我们在MusicCorp中创建用户时,发生了什么:

(1) 在客户的积分账户中创建一条记录

(2) 通过邮政系统发送一个欢迎礼包

(3) 向客户发送欢迎电子邮件

当我们在考虑具体实现时,有两种架构风格可以采用。

第一种,使用编排。我们会依赖于某个中心大脑来指导并驱动整个流程。第二种,使用协同。我们仅仅会告知系统中各个部分各自的职责,而把具体怎么做的细节留给它们自己。

可以想象在编排的解决方案中,会让客户服务未做大脑中心。

在创建时,它会跟积分账户、电子邮件服务及邮政服务通过请求响应的方式进行通信。客户服务本身可以对当前进行到了哪一步进行跟踪。

编排方式的缺点是,客户服务作为中心控制点承担了太多的逻辑,它会成为网状结构中的中心枢纽及很多逻辑的起点。这种方法可能会导致少量的上帝服务,而与其打交道的那些服务通常都会沦为贫血的、基于CRUD的服务。

如果使用协同,可以仅仅从客户服务中使用异步的方式触发一个事件,该事件名可以叫做客户创建。电子邮件服务、邮政服务及积分账户可以简单的订阅这些事件并且做相应的处理。这种方法能显著的消除耦合。

缺点是,看不到图4-2中展示的那种很明显的业务流程视图。

这意味着,需要做