美文网首页
详细解读微服务的两种模式

详细解读微服务的两种模式

作者: heishaovvv | 来源:发表于2019-02-19 16:46 被阅读42次

    微服务是一种架构范例。以黑少微服务商店(www.httpshop.com )为例,在这种架构中,多个小型独立组件协同工作,从而构成一个系统。尽管它的操作复杂性较高,但这种范式已经被迅速采用。这是因为它有助于将复杂的系统分解为可管理的服务。这些服务更关注微观层面的问题,包括单一责任,关注点分离,模块化等。

    所有这一切都要遵守各系统间相互制约的设计约束。

    服务间通信和执行流程是分布式系统的基础,它可以是同步的也可以是异步的。这两种方法都有其利弊,本问详细剖析各种选择并分析其影响。

    维度

    每种实现方式都可以从多种维度进行权衡。从这些维度对权衡点和系统约束进行评估,有助于我们分析其可行性和适用性。每种实现方式都有折衷。与此同时,考虑中的系统可能有各种各样的维度。根据这些限制评估取舍可以帮助我们推断方法和适用性。系统的各个维度都会影响系统的执行流程和通信方式,下面我们来看看其中的一些维度。

    消费者

    系统的消费者可以是外部程序,网页/手机接口,物联网设备等。消费者应用程序通常会同步处理服务器,并期望接口支持。用消费者的统一接口掩盖分布式系统的复杂性也是可取的,必要条件是,我们的通信方式选择能带来便利。

    工作流管理

    业务工作流贯穿多个服务,因此,业务工作流的管理至关重要。它可以是隐含的,并且可以发生在每个服务上,因此仍然分布在各个服务中。换言之,它可以是明确的。协调器服务可以承担协调业务流程的责任。编排是两者的结合。工作流规范规定了执行顺序和对服务的实际调用。协调器与参与服务遵循的通信范式紧密相关。通信风格和执行流程推动了协调器的实现。

    第三个选项是基于事件编排的设计,通过一个将所有服务绑定的事件总线来代替编排器。

    所有这些都是系统中的工作流管理机制,我们将在本系列的后续篇章详细介绍工作流程管理。在我们评估和选择通信方式时,我们会考虑当前上下文中与其相关的约束条件。

    读/写频率偏差

    系统的读/写频率可能是其体系结构中的关键因素。一个读取繁重的系统需要大部分操作同步完成。一个很好的例子就是大规模运营的天气预报服务的公共API。或者,写入繁重的系统可从异步执行中受益。一个例子就是一个平台,无数物联网设备都在不断报告数据。当然,它们之间也有系统。由于读写偏斜,有时候倾向于一种风格是有用的。在其他时候,将读取和写入拆分为单独的组件可能是有意义的。

    在我们审视各种方法时,我们需要保持这些约束。这些维度将帮助我们提取每种实现方式的适用性。

    同步

    同步通信是调用方等待响应可用的通信方式,是一个突出并得到广泛使用的方法。简单且直观的概念使其适用于大多数情况。

    同步通信与HTTP协议密切相关。但是,其他协议仍然是实现同步通信的合理方式。另一个很好的例子是RPC调用,每个组件都公开一个其他服务所调用的同步接口。

    入口点附近的拦截器拦截业务流程请求,然后将请求推送到下游服务,所有的后续调用本质上都是同步的。这些调用可以是并行或顺序的,直到处理完成。系统内的调用处理可能会有不同的方式。一个编排者可以显式调度所有的调用,或者调用可以跨组件有机地渗透。下面我们看看几个可能的机制。

    变化

    在同步系统中,架构可以采用几种方法,以下简要说明各种方法的可行性。

    去中心化和同步

    去中心化的同步通信方式在入口点拦截流量,拦截器将请求转发到下一步并等待响应,该循环一直下行,直到所有服务都已完成执行,每个服务可以顺序或并行执行一个或多个下游服务。

    虽然实现很简单,但流程细节分布在整个系统中,从而导致组件之间的耦合。

    这些调用在整个系统中保持同步。因此,这种通信方式可以满足同步消费者的期望。由于分布式工作流的性质,该方式缺少灵活性,并不适用于经常更改的复杂工作流。由于对系统的每个请求都可能阻塞服务,因此对于读/写频率高的系统来说,这并不理想。

    编排、同步和顺序

    同步通信的一种变体带有中央编排器,编排器仍然是拦截服务。它处理传入的工作流定义请求并将其转发到下游服务,并接受响应(如下图所示)。在处理请求过程中,编排器一直在对服务进行调用。

    在初始约束固定的情况下,工作流管理在这种方法中更加灵活。工作流更改对编排器来说仍然是本地化的,并保有一定的灵活性,由于通信是同步的,所以同步消费者可以在没有中介组件的情况下进行通信。但是,编排器需要持续保持所有活动请求,这使得编排器比其他服务承担更重的责任,也容易导致单点故障。这种风格的架构仍然适合读取繁重的系统。

    编排,同步和并行

    在之前的方法基础上的一个小改进是使独立请求可以并发,这可以获得更高的效率和性能。由于这个责任属于编排器,所以很容易做到。工作流管理已经集中,需要做的只是更改声明以区分并行和顺序调用。这可以提高流程执行速度,通过缩短响应时间,协调器可以获得更高的吞吐量。

    工作流管理比以前的方法更复杂,但它仍然可能是一个合理的折衷方案,因为它可以提高吞吐量和性能并同时保持消费者的通信同步。由于其同步特性,该系统对于读取型架构来说仍然更好。

    权衡

    尽管同步调用更易于掌控,调试和实现,但在分布式环境中实施则需要一些权衡。

    平衡能力

    它要求有意平衡所有服务的能力。一个组件上的流量激增可能淹没其他服务请求。在异步通信中,队列可以缓解流量激增。同步通信缺少这种中介,需要服务容量在流量激增期间进行匹配。如果做不到这一点,可能会出现级联故障。另外,像断路器这样的弹性范例可以帮助缓解同步系统中的流量激增。

    级联故障的风险

    同步通信使上游服务在微服务架构中容易出现级联故障。如果下游服务出现故障或最坏情况,则需要很长时间才能回应,资源会很快耗尽。这可能会导致系统产生多米诺骨牌效应。可能的缓解策略可能涉及一致的错误处理,合理的超时时间以及执行服务质量保障协议。在同步环境中,一个服务会立即影响到其他服务。如前所述,通过实施舱壁架构或断路器可防止级联错误。

    增加负载平衡和服务发现开销

    通过将参与服务放置在负载均衡器后面可以解决其冗余和可用性需求。这增加了每个服务的隔离级别。此外,每项服务都需要加入中央服务发现设置,这允许它推送它自己的地址并解析下游服务的地址。

    耦合

    同步系统会在一段时间内表现出更紧密的耦合。服务之间没有抽象,服务直接和其他服务的合同绑定。这在一段时间内产生了强烈的关联。对于合同中的简单更改,拥有的服务不得不在早期采用版本控制。它增加了系统的复杂性,或者会导致与合同相关的所有消费者服务的变化。

    随着服务网格等新兴架构范例的出现,有可能解决一些陈述的问题。Istio,Linkerd,特使等工具,允许服务网格创建。这个社区正在成熟并且充满希望,它可以帮助构建同步的,解耦的和容错的系统。

    异步

    异步通信非常适合分布式体系结构。它不需要等待响应,从而将两个或多个服务的执行分开。异步通信的实现有几种方式,通过RPC(例如,grpc)或通过中介消息总线直接调用远程服务就是一些例子。编排消息传递和事件编排都使用消息总线通道。

    中央消息总线的一个优点是一致的通信和消息传递语义。这给服务间的直接异步通信带来了巨大的便利。我们通常使用像消息总线这样的媒介来保证跨服务通信的一致性。下面将基于使用中央消息管道的假设来讨论的异步通信的种类。

    变体

    异步通信可以更好地处理流量激增。体系结构中的每个服务都会生成消息,消费消息或执行两者。我们来看看这种范式的不同变体。

    异步事件协同

    在这种方法中,每个组件监听中央消息总线并等待事件,事件的到来是执行的信号,执行所需的任何上下文都是事件有效负载的一部分,触发下游事件是每个服务所拥有的责任。基于事件的体系结构的目标之一是将组件分离,不幸的是,需要在设计层面满足这种需求。

    通知组件接受到事件时可能电子邮件或SMS发送。由于所有其他服务需要做的事情是生产事件,因此它看起来可能是彼此分离的。但是,确实需要有组件承担决定通知类型和内容的责任,通知可以根据传入的事件信息做出该决定,如果发生这种情况,我们就已经建立了通知和上游服务之间的耦合。如果上游服务将此作为有效负载的一部分,则他们仍然感觉到下游的流量。

    即便如此,事件协同也非常适合需要发生的隐式操作。比如错误处理,通知,搜索索引等。

    这种体系结构遵循分散的工作流程管理。该体系结构适合写入繁重的系统。缺点是同步读取需要协调,并且工作流会在系统进行广播。

    编排、异步和顺序

    我们可以从我们的协调同步通信方法中借鉴一些。我们可以通过中央编排器建立异步通信。

    每项服务都是中央消息总线的生产者和消费者。编排器的职责是将消息路由到他们相应的服务。每个组件消费一个传入事件或消息,并在消息队列上生成响应。编排器消耗此响应并进行转换,然后再前进到下一步。该循环继续,直到指定的工作流程达到系统中的最后一个状态。

    在这种风格下,工作流管理对于编排器来说是本地的。这种系统在处理写入流量很大时有很好的表现,同步消费者需要调解。这在所有异步通信变体中都很流行。

    在编排系统中,协同耦合问题的解决方案更加优雅。在这种情况下,工作流工作在编排器上,丰富的工作流规范可以捕获通知类型和内容模板等信息,对工作流程的任何更改都保留在编排器服务中。

    编排和事件协同混合使用

    另一个成功的变体是具有编排和事件协同的混合系统。编排非常适合显示的工作流执行,而协同可以处理隐式执行。在工作流程中执行叶节点可能是隐含的,工作流规范可以促进在特定步骤中发生事件,这可能会导致执行任务,如通知,索引等等,编排可以继续推动显式执行。

    这两种方法的融合提供了近乎完美的方案,虽然仍需要采取预防措施以确保它们不会责任重叠,并且明确边界以决定它们的功能。

    概述

    异步风格的体系结构解决了同步体系结构系统所具有的一些缺陷。异步结构在处理突发大量请求时表现更好。中央队列允许服务赶上合理的请求积压,当很多请求在短时间内出现或者服务暂时关闭时,这是非常有用的。

    每个服务都以消费者或生产者的身份连接到消息队列,只有消息队列需要服务发现。因此,对中央服务发现解决方案的需求不那么紧迫。此外,由于服务的多个实例连接到队列,因此不需要外部负载平衡,这可以防止负载平衡器引入导致的隔离级别提升。它还使服务能够无缝线扩展。

    权衡

    本质上是异步的服务流可能很难通过系统进行跟踪。采用异步通信的系统会有一些折衷,我们来看看其中的一些权衡点。

    更高的系统复杂性

    异步系统往往比同步系统复杂得多。然而,系统的复杂性和性能以及规模的要求是合理的开销。一旦采用协调器和单个组件,就需要接受异步执行。

    读取/查询需要中介

    除非专门处理,否则同步消费者受异步体系结构的影响最大。要么消费者适应异步系统的工作,要么系统为消费者提供同步接口。

    异步架构非常适合写入繁重的系统。但是,它需要协调读取/查询同步,有几种方法来管理这种需求,每种都有一定的复杂性。

    同步包装

    所有方法中最简单的是在异步系统上构建同步包装。这是一个可以调用下游异步流程的入口点。同时,它保存等待的请求,直到响应返回或发生超时。同步包装是一个有状态的组件。传入的请求将自己绑定到它所在的服务器上。来自下游服务的响应需要到达原始请求正在等待的服务器。这对分布式系统来说并不理想,尤其是那种大规模运行的系统。但是,编写起来很简单,而且易于管理。这种方法符合具有合理的缩放和性能需求的系统的需求。在进行更为剧烈的重构之前,可以考虑同步封装。

    CQRS

    CQRS是一种将读取与写入分离的架构风格。CQRS给系统带来了大量的风险和复杂性,它非常适合大规模运行并需要大量读取和写入的系统。在CQRS体系结构中,数据从写数据库流向读数据库,查询在读取优化的数据库上运行,读/写层是分开的,系统保持最终一致性。两个层的优化是独立的。这样的系统结构更复杂,但可以具有更大的规模。而且,组件可以保持无状态(与同步包装不同)。

    双重支持

    在同步包装器和CQRS实现之间有一个中间地带,每个服务/组件都可以支持同步查询和异步写入,这适用于中等规模运行的系统。所以读取查询可以在组件之间跳转以完成同步读取。另一方面,写入系统将流入异步通道。这里有一个权衡,对系统的读和写进行独立优化是不可能的,或者,这对于在高流量下运行的系统是有益的。

    消息总线是故障的中心点

    这不是一种折衷,而是一种预防措施。在异步通信方式中,消息总线是系统的支柱,所有的服务都不断地从消息总线上生产和消费。这使得消息总线成为系统的致命弱点, 因为它仍然是故障的中心点。消息总线支持横向扩展非常重要,否则它可能违背分布式系统的目标。

    最终一致性

    异步系统可以是最终一致性,这意味着查询结果可能不是最新的,即使系统已经发布了写入。虽然这种权衡可以让系统更好地扩展,但也需要同时考虑系统设计和用户体验。

    混合

    可以同时使用异步和同步通信,但这两种方法的权衡会压倒他们的优势。一方面,系统必须交换处理两种通信风格,同步调用会导致级联降级和失败。另一方面,异步通信会增加设计的复杂性。根据我的经验,选择单一方式对于系统设计来说更加富有成效。

    结语

    马丁福勒有一个关于如何构建微服务的伟大博客。一旦决定构建微服务架构,需要仔细审议其执行流程风格。对于写入繁重系统来说,带有同步包装的异步系统是最佳选择。而对于读取繁重系统,同步通信就很好。

    对于读写繁重,但具有适度规模要求的系统而言,同步设计将大大简化设计。如果一个系统具有显著的规模和性能需求,那么使用CQRS模式的异步设计是一种可选方案。

    相关文章

      网友评论

          本文标题:详细解读微服务的两种模式

          本文链接:https://www.haomeiwen.com/subject/rwsfyqtx.html