如何区分微服务还是分布式单体?


在没有意识到的情况下,基于微服务的应用可以轻而易举地转变为一个分布式单体。

当你意识到这一点时,可能已经太晚了,而且纠正它可能非常昂贵。

所以,就像你勤奋地审查代码和架构以确保它们遵守最好的实践、模式和原则一样,保持警惕并确保你的项目没有潜入这些陷阱也同样重要。在你的项目开发过程中,让这成为一个常规的习惯。

首先,什么是 "分布式单体"?
用非常简单的话来说,它是具有紧密耦合的微服务!!!

在更详细的解释中,一个系统在架构上像微服务一样分布在多个机器或容器中,但仍然表现出类似于单体系统的紧密耦合和相互依赖。

为什么它是坏的

  • 高耦合度:这些服务被紧密地联系在一起,一个服务的改变往往需要其他服务的改变。这大大降低了微服务的关键优势之一:独立改变单个服务的能力,阻碍了敏捷性。
  • 降低了自主性:微服务架构旨在促进服务的自主性,每个服务可以独立开发、部署和扩展。在分布式单体中,服务是紧密耦合的,限制了它们的自主性,使快速创新和迭代成为挑战。
  • 复杂性:分布式单体在管理和理解上可能比传统单体更复杂。你不仅有单体的所有相互依赖关系,而且还有分布式的挑战,如网络延迟和容错。
  • 减少可扩展性:微服务应该是独立可扩展的,允许将更多的资源分配给最需要的服务。但在分布式单体中,服务之间的紧密耦合会使独立扩展变得困难。
  • 缓慢的部署:微服务的目标之一是实现服务的快速和独立部署。然而,在分布式单体中,需要协调多个服务之间的变化,这可能会减慢部署过程。

让我们看看什么会导致分布式单体以及如何克服它。

  • 同步通信:如果你的服务是同步通信的,它们会变得高度耦合。如果一个服务没有另一个服务的响应就不能进行,那么一个服务的问题就会连带导致整个系统的故障。
  • 高度代码级的耦合:使用带有业务逻辑的共享库。共享库的函数签名的变化和其他变化会对多个微服务产生直接影响。
  • 共享数据库:如果多个服务使用同一个数据库,改变一个服务的模式可能需要改变其他服务。这是一种数据耦合的形式,会导致分布式单体。
  • 不适当的服务边界:如果你的微服务不是围绕业务能力设计的,那么你最终的服务可能过于细化或细化程度不够。这可能会导致服务间的高度通信和高度耦合,从而导致分布式单体的出现。
  • 共享部署:如果你的服务总是被部署在一起,那么它们就不是真正独立的。一个服务的改变需要整个系统的部署,这是单体的特点,而不是微服务架构的特点。
  • 缺少模块化:即使服务在物理上是分离的,它们在逻辑上仍可能是单体的。当服务紧密耦合时,就会发生这种情况,它们必须一起被改变和版本化。
  • 集中治理:如果对服务的改变需要由中央团队批准,或者有一个集中的团队来开发共享库或服务,这就会减慢开发速度,减少各个团队的自主性。
  • 事务管理:微服务应该能够处理其数据,以确保松散耦合。然而,在一些业务场景中,你可能会被诱惑去实现跨越多个服务的分布式事务。这种方法会导致紧耦合,因为服务必须协调以确保数据的一致性。
  • 服务网的误用:Istio、Linkerd或Consul等服务网格为微服务提供了强大的功能,如弹性通信、服务发现和可观察性。然而,它们不应该被用来实现业务逻辑或导致紧密耦合的复杂通信模式。
  • 避免隐藏的耦合:有时,耦合不是立即可见的。例如,拥有依赖另一个服务的特定行为的服务,如事件的顺序或响应的时间,会导致一种时间耦合,从而使服务难以独立改变。
  • 标准化的误用:虽然拥有标准很重要,但过度的标准化会限制服务团队的灵活性和独立性,可能会减缓开发和创新。确保你的标准提供一个广泛的框架,促进最佳实践,但不限制团队根据他们的具体需求做出决定的能力。
  • 忽视非功能需求:像可扩展性、弹性和容错性这样的方面不应该是事后的考虑。如果在最初的设计中没有考虑这些问题,系统可能会以集中处理这些问题的方式发展,从而导致一种耦合形式,并可能导致分布式单体。
  • 避免过度使用中间件:中间件解决方案(如ESB、BPM)有时可以用来处理服务之间的复杂协调,但这可能导致所有服务都依赖的集中式单体。通常,每个服务处理自己的业务流程会更好。
  • 谨防微服务版本化:在改变服务时保持向后的兼容性是很重要的。如果所有的服务必须同时升级以适应一个服务的新版本,你就进入了分布式单体领域。

让我们来探讨解决这些问题的潜在方案

  • 同步通信:利用异步通信模式,如事件驱动架构或消息队列,以避免时间上的耦合。
  • 高代码级别的耦合:当必须使用共享库时,确保它是向后兼容的,并且是有版本的。
  • 共享数据库:每个服务应该拥有自己的数据库,以确保信息隐藏和解耦。如果数据共享是必要的,就实施数据复制或同步机制。
  • 不适当的服务边界:使用领域驱动设计(DDD),使你的服务与业务领域保持一致,确保每个微服务与业务子领域相对应。
  • 共享部署:确保服务可以独立部署。实施自动化和容器化技术以简化部署过程。一个好的CI/CD管道在这里是至关重要的。
  • 缺少模块化:确保每个服务都是模块化的,可以独立发展。服务应该有一个单一的责任和一个有边界的环境。
  • 集中式治理:采用分散的治理模式,允许各个团队对其服务进行决策,促进自主性和交付速度。
  • 交易管理:避免分布式交易。拥抱像Saga或基于Choreography的工作流的模式来管理跨越多个服务的交易。这些模式确保最终的一致性,同时最大限度地减少紧密耦合。
  • 服务网格的滥用:利用Istio或Linkerd等服务网格,主要是为了运维操作上的考虑,如流量管理、可观察性和安全性。避免依赖服务网格来处理业务逻辑或复杂的通信模式。
  • 避免隐藏的耦合:谨慎对待隐藏的时间耦合,即一个服务的行为取决于另一个服务操作的时间或顺序。分析通信模式、依赖关系和潜在的故障点,以减少无意的紧耦合的风险。异步通信和适当的文档可以提供帮助。
  • 标准化的误用:设定标准,促进最佳实践,确保系统的凝聚力,但允许各个团队灵活地根据他们的具体需求做出决定。
  • 忽视非功能需求:从一开始就考虑到可扩展性、容错性和复原力。让它们成为你的系统架构的一个组成部分。集成诸如负载平衡、断路器和重试等机制,以确保系统的稳定性和响应性。
  • 避免过度使用中间件:每个服务都应该是独立的,并管理自己的业务流程。避免过度使用中间件来处理服务间的协调。
  • 谨防微服务版本化:对你的服务使用版本管理,并确保向后兼容。避免强迫所有服务同时升级新的版本。

构建和维护一个成功的微服务架构需要仔细关注,以避免落入分布式单体的陷阱。保持警惕,审查你的架构,并授权个人团队做出明智的决定,同时牢记服务独立性和灵活性的最终目标。