Kasun Indrasiri是WSO2的集成架构主管。
开发人员越来越依赖微服务架构将应用程序构建为一组细粒度,重点狭窄且独立的服务,每个服务均独立开发和部署。 尽管微服务方法促进了敏捷性,但它也带来了新的挑战,因为这些服务必须通过网络调用相互之间以及与其他系统(例如Web API和数据库)进行交互。 而且由于网络始终是不可靠的因素,因此此类交互随时容易出现故障。
因此,基于微服务的应用程序的弹性(即从某些类型的故障中恢复并保持功能的能力)在很大程度上取决于该应用程序处理不可靠网络上的服务间通信的能力。 因此,基于微服务的应用程序的弹性在很大程度上取决于您实现微服务通信的弹性。
鉴于微服务的激增,通过网络建立微服务通信已成为实现微服务体系结构最困难的方面之一。 同时,出现了许多弹性模式来应对这一挑战。 这些包括超时,重试,断路器,故障快速,隔板,事务,负载平衡,故障转移和保证的交付。
让我们看一下常用的微服务弹性模式。 然后,我们将研究如何使用Ballerina (一种新兴的开放源代码编程语言)应用这些模式,该语言已针对集成进行了优化,并旨在构建微服务和云原生应用程序。 最后,我们将讨论服务网格在减轻服务间通信功能中的作用。
分布式计算中的一个常见错误是假设网络将可靠。 一种更审慎的方法是计划最终的网络故障。 在这种情况下,考虑与不可靠网络上的应用程序间通信有关的几种模式非常有价值,Michael Nygard在他的书Release It中对此进行了讨论。 由于微服务通信始终通过网络进行,因此这些模式可以直接应用于微服务架构。
超时是一种非常常见的弹性模式。 当我们使用同步服务间通信时,一个服务(呼叫者)发送请求并及时等待响应。 超时模式用于确定何时停止等待呼叫者服务级别的响应。
因此,当您使用同步消息协议(例如HTTP)调用另一个服务或系统时,您可以指定希望等待响应的超时时间,如果达到该超时时间,则可以定义一个特定的逻辑来处理此类事件。 。 这样,超时可以帮助服务隔离其他系统或服务的异常行为或异常,从而不必成为服务的问题。
给定服务可能会间歇性或瞬时出现故障或中断。 重试模式背后的关键概念是提供一种方法,即使网络中断,也可以在重试一次或多次调用同一服务后获得预期的响应。 作为服务调用的一部分,您可以指定何时应该重试给定的请求,给定的后端服务的重试次数以及每次重试之间的间隔。
当我们调用给定的微服务时,如果该微服务持续失败,则对该服务的进一步调用可能会导致进一步的损坏和级联故障。 断路器是调用外部服务时使用的包装器组件。 如果服务调用失败并达到某个阈值,则断路器将阻止该调用。
如下图所示,在正常情况下,电路处于闭合状态,并且微服务B的调用正常进行。 但是,当存在符合断路器断开标准的故障时,服务将进入断开状态,从而阻止调用。
WSO2当调用失败时,断路器将保持闭合状态并更新阈值计数。 根据阈值计数或故障计数的频率,它会断开电路。 当电路断开时,将阻止对外部服务的真正调用,并且断路器会生成错误并立即返回。 当电路在一定时间段内处于断开状态时,我们可以通过在适当的时间间隔后再次尝试服务调用并在成功后重置断路器来应用自重置行为。 该时间间隔称为电路复位超时。 因此,我们可以在给定的断路器实例中识别不同的状态和状态转换,如以下状态图所示。
WSO2因此,当达到重置超时时,电路状态将更改为半开状态,在此断路器允许试运行一次或多次调用外部服务。 如果尝试成功,则断路器再次将状态更改为闭合状态;如果尝试失败,则将状态更改为断开状态。
通过设计,断路器是一种用于在系统运行不正常或出现故障时降低系统性能的机制。 这样可以防止对系统造成进一步的损害或级联故障。 我们可以使用各种退避机制,超时,重置间隔,触发断开状态的错误代码,触发忽略响应的错误代码等来调整断路器。
尽快发现故障对于构建弹性服务至关重要。 在快速故障模式中,基本概念是快速故障响应比慢速故障响应要好得多。 因此,作为服务的业务逻辑和网络通信的一部分,我们应该尝试尽快发现潜在的错误。
我们有很多方法可以检测到故障,并且它们可能从一个用例变为另一个用例。 在某些情况下,仅通过查看消息的内容,我们就可以确定该请求不是有效的请求。 在其他情况下,我们可以检查系统资源(线程池,连接,套接字限制,数据库等)以及请求生命周期下游组件的状态。 快速失败与超时一起将帮助我们开发稳定且响应Swift的基于微服务的应用程序。
隔壁是一种用于对应用程序进行分区的机制,以便将分区中发生的错误仅本地化到该分区。 这样,错误不会使整个系统处于不稳定状态。 只有分区会失败。 通过设计,基于微服务的应用程序擅长隔离故障,因为每个服务都专注于与系统其余部分完全隔离的细粒度业务功能。 如果我们可以将服务划分为多个子组件(例如,用于读取操作的组件与用于更新操作的组件),则我们可以独立设计每个组件,以便在一个组件发生故障时,另一个组件组件仍能按预期运行。 例如,即使更新操作被挂起,读取操作仍然可以使用。
故障转移和负载平衡模式背后的想法非常简单。 负载平衡用于在多个微服务实例之间分配负载,而故障转移用于在给定服务发生故障时将请求重新路由到备用服务。 有多种故障转移和负载平衡技术可应用于软件应用程序开发的不同级别。 在微服务的上下文中,我们将仅关注客户端故障转移和负载平衡,其中调用其他服务的给定服务使用故障转移或负载平衡逻辑来使服务功能高度可用。 使用客户端负载平衡(或故障转移),客户端无需依靠其他服务来分配(或拾取)负载,而是负责决定(使用服务发现或预定义集)使用算法将流量发送到何处,例如循环赛。
鉴于通信是通过不可靠的网络进行的,因此在某些情况下,我们需要确保两个服务或系统之间的消息传递是可靠的。 为此,作为实现的一部分,我们需要在服务或系统之间建立消息传递,以确保它们之间的通信不会丢失任何消息。 通常,使用第三方组件(例如消息代理)来实现此模式。 大多数现有的消息代理和消息传递协议,例如AMQP(高级消息排队协议),促进了可靠传递用例所需的许多语义。
在开发基于微服务的应用程序中,通常需要在服务边界内和服务边界外建立事务。 构建微服务时,通常需要在服务边界内构建事务,其中可能包括服务的私有数据库和其他资源。 这样的交易通常被称为本地交易,因为它们在单个服务的范围内运行,并且由于它们在服务本地,因此通常更易于实现。
当事务边界跨越多个服务和系统时,我们需要使用分布式事务来实现此类用例。 有一些通常用于在微服务之间分配事务的模式。 其中包括两阶段提交和sagas ,这是一种使用每个参与者服务的补偿操作构建分布式事务的方法,这些操作由在分布式持久层中运行的中央执行协调器进行协调。 但是,一个好的做法是,尽可能避免分布式事务,仅在您的用例绝对必要时才使用它们。
有多种技术可以使我们构建我们已经讨论过的微服务弹性模式。 根据您用于构建微服务的技术,您需要为所使用的每种技术选择弹性服务实现堆栈,例如Hystrix for Java微服务。
在这里的示例中,我们将使用Ballerina (一种新的开源编程语言),该语言专门用于构建微服务和云原生应用程序。 Ballerina的目的是通过使编写易于以类型安全和弹性方式跨分布式微服务和端点进行集成和编排的程序变得容易,从而填补集成产品和通用编程语言之间的空白。
让我们尝试使用Ballerina来实现我们上面讨论的一些微服务弹性模式。 GitHub回购中提供了以下所有示例的源代码。
对于超时用例,假设我们有一个旧服务需要花费更多时间来响应,但是我们开发的微服务被设计为具有3秒钟的超时。 要使用Ballerina实施此服务,您只需要将超时设置为出站端点的一部分。
WSO2如果发生超时方案,则调用将引发错误,并且您将需要显式处理超时相关方案的业务逻辑。
WSO2对于重试用例,假设您有一个间歇性失败的服务,而重试相同的请求可能会返回预期的结果。 您可以通过将重试配置添加到HTTP客户端端点来在Ballerina级别实现此类端点。
WSO2Ballerina的断路器实现与超时非常相似。 在这里,我们使用断路器配置来修饰HTTP客户端代码。 您可以定义在断开,闭合和半断开状态之间更改断路器状态的各种条件。 当电路断开时,端点的调用将引发错误,并且我们可以将断路器断开状态的行为作为该错误处理逻辑的一部分来处理。 在此示例中,我们将设置默认消息作为响应。
WSO2在“断路”情况下,将在不调用后端服务的情况下引发错误。 您可以将方案的业务逻辑编写为错误处理逻辑的一部分。
WSO2通过使用专用的故障转移端点并指定故障转移URL和故障转移参数,可以使用Ballerina实施故障转移方案。 例如,样本用例模拟了位于不同地理区域中的三个不同端点之间的故障转移行为。
WSO2同样,我们可以使用Ballerina的负载平衡端点构建负载平衡方案。 同样,在这里,我们可以定义一组负载平衡端点URL和其他参数,例如负载平衡算法。
From: https://www.infoworld.com/article/3310946/how-to-build-resilient-microservices.html