事件、流程和长期运行的服务:工作流自动化的现代解决方案

发表于:2018-3-14 11:31

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:佚名    来源:InfoQ

#
流程
分享:
  本文要点
  1.领域事件非常适合分散的数据管理、生成可读模型或者解决切面问题。然而,你最好不要实现复杂的点到点的事件链。使用命令来协调其它服务可以更好地减少你的服务耦合。
  2.集中管理的ESB不合适微服务架构。智能端点和哑管道更可取。但是,不要因为害怕引入集中控制而不考虑协调服务:重要的业务功能都需要一个中枢。
  3.过去,BPM和工作流引擎是供应商驱动的,因此市面上有许多可怕的“零代码”工具,吓跑了开发人员。然而,现在也存在一些轻量级的易于使用的框架,其中许多还是开源的。
  4.不要投入时间来写自己的状态机,利用已有的工作流工具。这有助于你避免意外的复杂性。
  如果你一直关注最近围绕微服务架构模式的讨论,你也许就听过这个建议:“你需要创建一个事件驱动架构,来有效地解耦你的(微)服务”。
  这个想法受到领域驱动设计(Domain-Driven Design,DDD)社区的支持。DDD社区为此提供了利用领域事件的具体细节,并展示了他们是如何改变我们看待系统的方式。
  尽管我们拥护面向事件的架构,但是还需要问问自己,如果没有深入反思就使用事件驱动架构,会遇到哪些风险。为了解答这个问题,我们重新审视了以下三个基本假设:
  1.事件减少耦合
  2.需要避免集中控制
  3.工作流引擎是令人痛苦的(这也许看起来毫无关联,但是我们稍后会展示其关联关系)
  我们之前已经在muCon London 2017(幻灯片和录像)、O'Reilly Software Architecture London(幻灯片和录像)以及KanDDDinsky(幻灯片和录像)上发表了讲话,展示了我们重新审视的结果。
  我们的讲话基于现实生活中的各种不同项目的经历,但是使用了一个虚构的零售例子,这个例子受到(一次见面会上分享的)Zlando订单处理流程的启发。
  假设我们有四个绑定的上下文,产生4种专用的服务(微服务、独立的系统或者一些其它形式的服务):
  如何利用事件来解耦服务
  我们假设,如果一项产品有现货而且可以马上交货,结账服务应该给用户反馈。结账服务可以使用请求/响应的方式来向库存服务询问存货的数量,但是这样就将结账服务与库存服务(在可用性、响应时间等方面)耦合起来了。
  一个替代方案是,库存服务将存货数量的任何变更作为一个广播事件发布,让整个系统都知道这些变更。结账服务可以监听这些事件并内部保存当前的存货数量,然后对相关的问题进行本地应答。这个信息是一份拷贝,可能不是绝对一致的。然而,在分布式系统中,某种程度的最终一致性是可以接受的,也是一种必要的折衷。
  另外一个用例是在非核心功能中插入钩子,例如切面问题(cross-cutting concerns,在面向切面软件开发中,是指程序中影响其它相关业务的切面)。例如,你想要向客户发送关于订单的重要通知。通知服务可能是用一种完全自治的方式实现的,为客户存储通知偏好和联系人数据。它能在发生特定事件(例如收到付款、商品发货)时向客户发送邮件,而不需要在其他服务中做任何更改。这令事件驱动架构(event-driven architectures,EDA)非常灵活,并且增加新服务或者扩展现有的服务变得非常简单。
  点到点事件链的风险
  一旦团队开始使用事件驱动架构,他们通常会对事件着迷,因为事件提供了惊人的解耦能力,那么让我们为所有业务都使用事件驱动架构吧!当你实现点到点的事件流,就像通过点到点的事件链实现订单流程,问题就会开始浮现。假设一个相当小的流程,它的实现方式是,服务链中的下游服务总会知道它需要在什么时候做什么事情。
  这样是没有问题的。然而,问题是没有任何一个服务清楚全局情况,令这个流程难以理解,更重要的是,难以改变。另外,你要记住,现实情况下的流程不会是那么简单的,通常会涉及更多的服务。我们曾经见到过一些微服务的应用导致出现这种情况:一个包含许多服务的复杂系统做了某些事情,但是没有人真正地清楚具体做了什么以及怎么做的。
  现在,想想我们如何实现,在我们进行支付之前发送一个简单的数量变更请求来获取商品存量信息:
  你不得不为步骤序列中的一个简单的变更做出调整并重新部署一些服务。这通常是使用微服务的反面模式,因为这种架构模式的核心设计原则是努力争取低耦合和服务自治。因此,我们建议你在使用事件来实现对到点服务流之前要反复思考,尤其是可能会有相当大的复杂性的时候。
  命令,但不需要集中控制
  一种更靠谱的处理这种流程的解决方案是用一个专门的服务来实现它。这个服务可以作为一个协调者,向其它服务发送命令,例如,发起支付。这通常是一种更自然的方案,在这种情况下,如果支付服务通过订阅大量能够触发支付检索的业务事件来了解其所有消费者,我们通常不认为这是一种好的设计。以下的方案避免了这种耦合,可以认为是一种编排方案:“订单服务编排支付服务、库存服务和运输服务,来满足客户业务需求。”
  然而,当我们一提到编排,一些人会想到“神奇的”企业服务总线(Enterprise Service Buses,ESB)或者集中化的业务流程建模(Business Process Modelling,BPM)解决方案。在过去关于这些工具有许多不好的经历,因为这通常意味着,你因为要使用一些太复杂的专业工具而不得不放弃易测性和自动化部署。James Lewis和Martin Fowler建议更好地使用“智能端点和哑管道”,奠定了微服务架构的一些基础。
  然而,上面的图片没有建议智能端点。编排服务将订单服务作为一等公民来处理,并作为一个专门的服务来实现。像这样的服务可以用你喜欢的任何方式和你喜欢的任何技术栈来实现。这个流程现在非常简单,你有一个专门的地方可以在其中了解流程,并且可以通过只更改一个服务来更改整个流程。
  Sam Newman在他的《Building Microservices》一书中描述了这种订单编排服务的另外一种风险:随着时间的推移,这个服务会引入所有的业务逻辑而发展成一个“上帝服务”,而其它服务会退化成“贫血的“服务,甚至在更坏的情况下变成CRUD那样的“实体”服务。之所以会发生这样的事情,是因为我们有时更偏好命令而不是事件吗?或者是因为服务编排?并不是。让我们快速复习下Martin Fowler提出的“智能端点”概念。什么构成了一个智能端点?这大概与良好的API设计有关。对于支付服务,你可以设计一个高效的粗粒度的API,这个API可以按照检索支付命令执行,并且可以发送付款收到事件或者付款失败事件。付款处理的中间状态,例如客户信誉值或信用卡付款失败不会被暴露出来。在这种情况下,服务不会变得贫血,因为它是在其它环境下被编排的(或者更简单地说,“被使用”)。
  考虑服务潜在的长期运行的性质
  为了设计智能端点并向你的客户提供一个有价值的API,你必须认清,许多服务都存在长期运行的可能性,因为它们需要解决各种场景的业务难题。假设,在信用卡过期的情况下,我们给客户一个机会去更新这个信息(从GitHub获得的启发,GitHub允许你在付款失败需要关闭账户之前有2周的缓冲期)。如果支付服务完全不关心这些等待用户的细节,这将会把这种需求的责任推给它的使用者——订单服务。然而,将这种职责保留在支付服务中是更加简洁的做法,也更符合领域驱动设计中绑定上下文的理念。等待客户提供新的信用卡信息,意味着付款操作可以继续被恢复或者失败。因此,支付API变得更加清晰,服务也变得更容易使用。然而,目前在某些情况下,我们在得到一个业务响应之前可能需要等待2周时间。这就是我们所谓的“长期运行的”业务流。



上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。

21/212>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号