代码共享的四种方法


在多个服务之间共享代码可能会成为项目团队争论的话题。服务涵括范围越大,关于如何在不同服务之间共享功能的争论就越激烈。

  • 一方面,开发人员认为 DRY(不要重复自己)是正确的做法。
  • 另一方则是 "无共享 "理念的支持者。

通常情况下,任何一方都无法提供明确的解决方案,因为在典型的企业应用程序中,可能存在许多不同类型的情况。很难提供一个能满足所有可能需求的通用解决方案。

本文将介绍四种不同的代码共享方法,并探讨每种方法的利弊得失。

1、代码复制
这是迄今为止在两个独立服务之间共享代码的最简单方法。

在这种方法中,只需将共享代码复制到每个服务中即可。

  • 虽然现在看来这可能是一种丑陋的黑客行为,但实际上这种技术在微服务架构的初期非常流行。
  • 界限上下文 的概念推动了整个 "无共享架构 "的发展,从而使这种技术大行其道。

不过,经过多年的发展,这种方法在很大程度上已经失宠。

更新很困难
为什么认为这种方法有问题?

试想一下,在共享或重复的代码中发现一个错误,或者需要对这部分代码进行重要修改。

  • 您需要更新包含复制代码的所有服务。无论你如何努力,都可能会错过某些服务的更新,从而导致问题。
  • 此外,您还必须彻底测试所有这些服务。

对于大多数新项目,应避免使用这种技术。
但是,您有可能会发现这种方法被用于现有的应用程序中,您可能需要以适当的方式来处理它。

banq注:
更新困难本身是一个伪命题,不存在的问题,因为如果不同代码所在的上下文不同,所以才复制代码,更新这些复制代码时,需要仔细考虑,这个上下文中的代码更新是否适用于另外一种上下文,这种推理本身就避免了很多BUG。多次思考也许让自己理解更深入。

2、共享库
使用共享库是跨多个服务重用代码的最常用技术。

共享库是一种外部工件,如包含通用功能的 JAR 文件、DLL 或 NPM 包。

我们的想法是,只需在任何需要它的服务中包含该共享库,并使用它提供的功能即可。

优势和问题

  • 这种方法的主要优点是在编译时将共享库与服务绑定,从而更容易在开发和测试过程中发现问题。
  • 这种方法的一个问题是共享库的原始大小。不过,在测试和版本控制方面也有更大的问题,这就需要对共享库的粒度进行权衡。

依赖性管理与变更控制
要决定采用哪种方法,您需要平衡依赖性管理和变更控制这两股力量。

如果共享库变得过大并被多个服务使用,那么一旦对库进行更改,即使更改与特定服务无关,每个服务也会受到影响。所谓影响,是指我们需要重建、重新测试和重新部署服务。此外,本地开发也变得更加困难,因为在同时修改共享库和服务时,需要频繁地进行构建和发布步骤。

另一方面,如果共享库的规模太小,就会使变更控制变得更容易。不过,最终可能会出现非常复杂的依赖关系矩阵。

一般来说,最好还是避免使用大型、粗粒度的共享库。此外,良好的版本策略也有助于管理共享库的变更范围。

3、共享服务
共享库包方法的主要替代方法是共享服务方法。

在这种策略中,用户将所有常用功能提取到共享服务中,然后将其作为独立流程部署。

独立部署
使用这种技术,你可以将通用功能放到具有独立部署路径的单独服务中,从而避免代码重用。

这种方法非常适合使用多种异质语言和平台的环境。此外,与共享库方法相比,对共享服务进行更改更加灵活。

潜在的权衡
这种方法有一些重要的权衡,例如:

  • 更改风险--对共享服务的任何错误更改都有可能导致依赖于该服务的所有其他服务瘫痪。这是因为更改只能在运行时进行,而不能在编译时进行。
  • 性能 - 在共享服务方法中,每个服务都可能需要通过网络进行服务间调用。这意味着性能会受到整体网络延迟的影响。
  • 可扩展性 - 共享服务还必须与依赖于共享服务的服务一起扩展。
  • 本地开发 - 对于共享服务,如果很难在开发机器上复制环境,本地开发就会相当困难。您必须与服务的不同消费者进行协调,还需要经历多个构建和部署活动。

4、边车Sidecars
应用程序通常由两类功能组成:
  • 领域
  • 平台

对于领域功能,我们通常会选择松耦合和高内聚。
但是,日志记录、监控、身份验证和断路器等操作功能采用高耦合实现会更好。

你不希望每个服务团队都重新发明操作功能。此外,整个组织或项目通常都需要标准化的解决方案。

要在多个服务中共享操作功能,Sidecar 模式是一个不错的选择。

实施 Sidecar
在此设置中,每个服务都包含 Sidecar 组件,负责操作功能。实现 Sidecar 有多种方法:

你可以使用 Kubernetes 等容器编排工具。在定义 Kubernetes Pod 时,规范包括两个容器--主应用容器和 Sidecar 容器。它们共享同一个网络命名空间,可以通过 localhost 进行通信。
Istio 或 Linkerd 等服务网格框架也提供了一种实现 Sidecar 模式的方法,即在每个服务实例中注入一个代理。

使用六边形架构
基本上,Sidecar 模式使用六边形架构的概念,将领域逻辑与技术或基础架构逻辑分离开来。
六边形架构是一种软件设计方法,它强调核心应用领域逻辑与外部组件(如日志、身份验证、监控等)之间的明确分离。
当然,重要的是要确保我们最终不会通过侧车Sidecar 重复使用领域功能。

Sidecar 模式的潜在风险
西德卡图案的主要风险在于,随着时间的推移,它可能会变得过于庞大或复杂。

因此可能会出现以下几个重要问题:

  • 由于复杂性增加,维护变得耗时
  • 大型Sidecar 可能会导致更多的资源消耗,并在容器化环境中产生资源争用问题

总结
每种代码共享方法都有其利弊和需要考虑的利弊权衡。

代码复制是最简单的代码共享方式,但在日后维护共享代码时会产生很多问题。

代码复制的一些大问题如下

  • 测试变得非常麻烦,因为必须对每个受影响的服务进行彻底测试
  • 找出影响足迹并不容易。很有可能遗漏关键服务,导致生产问题。
  • 旧的服务可能仍在使用它,最好将其视为技术债务,并通过其他机制加以修复。

随着服务足迹的成熟度提高,最好在共享库或共享服务方法之间做出选择。

随着服务足迹的成熟度不断提高,在共享库或共享服务方法之间做出选择始终是个好主意。

需要考虑以下几点

  • 共享库在编译时与服务绑定。这使得在开发过程中很容易发现潜在问题。
  • 不过,对于共享库,您需要在变更管理和依赖性管理之间做出权衡。
  • 另一方面,共享服务非常适合异构环境,在这种环境下可能无法使用共享库。
  • 不过,共享服务在运行时与其他服务绑定。这使它们面临变更风险、性能和可扩展性问题。

最后,还有一种使用 Sidecar 模式在服务间共享代码和功能的方法。
  • Sidecar 模式是在多个服务之间共享交叉关注点的好方法。
  • 您可以将其用于日志记录、监控和安全等各种功能。
  • 这些关注点在不同服务间紧密耦合的情况下表现良好,因为您希望在整个服务范围内提供一致性。