本文翻译自 Gero Vermaas 在 voxxed 的博客文章,原文地址:
https://www.voxxed.com/blog/2015/04/coupling-versus-autonomy-in-microservices/
微服务是一种有望解决以前架构风格各种问题的新架构风格。如同其他架构风格,微服务也有其自身的挑战。本文讨论的问题是如何解除微服务之间的耦合并保持微服务尽可能的自治,这里我们会讨论四种方案并最终选择一种。
对我来说,微服务是一种要对业务能力完全负责的自治服务,这里的完全负责是指对表现层、API、数据存储和业务逻辑完全负责。我觉得自治是微服务的关键,自治服务才会在修改时对其他部分造成尽可能小的影响;自治服务运营时也不会对其他服务的功能造成影响。
听起来不错,但是服务永远不会成为了一个与世隔绝的孤岛,服务几乎总是要依赖其他服务提供的数据。例如,网上商城都有一个购物车微服务,一些其他服务必须能向购物车添加商品,还必须能访问购物车内的商品并下单和配送。现在问题是,如何在保持服务尽可能自治的前提下实现对接。本文的目的是介绍几种在保持微服务之间最大可能的自治前提下对接微服务需要遵循的模式。
我将从两个维度构建这些模式:交互模式和信息交换模式。
交互模式:Request-Reply 还是 Publish-Subscribe
- Request-Reply(请求-应答)意味着一个服务处理信息的特定请求或者执行一些动作并返回一个应答。发起调用的服务需要知道去哪儿请求以及请求些什么?这种模式仍然可以被实现为异步执行,并且你还可以做一些抽象使服务调用方不需要知道被调用服务的物理地址,不能逃避的一点是服务必须明确的要求一个特定的信息和功能(或者执行动作)并等待应答。
- Publish-Subscribe(发布-订阅) 这种模式下的服务将自己注册为对特定的信息感兴趣,或者能够处理特定的请求,相关的信息和请求将被交付给它,并且它可以决定怎么处理这些信息和请求。本文假定有一些中间件能够处理交付或者发布消息给订阅这些消息服务。
信息交换:Events 还是 Queries/Commands
- Events(事件)是没有争议的事实,比如订单号 123 的订单已经创建,事件只陈述发生了什么事,不描述这样一个事件会导致什么事情发生。
- Queries/Commands(查询/命令)两者都传达了什么事情会发生,查询是对信息的特定请求,命令是要求一个服务执行一些动作的特定请求。
把这两种维度组成矩阵就得到微服务间对接的四种方式。那么每种方式的优势和劣势是什么?那种是保持服务最大化自治的最好方式?
以下的描述中我们将使用两个服务来阐述每种方式,订单服务负责管理订单,配送服务负责配送订单里的商品。这些服务是网上商店的一部分,网上商店还可能包含一些类似购物车、商品目录(搜索)等服务。
1. REQUEST-REPLY WITH EVENTS
在这种模式下,一个服务请求另一个导致事件发生的特定服务,这意味着这两种服务之间有很强的依赖。配送服务必须知道要连接那个服务来获得订单相关的事件,这也导致了运行时依赖,因为配送服务只有在订单服务可用的时候才能配送新订单。
既然配送服务只接收事件,它基于事件里的信息自己决定何时一个订单可以被配送,订单服务不需要知道配送服务的任何信息,它只是简单的提供事件表明当其他服务请求时订单进行怎样的处理,把响应事件的职责完全交给请求事件的服务。
2. REQUEST-REPLY WITH COMMANDS/QUERIES
在这种模式下,订单服务将请求配送服务来配送一个订单,这意味着强烈的依赖,因为订单服务明确的请求一个特定的服务来处理配送,现在订单服务必须决定何时一个订单准备好配送,它意识到配送服务的存在,甚至知道怎样与配送服务交互,在订单配送前需要考虑是否有其他因素关联到订单(比如客户信用卡状态),订单服务在请求配送服务来配送订单前也需要考虑这一点。现在业务处理被混到了架构里,因此架构不能被简单的修改。
这也是运行时依赖,因为订单服务必须确保配送请求成功交付给了配送服务。
3. PUBLISH-SUBSCRIBE WITH EVENTS
配送服务注册自己对订单相关的事件感兴趣,注册后,配送服务会收到订单的所有事件而不需要关心订单事件的来源,这是对订单事件来源的松散耦合,配送服务需要保留接收到事件的副本,这样就可以决定何时订单准备好配送。
订单服务需要对配送无关,如果多个服务提供包含配送服务需要的相关数据的订单相关事件,配送服务应该不可识别,如果一个提供订单事件的服务宕机,配送服务也应该不知道,只是收到的事件变少了,配送服务不会因此阻塞。
4. PUBLISH-SUBSCRIBE WITH COMMANDS/QUERIES
配送服务自己注册为能够配送货物的服务,接受所有想要配送货物的命令,配送服务不需要意识到配送命令的来源,同样订单服务业不知道那些服务将处理配送,在这个意义上说,他们是松散耦合的,不过,订单服务知道既然发送了配送命令,订单必须被配送的事实,这确实让耦合更强了。
结论
现在我们已经描述了四种模式,让我们回到原来的问题,那种方式更自治?
两种 Request-Reply 模式都意味着两个服务的运行时耦合和强耦合,两种 Command/Queries 模式意味着一个服务知道另一个服务应该做的事(我们的例子中订单服务知道配送服务负责配送),这也意味着强耦合,但是这一次在功能级别。留下了一个选项:Publish-Subscribe with Events,这种情况下,两种服务从运行时和功能的角度都没有意识到彼此的存在。对我来说这是实现服务之间最大的自主权的赢家。
下一个问题是 - 你应该总是使用 Publish-Subscribe with Events?答案是肯定的,但是我们需要考虑更多的因素,一直使用这种方式交互是有代价的,例如,数据被复制、事件丢失、事件驱动的架构增加更多基础设施的需求、额外的延迟。
在以后的文章中,我将深入到这些权衡和正确看待这个事情。现在,记住 Publish-Subscribe with Events 是实现服务自治的良好基础。