目录
在本文中,我们探讨了为什么应避免或从应用程序中删除以下内容:状态服务,紧密耦合,启用横向扩展,将设置移出应用程序的每个部分,构建自动部署管道、监视、控制配置和环境的“独特性”。
因此,我已经完成了几篇有关如何提高应用程序的可用性和弹性的文章,并且在过去的几个月中我得出了一个结论。对于很多人来说,这是一个非常重要的主题。我已经在以前的文章中讨论了走这条路意味着什么,以及如何为可用性设计解决方案。
但是随之而来的另一个问题是,我应该做什么类型的事情才能在应用程序中实现更强的可用性和弹性?如何升级旧版应用程序以提高弹性?作为开发人员,我该怎么做才能牢记这一点?
因此,我想汇总一份您应该在应用程序中寻找的内容的列表,这些更改会增加您的可用性/弹性,并且/或者如果您想同时为应用程序增加这两个因素,则应该避免这些事情。
因此,让我们从我继续使用的两个术语开始,因为我发现这可以使两种方法都得到改善,并且只有当我们都在谈论同一件事时才会发生。
- 可用性(Availability) ——是您的应用程序即使在重大停机情况下也能够继续运行关键功能的能力。因此,能够以最小的用户影响继续提供服务的能力。
- 弹性(Resiliency ) ——是您的应用程序即使在出现重大或暂时性故障时也能够继续处理当前工作的能力。因此,完成当前正在进行的工作。
因此,进一步研究,问题就变成了我应该避免或从应用程序中删除哪些类型的东西,以改善我的前进位置。
项目#1——有状态服务
总体而言,这是消除可用性和弹性问题的关键要素,并且可能是一个备受争议的问题,但是在这里,我要谈到这一点。如果服务具有某种状态(在内存中或其他状态),则意味着对我来说,故障转移到其他地方变得更加困难。我知道我必须复制该状态,如果在内存中完成了,那将变得非常困难。如果是像SQL或Redis这样的独立存储,它会变得更容易,但同时又需要额外的复杂性,这会使这种形式的可用性更加困难。当您在SLA中添加“9”时,尤其如此。因此,一般而言,如果可以避免使用依赖状态的应用程序组件,那是最好的。
此外,有状态服务还会在云中引起其他问题,包括限制随着需求增长而扩展的能力。完美的例子是“粘性会话”,这意味着一旦将您路由到服务器,我们将继续将您发送到同一服务器。这是横向扩展的对立面,应不惜一切代价避免。
如果您正在处理旧版应用程序,并且删除状态不可行,那么至少需要确保在内存之外对状态进行管理。例如,如果您无法删除会话,请将其移至SQL并复制。
项目#2——紧密耦合
这指出了我上面概述的两个关键要素。当应用程序组件之间紧密耦合时,您将创建一些最终会失败并且无法很好扩展的东西。它阻止了构建可以很好扩展的解决方案的能力。
让我们举一个常见的例子,假设您的应用程序上有一个API层,并且该API与您的UI前端内置在同一个Web项目中。然后,该API直接与数据库对话。
这是一种非常常见的传统模式。这造成的问题是,加载Web应用程序的需求与后端API紧密耦合,因此,一个失败意味着另一个失败。
现在,让我们更进一步,说您将API暴露给外部世界(遵循安全实践,使您的应用程序可扩展。听起来不错,对。
除了深入了解之外,通过使所有应用程序元素都直接相互通信,您现在创建了一个方案,其中级联故障会完全破坏您的应用程序。
例如,您的一位客户决定相当努力地利用您的API,每30秒提取一次完整的数据转储,或者您注册了很多都决定使用您的API的客户。它导致以下效果:
- 对API的需求增加导致Web层上的内存和CPU消耗增加。
- 这会导致应用程序加载页面的性能出现问题。
- 这会导致间歇性区域,从而导致针对API的事务要求更高的SQL要求。对SQL的需求增加导致您的应用程序遇到资源死锁。
- 当应用程序失败时,这些资源死锁会导致用户体验的其他问题。
现在您可能正在考虑,是的,凯文,但是我可以在云中启用自动缩放,它可以解决所有这些问题。我的答复是,与此同时,您的账单出现了不受控制的通货膨胀。因此,显然,您的CFO对无法控制的成本和通货膨胀是可以接受的,以抵消一个不好的做法。
我们可以解决此问题的一种情况是,将API拆分到一个单独的计算层,这样我们就可以分别管理计算,而不必大肆扩展以解决问题。然后,我有允许我的应用程序扩展的单独选项。
另外,我可以将队列作为负载均衡实践来实现,这仅允许在队列深度扩展到超出合理响应时间的情况下扩展应用程序。我还可以限制来自API的请求或优先处理来自应用程序的消息。然后,我可以复制队列消息以提供更大的弹性。
项目#3——启用横向扩展
现在我知道了,我只是觉得横向扩展很糟糕,但这个的关键部分是“受控的”。我的意思是,通过使服务成为无状态,并实施解耦的实践,可以创建可以运行一个或多个服务副本的方案,从弹性和可用性的角度来看,这可以带来各种好处。它将您的服务从宠物变成了牛,您不再在乎是否将其中一个倒下,因为另一个已取代了它。它有点像九头蛇,这是一个很好的思考方式。
项目#4——将设置移出应用程序的每个部分
您的设置和应用程序代码连接的越紧密,进行更改的难度就越大。如果您的代码紧密耦合,并且需要进行部署以进行配置更改,那么这意味着您需要更改端点时,这将变得越来越困难。因此,您可以做的最好的事情就是开始将那些配置设置从应用程序中移出。无论您如何看,这都是一件重要的事情。由于以下原因:
- 安全
- 可维护性
- 自动化
- 变更管理
项目5——建立自动部署管道
很多时候,高可用性的关键在于自动化,特别是当您获得更高的9s级别时。简单的事实是秒数。
不仅如此,自动部署还有助于管理配置漂移,一个简单的事实是,配置漂移越多,维护辅助区域就越困难,因为您必须进行管理以确保一个区域没有任何东西。通过强制一切都通过自动化部署管道来消除这种情况。如果必须对每项更改进行脚本化和自动化,那么几乎不可能看到环境中发生的配置漂移。
项目#6——监视、监视、监视
高可用性和弹性的另一个要素是监视。如果您几年前曾问过我大多数开发人员认为是第一件事的问题,那就是“我如何确保这一点?”尽管这是一个问题,但许多开发人员仍然以某种方式将其视为事后的想法,而更大的问题是“我如何监视并知道它是否有效?”鉴于微服务和无服务器计算的兴起,我们确实需要能够监视我们部署的每段代码。因此,我们需要了解您构建的所有新产品才能回答该问题。
这可能很简单,例如将自定义遥测记录到Application Insights中,或者记录传入和传出请求,记录异常等。但是,如果不实现这些指标,我们就无法确保某些程序正在运行。
项目#7——控制配置
这是我在上面评论的基础上进行的。我看到人们在这类实现上遇到的最大错误是,他们不管理如何对环境进行配置更改。最终,这导致了“宠物”VS“牛”的心态。我的职业生涯中曾经有一个老板,他的办公室上方有一个标语,上面写着:“服务器是牛,而不是宠物……有时您必须做汉堡包。”
和上面的陈述一样有趣的是,它有一个真理元素。如果您允许更改配置并将修订直接应用到环境中,则会导致无法以任何信任度依赖自动化的情况。而且,它使监视和真正高可用性或弹性架构的其他所有元素完全不相关。
因此,您可以做的最好的事情就是利用自动化管道,并且如果需要进行任何更改,则必须将其推送通过管道,理想情况下,除了读取度量标准和日志记录外,最好使人们无法访问生产环境。
项目#8——消除环境的“独特性”
像上面一样,我们需要确保环境的所有内容都是可重复的。从理论上讲,我应该能够破坏整个环境,只需单击一个按钮,即可部署一个新环境。这只能通过编写所有脚本来完成。我是terraform的超级粉丝,可以帮助解决这个问题,但是bash脚本、powershell、cli,请自便。
您可以删除的内容越多,就越容易复制它,并至少创建一个主动/被动环境。
项目#9——开始实现可用性模式
如果您正沿着实现更多实践以增强应用程序的弹性的道路走,那么您应该考虑以下几种实践:在构建新服务时,这将有助于创建要构建的弹性类型。这些模式包括:
- 健康端点监控——在应用程序中实现功能检查,以确保可以利用外部工具来提供帮助。
- 基于队列的负载均衡——利用充当缓冲区的队列,或在应用程序如何以更灵活的方式处理传入请求之间放置抽象层。
- 节流——此模式有助于管理资源消耗,以便您可以在控制消耗的同时满足系统需求。
- 断路器——根据我的经验,这种模式非常有价值。您的服务应该足够聪明,可以使用增量重试,并在下游服务受到影响时退回。
- 散装头——这种模式利用分离和对容错能力的关注来确保由于一项服务关闭而导致应用程序未关闭。
- 补偿事务——如果您正在使用隔板或任何类型的容错功能,或者有顾虑分开,那么将事务回滚到其原始状态非常重要。
- 重试——要实现的最基本模式,对于建立瞬态容错能力至关重要。
项目10——记住这是一个不断发展的过程
如本文前面所述,此处的意图是,如果您希望构建更多基于云的功能,并进而提高应用程序的弹性,那么我能提供的最佳建议是记住这是一个迭代过程并寻找机会来更新您的应用程序并提高弹性。
例如,假设我必须对发送通知的API进行更改。如果要进行这些更新,也许我可以实现队列,记录日志并进行一些更改,以将其扩展到微服务中以提高弹性。当您这样做时,您会发现应用程序的位置将得到改善。