你的团队决定是时候摆脱那个旧的、笨重的单体了,它运行得很好,但是单体已经变得如此之大,以至于你花费更多的精力来维护它而不是添加功能。这里有 12 个技巧,可帮助您尽可能顺利地过渡到微服务。
#1 确保你知道你在做什么
重写从来都不是一件容易的事,但是从单体应用到微服务,你改变的不仅仅是编码方式;你正在改变公司的运营模式。你不仅需要学习一个新的、更复杂的技术栈,管理层还需要调整工作文化,将人员重组为更小的跨职能团队。
如何最好地重组团队和公司是值得单独发帖的主题。在本文中,我想重点介绍迁移的技术方面。
首先,在开始之前尽可能多地研究采用微服务所涉及的权衡是很重要的。您希望绝对确定微服务(而不是其他替代解决方案,例如模块化单体)是适合您的解决方案。
首先学习有关微服务架构的所有知识,并查看一些示例项目以了解其工作原理。这里有些例子
#2 制定计划
拆除单体应用需要大量准备工作,因为旧系统必须在过渡期间保持运行。
迁移步骤可以通过工单进行跟踪,并像任何其他任务一样在每个 sprint 中进行。这不仅有助于获得动力(实际上有朝一日实现迁移),而且让业务所有者了解团队如何计划实施如此大的变化。
在计划期间,您必须:
解开单体内部的依赖关系。
确定所需的微服务。
为微服务设计数据模型。
开发一种在单体和微服务数据库之间迁移和同步数据的方法。
设计 API 并计划向后兼容。
捕获单体应用的基线性能。
为新系统的可用性和性能设定目标。
除非您从一个相当简单的单体架构迁移,否则您将需要高级技术,例如领域驱动设计 (DDD)。
#3 把所有东西都放在一个 monorepo 中
当你分解单体时,大量代码将从单体中移出并转移到新的微服务中。monorepo可帮助您跟踪这些类型的更改。此外,将所有东西放在一个地方可以帮助您更快地从故障中恢复。
您的单体应用很可能已经包含在一个存储库中。因此,只需为微服务创建新文件夹即可。
#4 使用共享 CI 管道
在开发过程中,您不仅会不断推出新的微服务,还会重新部署单体应用。这个过程越快、越轻松,你的进步就越快。设置持续集成和交付(CI/CD) 以自动测试和部署代码。
如果您使用 monorepo 进行开发,则必须牢记以下几点:
通过启用基于更改的执行或使用单存储库感知构建工具(例如Bazel或Pants )来保持管道快速。通过仅对更新的代码运行更改,这将使您的管道更加高效。
配置多个促销,每个微服务一个,一个单体。使用这些促销活动进行持续部署。
配置测试报告以快速发现和排除故障。
#5 确保你有足够的测试
当我们确定代码没有回归时,重构会更加令人满意和有效。自动化测试让您有信心持续发布单体更新。
一个很好的起点是测试金字塔。您将需要大量的单元测试、一些集成测试和一些验收测试。
旨在像在持续集成管道中一样经常在本地开发机器上运行测试。
#6 安装 API 网关或 HTTP 反向代理
随着微服务的部署,您必须隔离传入流量。迁移的功能由新服务提供,而尚未准备好的功能由单体提供。
有几种路由请求的方法,具体取决于它们的性质:
API 网关允许您根据经过身份验证的用户、cookie、功能标志或 URI 模式等条件转发 API 调用。
HTTP 反向代理的作用相同,但针对的是 HTTP 请求。在大多数情况下,单体实现了 UI,因此大多数流量都会流向那里,至少一开始是这样。
使用 API 网关和 HTTP 反向代理将请求路由到适当的端点。您可以在非常细粒度的级别上在单体和微服务之间切换。
迁移完成后,网关和代理将保留——它们是任何微服务应用程序的标准组件,因为它们提供转发和负载平衡。如果服务出现故障,它们还可以充当断路器。
#7 考虑一体成型的模式
好的,这仅适用于您计划将容器或 Kubernetes 用于微服务的情况。在这种情况下,容器化可以帮助您实现基础架构的同质化。一体式整体模式包括在 Docker 等容器内运行整体。
如果您以前从未使用过容器,那么这是熟悉该技术的好机会。这样一来,您就离了解 Kubernetes 更近了一步。要学习的东西很多,所以要为陡峭的学习曲线做好计划:
了解 Docker 和容器。
在容器中运行你的单体应用。
在容器中开发和运行您的微服务。
完成迁移并掌握容器后,了解 Kubernetes。
随着工作的进展,您可以扩展微服务并逐渐将流量转移到它们。
容器化单体应用是标准化部署的一种方式,也是学习 Kubernetes 的绝佳第一步。
#8 热身于变化
习惯微服务需要时间,所以最好从小处着手,为新范式做好准备。留出足够的时间让每个人都进入正确的心态、提高技能并从错误中吸取教训,而不会受到最后期限的压力。
在这些初步的初步步骤中,您将学到很多关于分布式计算的知识。您必须处理云 SLA,为您自己的服务设置 SLA,实施监控和警报,定义跨团队通信的渠道,并决定部署策略。
选择一些容易开始的东西,比如与单体架构的其余部分几乎没有重叠的边缘服务。例如,您可以先构建身份验证微服务并路由登录请求。
选择一些容易开始的东西,比如简单的边缘服务。
#9 使用功能标志
功能标志是一种无需重新部署即可更改系统功能的软件技术。您可以使用功能标志在迁移时打开和关闭部分单体应用、试验替代配置或运行 A/B 测试。
启用功能标志的迁移的典型工作流程是:
确定要迁移到微服务的单体功能的一部分。
用功能标志包装功能。重新部署单体。
构建和部署微服务。
测试微服务。
一旦满意,通过关闭该功能来禁用单体应用上的该功能。
重复直到迁移完成。
因为功能标志允许我们将非活动代码部署到生产环境并随时切换它,所以我们可以将功能发布与实际部署分离。这为开发人员提供了极大的灵活性和控制力。
#10 模块化单体
如果你的单体应用是一堆代码,那么一旦迁移完成,你很可能会得到一堆分布式代码。就像在全面翻新之前收拾房子一样,模块化整体结构是必要的准备步骤。
模块化单体是一种软件开发模式,由独立且可互换的垂直堆叠模块组成。与模块化单体相反的是经典的 N 层或分层单体。
分层的单体很难解开——代码往往有太多的依赖关系(有时是循环的),使得更改难以实现。
模块化单体是微服务的下一个最佳选择,也是迈向微服务的垫脚石。规则是模块只能通过公共 API 进行通信,默认情况下一切都是私有的。因此,代码的交织更少,关系易于识别,依赖关系清晰。
有两种模式可以帮助你重构单体:Strangler Fig 和 Anticorruption Layer。
Strangler Fig
在Strangler Fig模式中,我们将整体重构从边缘到中心。我们在边缘咀嚼,逐步重写孤立的功能,直到整体重做。
模块之间的调用通过“扼杀外观”进行路由,该外观模拟和解释遗留代码的输入和输出。一点一点地,模块被创建并慢慢取代旧的单体。
单体是一次模块化的。最终,旧的巨石消失了,取而代之的是新的。
反腐败层模式
您会发现,在某些情况下,当您重构整体时,一个模块中的更改会传播到其他模块。为了解决这个问题,您可以在快速变化的模块之间创建一个转换层。此防腐败层可防止一个模块中的更改影响其余模块。
反腐败层通过转换模块和单体之间的调用来防止更改传播。
#11 解耦数据
超级强大的微服务使您能够随时部署任何微服务,而与其他微服务几乎没有协调。这就是为什么必须不惜一切代价避免数据耦合的原因,因为它会在服务之间产生依赖关系。每个微服务都必须有一个私有且独立的数据库。
意识到您必须将单体应用的共享数据库非规范化为(通常是冗余的)较小的数据库,这可能会令人震惊。但数据局部性最终将让微服务自主工作。
上图是将数据解耦到独立的数据库中。
解耦后,您必须安装机制以在转换过程中保持新旧数据同步。例如,您可以设置数据镜像服务或更改代码,以便将事务写入两组数据库。
在开发过程中使用数据复制来保持表同步。
#12 添加可观察性
新系统必须比旧系统更快、性能更高、可扩展性更强。否则,为什么要用微服务呢?
您需要一个基线来比较旧的和新的。在开始迁移之前,请确保您有良好的指标和日志可用。安装一些集中式日志记录和监控服务可能是一个好主意,因为它是任何微服务应用程序可观察性的关键组件。
结论
微服务之旅绝非易事。但我希望通过这些提示,您可以节省一些时间和挫败感。
请记住以小增量进行迭代,利用 CI/CD 来保证单体应用程序正在接受回归测试,并将所有内容保存在一个存储库中,以便在出现问题时随时回退。