将容错功能整合到你的微服务架构中

251 阅读10分钟

在本文中,我们讨论了将容错合并到微服务体系结构中的过程。

通过优锐课的java学习,get很多心得干货。

容错是每个微服务体系结构的基本特征。原因很简单:在系统中的集成点达到一定数量后,每天都会发生故障。原因仅仅是统计上的。正如我将要展示的,我们不能无视数学定律。

这就是为什么我们需要从更高的角度了解故障的关键驱动因素。必须有效地做出战略决策。
没有这些决定,就不可能达到我们所期望的容错水平。

了解系统总可用性

该模型

我创建了一个简单的模拟来模拟各种情况。下面的所有结果都是借助该软件创建的。从高层次上讲,我们实际上并不需要具有真实网络堆栈的成熟应用程序来进行这些计算。

小型仿真的执行速度将大大提高,并且构建起来要简单得多。它使我们能够看到将系统扩展到100个节点的效果,这在实际场景中很难实现。

串行和并行连接

我们将首先看两个简单的示例,以了解每个组件之间的依赖关系将如何影响整个系统的可用性。简而言之,你可以串行或并行连接每个组件。

串行(应用程序和数据库)

在串行连接中,两个组件都应可用于将传入的请求提供给客户端。服务及其数据库是这种配置的一个很好的例子。在下面的示例中,两个组件都具有95%的可用性。 由于依赖关系,服务的可用性降低到90%。 串行连接的总可用性始终低于其组件的最低可用性。服务和数据库可用性

并行(群集)

在并行连接中,只需要一个组件即可将服务传入客户端。考虑到负载平衡逻辑的可用性为100%,服务于集群的请求就是一个很好的例子。

在实际情况下,将客户端负载平衡与重试结合起来很接近这种设置。
让我们来看另一个例子。在这里,每个组件再次具有50%的可用性。 模拟完成后,我们的总可用性应为75%。 故障率减半,这将使我们具有1到0.25%的可用性。 并行连接的总可用性始终高于其组件的可用性。 请注意,在Python模型中,我们仅允许相同服务组件的并行连接。

服务
2可用性

分解服务:网格和星形

在考虑如何将系统分解为多种服务时,首先要使用所描述的规则。让我们想象一下,我们从一个设计开始,每个组件之间都在互相交流,而没有任何限制。整个过程开始变得非常混乱,而且没人知道为什么在大多数工作时间内该站点都关闭了。使用错误选择的服务分解策略的“大爆炸”迁移项目很容易发生这种情况。

让我们从小处开始,说我们只有10个服务。每个新添加的组件都将依赖于所有先前存在的组件。这增加了许多抽象层,但更重要的是,许多集成点值得担心!

这些类型的系统的依赖关系图通常看起来像一个完整的网格。在Python模型中,我添加了具有99%可用性的每个服务。每个新添加的服务将取决于所有现有节点。中间所有连接中的最后一个估计可用性低于(1%)!

服务网

结论如何?
我们如何改善整体状况?好吧,在现有功能的基础上构建很好。但是,用网络调用封装每个依赖关系太危险了。我们的目标应该是减少集成点的数量并创建更大的服务。相反,请使用依赖管理工具重用依赖。你甚至可以复制功能,并根据需要多次实施它们。

在这种情况下,逆转集成调用,合并服务和重新实现某些功能会有所帮助。

向星型拓扑转变

现在,让我们看看另一个拓扑。
假定系统中的每个组件都通过
“代理”进行通信。 这会以积极的方式改变我们的可用性吗?

在下图中,每个服务的可用性为95%。 它们中的每一个都只有一个依赖性:中间的代理群集。 我们正在使用两个具有95%可用性的单独经纪人。你可以看到,可用性总体上有所提高。群集将代理的可用性提高了高达99.75%,并且每种服务的可用性仅按比例损失。

服务可用性

这些例子告诉我们什么?
如果确实需要在服务之间的依赖性方面具有如此复杂的功能,则最好使用消息传递作为通信渠道。你需要查看企业集成模式以弄清楚如何实施一些最具挑战性的方案。

消息驱动的体系结构通常在中间有一个消息代理,由Kafka,RabbitMQ或类似技术实现。 这种设置旨在继续推动公共依赖项的可用性和可靠性。 这可以或多或少地通过以下技术来实现。

从部署中消除单点故障

仔细评估所选技术的配置可以帮助你避免很多麻烦。这听起来并不那么简单,并且通常需要专门的人员来为你正确配置群集。如果你使用的是云提供商,请检查托管服务的可用性。例如,AWS还提供了SQS,SNS和托管的Kafka集群。

对经纪人进行分区

即使你对所选的技术有信心,还是建议将经纪人集群划分到不同的团队中。当你需要推出新的配置或改进时,它将为你提供帮助。例如,你可以先将它们交付到单个分区,然后在出现问题时退回。

常见的微服务模式及其对可用性的影响

现在,让我们看看如何通过分析两种最常见的微服务模式来应用所学知识。

CRUD服务和集合

通常,团队使用简单的
CRUD服务来封装特定的数据类型,而缺少任何业务逻辑。在这些情况下,你必须将上一层的业务逻辑推入通常通过调用多个CRUD服务来联接数据的聚合中。作为替代方案,你可以合并业务逻辑,而只是减少集成点的数量。但是,让我们看一个不太简单的场景。同样在这种情况下,每个单独服务的可用性都是95%。

CRUD服务和集合

我们有两个由汇总调用的CRUD服务。假设我们想通过将another_crud合并到聚合中来消除集成点。我们怀疑从代理的角度看可用性会增加,因此让我们做一些模拟并检查结果。

检查新的可用性

令人惊讶的是,我们失去了大约6%的可用性!但为什么?原因是我们不小心将聚合的出站依赖性从2增加到3。这使我们的系统的可用性变得更差。

因此,该模型向我们展示了我们不应就此止步。在将两个CRUD服务合并到集合中之前,我们将无法达到预期的效果,这将使我们想起更单一的设计。

CRUD服务合并为一个汇总

连锁通话

另一个常见的模式是与每个服务调用形成一条链,以简化数据聚合。在下面的示例中,从BFF获取用户数据和购物车信息会更有益吗? 通过设置仿真并评估结果很容易发现。

链式通话示例

我们从两种设置中获得的可用性都差不多,所以我说简单应该是这里的决定性因素。
如果用户数据包含在每个相关的购物车信息中,我们应该考虑购物车服务的可重用性。

另外,如果不能在一段时间内接收到用户信息,我们需要考虑哪个服务应该提供有意义的默认值。这些默认值在每个用例中都一样还是不同?与购物车页面相比,用户页面对失败的用户请求应该更加敏感。因此,后者的设计可能比前者更可扩展。无论如何,很高兴知道可用性在这类决策中没有重要作用。

最终设计

摘要

当微服务组合的全部可用性根本不可行时,我已经看到了一些用例,因此我决定创建自己的仿真来帮助演示高层架构决策的效果。

正如我们所看到的,在许多情况下,只要结果会带来正面或负面影响,它就不言自明。不过,最好了解其他与容错相关的模式。这些在使我们的服务更具弹性方面也发挥了重要作用。

你可以通过使用库在你自己的服务中本地介绍这些模式实现中的每一个,也可以在代理或服务网格中集中介绍这些模式实现。

重试

Cassandra的快速读取保护就是一个很好的例子。 此内置功能可确保如果在指定时间范围内未收到响应,则将发送其他请求。 弹性4实现许多容错模式,包括重试。

你甚至可以选择为重试策略配置一定程度的随机性的指数退避。如果你正在寻找集中式解决方案,则可以实现最流行的代理和服务网格,例如Trafik或Istio。

指数退避

最好在重试策略中添加指数退避和抖动以避免服务因你稀缺的资源而形成一群群。例如,Envoy代理提供了默认的退避算法。

后备和默认值

每当依赖服务不可用时,你仍然可以选择从缓存中返回过时的值或某些默认值。由于大多数情况下,后备和默认值与域紧密相关,因此只能在你的应用程序代码中引入。Resilience4J提供了一些现成的实现,例如在其Spring注释中的fallbackMethod属性。

断路器

断路器是最著名的稳定性模式。最受欢迎的实现是Hystrix。 但是,几乎所有常用的服务网格,代理和库都可以使用它,例如Istio,Envoy,Traefikor Resilience4J。

超时时间

你已经使用的连接库应立即提供超时。例如,如果你使用的是Spring Boot,则应为JDBC池配置Hikari CP。你应该检查默认超时设置并设置一个更合理的值,因为默认值通常过高。在大多数情况下,这需要30秒,因此请确保将其拨低一点。

> 喜欢这篇文章的可以点个赞,欢迎大家留言评论,记得关注我,每天持续更新技术干货、职场趣事、海量面试资料等等 > 如果你对java技术很感兴趣也可以交流学习,共同学习进步。 > 不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代 文章写道这里,欢迎完善交流。最后奉上近期整理出来的一套完整的java架构思维导图,分享给大家对照知识点参考学习。有更多JVM、Mysql、Tomcat、Spring Boot、Spring Cloud、Zookeeper、Kafka、RabbitMQ、RockerMQ、Redis、ELK、Git等Java干货