在微服务架构风格中,微服务通常设计遵循SRP(单一职责原则),作为一个独立部署的软件单元,专注于做一件事,并且做到极致。作为开发人员,我们常常倾向于在没有考虑为什么的情况下尽可能地将服务做得小。这种关于什么是和不是什么是单一职责的主观性是我们开发人员在服务粒度方面容易犯错误的地方。为了克服开发团队在微服务尺寸上面临的这种困境,理解粒度驱动因素至关重要。
粒度
在微服务中,我们有两个概念——模块化,涉及将系统分解成独立部分,另一个是粒度,处理这些独立部分的大小。
确定正确的粒度水平——服务的大小——是微服务架构中我们开发人员苦苦挣扎的众多难点之一。粒度不是由服务中的类的数量或代码行数来定义的,而是由服务的职责来定义的——因此,找到服务粒度的正确之道存在困惑。
服务的粒度分为两种对立的力量——粒度解耦器和粒度整合器。
粒度解耦器
什么时候我应该考虑将一个服务拆分成更小的部分?
粒度整合器
什么时候我应该考虑将服务重新组合在一起?
粒度解耦器
由于我们生活在微服务和纳米服务的时代,大多数开发团队往往会错误地随意拆分服务,而忽略随之而来的后果。为了找到合适的大小,应对不同参数进行权衡分析,并在微服务的上下文和边界上做出明智的决策。
粒度解耦驱动因素提供了何时将服务拆分成更小部分的指导和依据。让我们看看这些驱动因素如何影响微服务的尺寸,以一个例子为例。
示例:考虑一个典型的通知服务,它执行三项操作:通过短信、电子邮件或邮寄信件通知客户。
让我们在解耦驱动因素上分析这个场景,并找到合适的尺寸。我们从:
服务范围和功能
服务是否在做太多不相关的事情? 范围和功能主要取决于两个属性——第一个是内聚性,指的是特定服务操作之间的相互关系的程度和方式。第二个是组件的总体大小,通常以职责数量、服务的入口点数量或两者的综合来衡量。 场景:观察通知服务,有人可能会说将这个服务拆分成三个单独的单一职责服务。但这是正确的做法吗?答案是否定的!因为这个服务具有相对较强的内聚性,即所有这些功能都与一件事有关,即通知,并且具有一个单一的目的。所以,不需要拆分这个服务,它应该是一个服务执行三项操作。 接下来是:
代码波动性
更改是否仅限于服务的一部分? 代码波动性是源代码更改的频率。我们必须衡量服务中代码更改的频率,以合理解释为什么要拆分服务。 场景:假设我们有以下服务功能的指标:
现在,如果我们依据更改的指标来看,邮寄信件通知部分的频繁更改也需要测试短信和电子邮件部分,从而作为单一服务,这增加了测试范围和部署风险。那么我们如何解决这个问题呢?
如果我们将这个服务拆分成两个单独的服务,电子通知和邮寄信件通知,那么频繁的更改现在被隔离在自己的服务中,从而减少了测试范围并降低了部署风险。
可扩展性和吞吐量
服务的部分是否需要不同的扩展能力? 不同服务功能的可扩展性需求可以客观地测量,以量化服务是否应拆分。 场景:再次考虑通知服务示例,测量单个服务的可扩展性需求如下:
在这种情况下,作为一个单一服务,电子邮件和邮寄信件功能必须不必要地扩展以满足短信通知的需求,从而影响成本和弹性(如MTTS,即平均启动时间)。这完全合理地解释了将通知服务拆分成独立的服务——短信、电子邮件和信件,因为这样可以让每个服务独立扩展以满足它们不同的吞吐量需求。
容错性
是否存在导致服务关键功能失败的错误? 应用程序在特定领域内继续运行的能力,即使发生了致命崩溃(如OOM)。 场景:考虑我们的通知服务场景,假设电子邮件功能继续出现OOM错误并致命崩溃,整个整合服务将会瘫痪,包括短信和邮寄信件处理。 将这个单一的整合通知服务拆分成三个独立的服务,为客户通知领域提供了一定的容错性。因此,电子邮件功能中的致命错误不会影响短信或邮寄信件。 进一步说明:现在,这里可能会出现一个问题,因为电子邮件功能频繁崩溃,为什么不将短信和邮寄信件功能合并?这是一个有效的问题。如果我们记得,当我们讨论代码波动性场景时,我们将邮寄信件与电子邮件和短信分开,并将它们合并成一个——电子通知。如果我们在那里能这样做,我们也可以在这里这样做。那么,为什么不呢? 因为电子邮件和短信是相关的,它们都是电子通知方式。但在这里,短信和邮寄通知没有任何共同之处可以将它们合并。换句话说,这里没有内聚性。
注意:记住,如果一个服务难以命名,因为它在做多个不相关的事情,那么考虑拆分服务。第二,每当拆分服务时,无论驱动因素是什么,总是检查是否可以与“剩余”功能形成强烈的内聚性。 所以,在这里将通知服务拆分成三个独立的服务是有意义的。 最后一个驱动因素是:
可扩展性
服务是否总是在扩展以添加新功能? 随着服务扩展,添加额外功能的能力。 场景:假设我们有新的功能要添加到通知服务——比如移动推送通知、桌面通知、社交媒体通知等。这些新功能当然可以添加到一个单一的整合通知服务中。然而,每次添加新通知时,整个通知服务都需要进行测试,并且所有通知功能都需要不必要地部署到生产环境中。
注意:仅当事先知道计划和希望作为域的一部分的额外整合功能时,才应用此场景。
推荐实践
1.如果一个服务难以命名,因为它在做多个不相关的事情,那么考虑拆分服务。2.每当拆分服务时,无论驱动因素是什么,总是检查是否可以与“剩余”功能形成强烈的内聚性。3.根据业务能力而不是技术能力拆分服务。4.在设计微服务时使用单一职责原则(SRP),但要牢记强内聚性的全景。5.使用解耦驱动因素分析拆分服务的权衡。