微服务生态环境全解析,使开发更具弹性和容错能力,你期待吗?

134 篇文章 1 订阅
49 篇文章 0 订阅

前言

在软件开发过程中,总会出现一些我们所不了解的内容。例如,软件最终是否会成功运行?当编写应用程序并将其置入产品中时,也会产生各种问题,其间也会伴随着失败。

有人曾指出,零bug的软件是不存在的。充其量,软件只是存在未知的bug。这一说法并非谬论,甚至是100%正确的。

通常,应用程序包含较高的测试覆盖率;另外,领域业务还涉及自动化测试以及集成测试。显然,一切工作良好。但是,当谈及微服务时,还会涵盖一些潜在的风险, 例如网络连接、负载平衡中的错误,以及外部服务使用过程中的故障。

微服务中的问题可能源自开发团队中产生的bug,或者是与其他服务集成后造成的不良后果。毫无疑问,应用程序中的缺陷将导致产品的一种良性失败。

这里,大家可能会产生疑问,难道还会存在一种良性失败?答案是肯定的。良性失败意味着,问题不包含不可用服务,或者无效内容仅呈现为局部特性,而不涉及整个系统。除此之外,快速发现问题同样是一种“成功” 的标志。

对于故障的良性结果,我们将采取一些措施, 并在工作堆栈中解决此类问题。

容器中的分离机制

为了阻止故障的发生,重要的是需要了解产生故障的方式。下面考察单体应用程序中较为常见的操作,此类也会出现于微服务架构中,进而有助于我们理解故障前的响应方式。

种较 为常见的方法是将应用程序的整体结构置于单一存储库中。也就是说,软件代码、缓存以及应用程序的所有其他特性均位于同一台机器设备中,这种情况屡见不鲜。

如下图所示,显示了缓存、数据库、API以及业务逻辑层位于同一位置时的情形。初看之下,这一配置行为并无问题。在同一台机器中,诸如延迟、包丢失和部署复杂性等问题都被简化了。

微服务生态环境全解析,使开发更具弹性和容错能力,你期待吗?

 

在当前方案中,假设容器出现故障,那么,将难以分辨容器故障所对应的组件。这一辨识过程将占用大量的时间。另外,缓存中的某个缺陷可能会导致应用程序崩溃。故障的产生过程往往是以渐进方式进行的,当意识到容器的系统故障时,可能为时已晚。

由于所有组件均处于连接状态,并且无法对其进行单独处理,因而整个应用程序将被重新启动。针对此类行为,尚不存在一种优雅的 系统可对此提供支持。除了导致某种程度的不一致结果之外,还可能丢失数据。

之前的一些积极因素现在被证明是一种很容易破坏弹性的选择方案,例如部署复杂性的降低,以及延迟和包丢失的减少。将整个应用程序堆栈保存在一个物理组件中, 意味着所有软件层都将使用组件的相同特性。此时,故障的缓解往往与自身方案的选取有关。

这种系统故障不仅影响了应用程序的可用性,而且还可能影响产品在客户和投资者眼中的可信度。下图显示了缓存出现错误且数据库过载时发生的故障,进而导致系统崩溃。

这依然不是最糟糕的情况。对于应用程序来说,最不希望听到的即是“成功”一词,成功意味着应用程序的扩展。也就是说,在不提升弹性的情况下,成倍地增加可用性。高可用性将与所支持的命中数量有关,而不是保持可用性,即使发生内部故障。

微服务生态环境全解析,使开发更具弹性和容错能力,你期待吗?

 

这可能并不是当前应用程序的选择方案。通常,在较差的微服务生态系统中,单体架构呈现为重复状态。对于一些软件工程师来说,微服务架构仅仅是将业务逻辑与应用程序分离开来。这种认知是错误的一应用程序的组件 也必须以可伸缩的方式进行设计,因此,在极端情况下,必须可实现快速修复或重置行为。

分层服务架构

对于应用程序所采取的分离机制,这是一种十分常见的反模式。一些工程师常会在堆栈组件的分离与逻辑层的分离之间产生混淆,这样就产生了一个缺少业务表达的贫血域。

下图显示了一-种较差的实现方式。其中,软件由3个不需要的物理组件组成。同时,应用程序的分离具有较细的粒度。此外,编排器和数据访问并不具备实际意义。

基本的业务内容需要对数据库进行访问。很快,构建这种分离机制便没有逻辑可言了。另一点需要注意的是,如果编排器直接访问数据,也就意味着,它在应用业务方面具有一定的智能。另外,编排器也是完全不必要的一一仅存在一个业务层, 因而并不存在数据可供编排。

另外,当前粒度是微服务架构中非常常见的错误,同时也是我们不惜一切代价所要避免的。该粒度会提升系统的复杂度,同时增加维护工作物理组件的成本,以及多层间的通信成本。

微服务生态环境全解析,使开发更具弹性和容错能力,你期待吗?

 

存储分布

在任何应用程序中,数据保存均是一个十分重要的话题,微服务也不例外。利用分布式应用程序,数据的分布将变得更加灵活。

在处理微服务中的存储问题时,存在一些较好的实践方案。 显然,CQRS模式十分有效,但在性能方面仍有所欠缺。对于应用程序的健康问题,数据的区域化和折旧(depreciation)较为有用。

在容器的创建过程中,可以看到,除了应用程序自身的容器之外,在UsersService中,还有两个数据存储层的特定容器,并作为数据库本身的缓存。

折旧数据

在科学计算年代,数据分析占有重要地位。删除数据听起来是十分不合理的。是的,这的确有些荒谬。类似地,拥有100万数据行的数据库会生成越来越慢的查询操作。

这里的问题是,如果无法从数据库中删除数据,我们应该如何处理才能不影响查询操作?这个问题的答案是数据折旧。数据的折旧包括划分活动数据和非活动数据,并将非活动数据移至与实时应用程序层无关的存储中。

下面考察门票销售这一示例。其中,每天都会有事件被创建和执行,并完成基于事件的门票购买活动。这也是该应用程序的主要例程。

一段时间后,我们将对与事件相关的数据进行审计,或者仅用于数据分析。对于最近的事件、仍处于活动状态的事件,以及已超出1年的事件,保存其数据并无意义。

但是,随着时间的推移,将所有数据保存在一个存储设施中,会产生较慢的查询操作,以及更为严重的数据迁移或修改问题。针对于此,数据折旧是一种较好的方法,特别是在实现自动化时。

区域化数据

在应用程序中,通常的做法是将其部署到服务器中,并为应用程序提供访问点——如果应用程序在全球范围内使用,则重复执行这- - -处理过程。但通常情况下,服务器的部署一般在距新产品市场最近的地理位置处进行,这一做法完全正确,但问题也会随数据库服务器的地理位置而出现。

查看依据地理位置分布的应用程序是一类常见操作,但需要从几公里之外的服务器处获取数据。因此,在不同的地理位置处,这将带来不同的体验。例如,如果服务器设在美国,那么,美国终端用户的体验要远远好于澳大利亚的用户,其中会涉及物理距离、延迟以及可能出现的数据表丢失问题。针对这一类问题,数据的区域化则是较好的方法。

对于新闻门户网站来说,欧洲用户一般仅关注本地的新闻,而不是南非等国家。这意味着,当编辑出版系统发布一个新词时,必须首先按区域存储数据,然后在类似CQRS处理过程中对数据进行标准化。稍后,考虑到当前主题的特殊性,对应数据无须进行标准化处理。

田元基于地理位置划分的数据分布策略涵盖了多样化特征,但不应忽视数据的区域化问题,因为它将直接影响到应用程序的性能。

隔离——使用生态系统防止故障的出现

以下内容讨论了故障的防止措施,以及如何开发具有高可用性和弹性的应用程序。本节将考察相关结构的保护方式。

冗余设计

尽管我们的应用程序仅在单实例中执行,并且由于组件在容器中的分布而变得更加灵活,但仍然会受到系统故障的影响。

冗余是解决上述问题的一种有趣的方法。使用冗余方案时,即使应用程序的一个节点丢失,其他节点仍可以继续响应。

负载平衡器是微服务冗余的一个很好的例子,它使用一.种策略来重定向请求。其中,我们可以创建大量节点,如果某个节点出现故障,仍然有另一个节点可以响应。

在下图中,请求发送至负载平衡器中,并根据开发团队和相关操作制定的某些规则,定向至业务层的实现中。

应用程序的所有节点一般不会出现整体崩溃。如果出现任何不稳定现象,我们仍有时间升至某个新版本;或者简单地识别和修复所遇到的错误。

下面针对当前应用程序使用负载平衡器。对于大型应用程序来说,一种较好的方法是使用HAProxy。但在当前示例中,我们令Nginx担负起这一职责 。

微服务生态环境全解析,使开发更具弹性和容错能力,你期待吗?

 

临界分区

在理解我们正在开发的应用程序时,重要的一点是了解应用程序最常使用的访问点,而此类访问点往往难以发现。

假设我们正与某个在线销售系统协同工作,在该应用程序类型中包含了多种组件,但显然,最为重要的组件是完成产品销售部分的组件。然而,全部销售流程与业务逻辑之间处于耦合状态,这种耦合源自销售界面直至付款。

上述在线商店似乎工作良好。只要向服务器群添加更多的机器,系统的所有压力都可以通过水平可扩展性得到解决。这里的问题是,针对当前应用程序类型,水平扩展并非绝对可行。其中,许多资源处于共享状态并可立即获得。对于-一些难以令人满意或者是无关紧要的结果,过程中的延迟将会造成很高的代价。再次强调,对于应用程序来说,“成功”一词往往意味着严峻的问题。例如,网上商店在“黑色星期五”时即会面临此类问题。

在几分钟内,可以查看到大量的点击量,但是销售额却未见明显增长,这绝非是正常现象。几分钟之后,就会发现问题所在。在这种情况下,主要问题来自处于耦合状态的购买流。其间,在线商店的访问量巨大,以至于阻塞了后续的购买流程。也就是说,搜索价格的用户屏蔽了广告产品的实际购买者。如果这种偏差十分严重,那么,应采取相关措施处理此类问题。

这种在实时交互性应用程序中的故障是非常常见的。通常,点击量-般大于完成整个交互性流程的用户数量。此类场景适用于任何类型的实时应用程序,无论是在线商店、游戏还是政府的税务申报系统。所有这些应用程序均包含相同的特征:高季节性访问以及低转换率。产品的转换数量- -般不会受到影响,即使该数量低于点击量。

当处理各类问题时,微服务架构通常十分有效。当采用DDD对每个域内的边界上下文进行设置,以及执行关键评估时,应用程序每个部分可选取更加智能的架构。

在下图中,采用临界划分这概念可使处理操作升至当前最为需要的程序段。据此,软件的其他部分则可避免外部大量点击操作造成的压力。

微服务生态环境全解析,使开发更具弹性和容错能力,你期待吗?

 

隔离设计

我们已经看到,通过临界状态分离应用程序包含了诸多优点。然而,一个常见的误解是组件的复用。对此,一种较差的资源共享示例是数据库的复用。无论负载平衡器、应用程序线程级别或者域划分方式如何优化,如果应用程序仅依赖于单一物理组件,那么,系统崩溃迟早会到来。

下图中的设计方式即显得较为草率。其中,应用程序的全部组件(暂不考虑相关域)均依赖于同一物理组件,在该示例中则是数据库。这一类错误 也常出现于缓存和消息代理中。

微服务生态环境全解析,使开发更具弹性和容错能力,你期待吗?

 

需要注意的是,无论是否缓存,数据也是微服务域中的一部分内容。物理组件应尽可能地处于独立状态,如下图所示。

微服务生态环境全解析,使开发更具弹性和容错能力,你期待吗?

 

上图中所示的物理组件方案则更为常见,进而可简化迁移和数据库的分片机制。

快速故障

本节将考察一个在线商店示例。如前所述,就可用性来说,临界划分并遵循DDD边界包含诸多优点。除此之外,前述内容还讨论了软件与物理组件依赖关系中蕴含的风险。

相应地,其中的大部分内容可消除系统架构中的故障或风险。有时,问题并不取决于我们,外部服务也会在生态系统中使用微服务。

对于在线商店的支付流程,微服务则是购物流程中的最后-一个步骤,并负责完成产品的付款操作。付款过程必须通过信用卡网关进行通信,如果这些网关中的任何一个出现中断,鉴于外部服务的依赖关系,微服务可能会出现故障。

在下图中,微服务支付过程尝试构建与计费系统间的通信,但其中存在某些问题。

这里,我们不能简单地等待连接被恢复;相反,此处应产生快速故障并及时进行下一步处理。对于此类问题,断路器是-一种较好的方法。据此,可以设置超时或连接故障策略,进而提供其他形式的付款方式,或者以友好的方式向用户提供故障信息,从而避免了由于资源耗尽而导致的系统故障。

微服务生态环境全解析,使开发更具弹性和容错能力,你期待吗?

 

断路器

断路器是当出现过载或短路时自动关闭的操作开关。与电路保险丝类似,断路器的目的是实现快速故障和保护电力装置。对于微服务来说,断路器可保护应用程序的整体完整性。

假设微服务的运行速度变得十分缓慢。其中,各方请求不断出现并开始排队。在某种程度上,这可视为一种间接损害。特别是对于依赖于与其他微服务通信的微服务,此时需要使用断路器。

断路器的概念较为简单,其中仅包含了两种状态,如下所示。

  • 开:释放对外部依赖关系的调用。
  • 关:即刻放弃当前调用,并执行先前配置的操作。

在实际操作过程中,断路器自身将置于调用中,而不是微服务直接访问外部依赖项。对于基于预定义参数产生的任何故障,例如超时,断路器将中断与故障依赖项间的通信。当然,微服务方面也可以采取一些措施。断路器的行为如下图所示。

一些框架可以帮助实现断路器。目前,较为突出的框架是由Netflix开发团队创建的Hystrix。Hystrix 最初是在Java 中开发的,但是已经有其他编程语言实现了该算法,如Go语言。

微服务生态环境全解析,使开发更具弹性和容错能力,你期待吗?

 

总结

本篇讨论了一些较好的实践方案,以使微服务更具弹性和容错能力。另外,还介绍了应用程序中容器的使用方式,并使用Nginx开发UsersService 集群。

以上只是小编个人的想法,如果哪里有不准确的地方,还请多多评论指出,咱们共同学习进步。如果觉得对自己的学习有帮助,请多多哦点赞评论转发,关注小编,你们的支持就是小编创作最大的动力~~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值