Camunda工作流微服务编排白皮书
微服务
由于系统复杂度的增加,微服务变得越来越受欢迎。微服务将系统分成多个独立的应用,并要求每个应用只专注于将一个业务做好。这虽然听起来像面向服务的开发(SOA),但是微服务在开发、部署和集成上和SOA有着根本的不同。SOA架构主要是为了代码的重用,而微服务从另一个方面来说,解决了人员组织和组件的独立可交换性(为替换而生)。也就是说,我们可以单独的替换某一服务的维护人力,资源的协调会更加的方便,减少资源浪费。微服务旨在解决软件开发扩展问题。软件结构总是反应构建软件的团队结构,这被称之为康威定律。在定义微服务的同时,也同样定义了团队有意义的业务边界或者说领域能力,这就是所谓的有界上下文。一个团队负责需要的整个技术栈。在这种情况下,团队可以自主实现手边的需求,并且减少与其他服务的耦合。尽量减少与其他团队协作的精力,以获得一个全面而灵活的组织。
因此,单个微服务应该独立工作,这种独立性涉及到服务的各个方面:
- 独立的生命周期:它必须能被不同的团队独立的部署。它同样能启动和停止微服务,或者独立于其他服务进行更改。微服务的部署和其他服务的合作并不应该是必须的。
- 稳定的接口:微服务为环境提供稳定的接口,在更新期间这些接口不能被破坏。如果需要再接口上进行不兼容的更改,则应该在接口文档中标注。
- 通信:如果一个微服务需要与其他微服务通信以完成其任务,则调用服务必须预期其通信伙伴无法立即响应其请求。这也是为什么会经常使用异步通信。其他模式也存在,比如隔仓模式,其中一个服务中断不会影响上游服务。
- 稳健性和容错性:即使整个系统的其他服务出现问题,单个微服务也必须继续运行。在很多情况下,让系统的单个用户看见错误比让整个系统以一种无法控制的方式崩溃要好。
- 本地数据存储:微服务经常保留他们需要的本地数据副本,以实现他们的服务,这基本上支持这里提到的其他特性。
- 独立的可伸缩性:并不是所有的微服务都需要相同的资源。所以必须能让每个服务独立的进行资源扩展。
上述六个要点旨在作为指导方针。并不是每一个微处理器都必须满足这些标准中的每一个,或者以同样的方式满足上述的挑战。
业务流程跨越多个微服务
当看到端到端业务流程时,它们通常跨越多个独立的微服务,因此为了实现预期的业务结果,它们必须相互协作。他们之间相辅相成。在该白皮书里,我们将使用一个简单的订单履行服务实例,该示例涉及支付、库存和运输微服务。
一个公司中的微服务可能采用多种通信方式。在这篇白皮书中,我们并不会谈论所有的通信方式的好坏,相反,我们只谈论目前最流行的两种通信方式:REST 和 基于消息或者基于事件的通信。这些通信面临的挑战略有不同,我们从当下最流行的趋势开始。
异步通信
这种方法的核心是微服务之间交换异步消息。虽然可以使用消息中间件,但是目前流行的是使用消息总线来完成。通常,你不会为一个通信通道使用专用队列和主题,而是使用一个“大管道”,将所有的事件全部推入其中。因此,微服只是发出事件,而不关心谁来接收他。也就是说,微服务会在不知道消息来源的情况下去消费事件,这就导致了高度的耦合。
我们希望将注意力集中在还行整个业务流程的结果事件链上。在这种架构中,不需要任何中央大脑来控制,将整个架构从中央组件中解放出来。就如同Maintin Fowler说的,微服务架构不需要编制引擎。
让我们来看下这个简单的订单履行流程示例,您可以想象图一中所示的微服务和事件链。
图一:简单订单执行系统的事件流
初看还不错,但是细想就发现了问题所在。
事件链的局限性
付款微服务监听下订单事件。也就意味着每次你有一个新的要求付款的案例你都会触发一次付款服务。这对团队自治的整体目标是非常不利的。假如你想构建和部署一个可出售下载构建的新服务。现在你必须与支付团队协调部署,因为他们必须开始监听你发出的事件,例如购买可下载构件。
这种情况可以引入事件命令转换(event command transformation)来解决。这种模式反映了您必须发出命令另一个服务做某事的的消息。在上面的事例中,支付服务应该监听retrieve payment command,这在解耦方面有所改进。
由于您可以使用链中的其他事件创建类似的示例,显然,为整体订单履行逻辑引
入单个业务是有意义的。在这个示例中,我们推荐一个订单微服务,它执行所有必要的转换,如图2所示。
图二:使用事件命令转换后的事件流
现在,该订单服务还可以决定整个业务流程的所有相关需求。所以,如果你想在取回支付之前先取回所有商品,有一个微服务可以让你轻松做到这一点。一个团队可以自己实现并重新部署他。团队之间的协调是不必要的。如果没有这个订单服务,您将不得不接触至少两个服务并协调一个联合发布,以便进行完全相同的更改:库存必须监听订单的下达,付款必须监听货物的提取。
有时会认为这种微服务架构在完成订单时会出现单点故障。我们不认为这是一个恰当的论点,因为每个微服务都应该是特定业务能力的单点。如果支付故障,没有支付可以被完成。如果订单故障,没有订单会被完成。在事件链中缺少任意一个重要的服务,任务都是不可能完成的。如果你无法取回付款或者无法取回商品,你还会运送你的订单吗?
订单团队有责任实现针对业务高可用性。就像异步通信,就算服务停止了,但是事件是不会丢失的。
你可以看到订单服务积极的一面:无论何时你需要停止订单(这比你想象的要普遍)你都知道应该在哪儿去做这件事。
同步远程调用
另一种方式是使用同步远程调用,典型的就是使用REST。比较下基础的调用链,此时使用订单服务的理由更加充足了,因为如果没有订单服务,你想了解库存接口,将被迫使用支付服务。
此外,实现订单服务将变得有些困难,因为你不得不考虑订单服务不可用的问题。这涉及到状态处理和重试机制。虽然这可以通过包括消息传递在内的不同方法来解决,但我们再次看到许多项目在现实生活中都在努力解决这个问题。这就是为什么我们经常在这个用例中使用状态机(稍后介绍)。
长时间运行的流程需要状态
这给我们带来了另外一个需求,迫使我们无论如何都需要处理订单。让我们假设付款是需要使用您的信用卡。当卡片无法完成付款时我们不用取消整个订单,而是发送信息给客户,让客服在7天时间内去更新卡片信息。如果他及时更新信用卡信息,支付仍然会重试成功。订单过程需要等待付款流程,可能需要等待相当长的一段时间,需要立即持久化每个订单的状态。
持久化可以通过多种方法来解决,典型的解决方案包括自定义实体、角色框架或者简单的状态处理框架。但是在现实项目中,只要你持久化了状态,就一定会有其他很多需求产生。那么,如果客户在七天之内没有响应怎么办?所以,你必须对及时处理和未及时处理都要追踪。你还需要对正在进行中的订单进行监控,必要时需要报告情况。
流程引擎是持久化状态的一个非常完美的方案。它还可以以一种复杂的方式处理事件流(或者更准确地说:如上所述的事件命令转换流)。流程可以像图三一样通过图像来展示,像Camunda这样的流程引擎,甚至可以不通过图像只是通过纯java代码来表示流程。
图三:使用BPMN图形化显示事件流
使用流程引擎的优点
当你使用流程引擎时,你会体验到如下几个好处:
- 明确的流程 :流程将变得非常明显,而不是隐藏在代码的某个地方,因此,修改起来非常方便。
- 状态处理 :工作流引擎会处理每个订单实例的持久化。
- 透明的状态 :流程状态可以很容易的在流程引擎中查询。监控可以直接在图形图中展示。
- 可见性 :图形模型可以用来讨论流程。可以是业务与IT之间进行讨论,也可以是开发人员之间进行讨论,还可以是开发人员和其他操作者之间讨论。
Martin Fowler 也意识到可见性和透明性的重要性,就像他最近写的:“事件通知是非常好的方案,因为它既简单又低耦合。但是如果一个逻辑流中穿插了各种各样的事件通知,这就会变得有问题了。问题就是在任何程序中它都将变得不透明。通常如果想要明确找出这个流程只能通过系统监控实现。这将使得这个流程变得难以调试和修改。更危险的是使用事件通知虽然很容易构建一个低耦合的系统,但是却没有意识到当系统变得越来越庞大时,流程变得越来越庞大,你将失去其扩展的能力。在未来你将遭遇更大的麻烦。”。
除了这些通用的优点,工作流引擎还有一些显著的特性,它们可以解决微服务体系结构中非常突出的问题。让我们快速了解其中一些。
超时处理
流程引擎可以追踪时间。如果一些消息没有及时到达,它会自动采取一些额外的措施,或者采用流程中的其他路径。如下图4所示:
图4:BPMN的超时处理
消息相关性和协调
有时在流程实例中,同属一个流程的多条消息必须被合并。在工作流引擎的支持下,这很容易做到,因为工作流引擎可以根据当前的持久状态和传入消息来决定接下来需要发生什么。BPMN中已经解决了诸如消息序列、消息同步、等待消息超时和消息互斥等通信模式,如图5所示:
图5:BPMN2.0中的各种通信方式
错误处理
每当发生错误时,您可以指定行为,例如,您可以采用另一条路径或暗示某些重试机制,特别是在执行同步调用时。如图6所示:
图6:BPMN中的错误处理
业务事务
BPMN了解补偿的概念。补偿用于当流程遇见问题时需要撤销之前执行的步骤。这使得在分布式系统中很容易实现所谓的Saga模式 1 。一个典型的例子是调用多个服务的旅行预订,如图7所示。Saga需要存储状态,并且可以从利用工作流引擎的特性中获益。你可以在网上找到完整的源代码 2 。
图7:BPMN中的业务事务和补偿
对工作流引擎的误解
在提出本文中推荐的工作流引擎时,您可能不愿意在微服务架构中这样做。通常情况下,这是由以下误解造成的:
- 违反有界上下文的流程:“当你对端到端流程建模时,它包含了由不同微服务拥有的部分,所以你不应该在它们的领域里瞎搅和”。
- 中央控制器:“工作流引擎是一个中心工具,它不仅是单点故障和可伸缩性的限制,而且还迫使团队使用某种技术或适应中央治理的变化,如升级引擎”。
- 对开发人员不利的重量级工具:“构建微服务的团队在他们的工具决策和操作他们选择的解决方案时应该是自主的。BPM工具是重量级的,需要几周的时间来安装,这是微服务团队永远无法管理的,开发者无论如何也不会选择它。”
- 代价昂贵(同样的理由,只是针对钱而言):“BPM构建适合对内核授权,而如果对每个运行服务的主机都进行内核授权,这种代价是我们无法承担的”。
- 不能在云端运行:“我们不能将不够灵活的BPM套件部署到我们(可能是内部环境)的云端环境。”
这些原因是基于过去在BPM方面所犯的错误或基于对工作流技术的过时观点而产生的误解。而现在,答案是:
- 将业务流程的所有权合理的分配给微服务
- 轻量化的引擎
- 云技术和云授权模型
- 深思熟虑的措辞
让我们更深层次的去了解Camunda是如何解决这些问题的。
分配端对端流程的所有权
微服务社区认为“上帝模式”是反模式。这种服务非常强大,可以完成所有的工作,只是委托给CRUD的服务进行数据存储。我们应该避免任何改变都需要去改变上帝服务。不存在完全的解耦。BPM实践者确实经常创建像上帝一样的整体式端对端流程模型。如图8的例子一样:
camunda.getRepositoryService().createDeployment()
.addModelInstance(Bpmn.createExecutableProcess("order")
.startEvent()