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