编者:本系列共7篇文章,现已全部发布:
还可以下载电子书,其中收录了完整系列文章以及使用NGINX Plus实施微服务的相关信息:《微服务:从设计到部署》。另请参阅我们的微服务参考架构系列文章和微服务解决方案页面。
这是我撰写的微服务应用构建系列的第7篇,也是最后一篇。该系列的第一篇文章介绍了微服务架构模式,并探讨了使用微服务的优势和不足。后续文章分别探讨了微服务架构的不同方面:使用API网关<.htmla>、进程间通信、服务发现、事件驱动的数据管理以及微服务部署。本文将探讨如何将单体应用迁移到微服务架构。
希望本系列文章能帮助您充分了解微服务架构、其优势与不足,以及适用场景。微服务架构或许适合贵组织。
不过,您很可能正在开发一个庞大而复杂的单体应用。日常的应用开发和部署过程缓慢而痛苦,微服务似乎遥不可及。好在有一些策略可以帮助您摆脱“单体地狱”。本文将介绍如何以渐进方式将单体应用重构为一组微服务。
微服务重构概述
将单体应用转变为微服务的过程是应用现代化的一种形式。开发人员数十年来一直在开展这项工作。因此,在将应用重构为微服务时,可以借鉴其中的一些思路。
不应采用的一种策略是“大爆炸式”重写,即投入全部开发资源,从头构建一个基于微服务的新应用。虽然这种做法听起来很有吸引力,但风险极高,很可能以失败告终。正如Martin Fowler据称曾说过的:“大爆炸式重写唯一能保证的,就是一场大爆炸!”
不要采用“大爆炸式”重写,而应逐步重构单体应用。逐步构建一个由微服务组成的新应用,并让它与单体应用同时运行。随着时间推移,由单体应用实现的功能会不断减少,直至单体应用完全消失,或仅成为另一个微服务。这种策略就像以70 mph的速度在高速公路上行驶时维修汽车,虽然颇具挑战,但风险远低于尝试“大爆炸式”重写。
Martin Fowler将这种应用现代化策略称为“绞杀者应用”。这个名称源自热带雨林中的绞杀藤(又称绞杀榕)。绞杀藤环绕树木生长,以获得森林冠层上方的阳光。有时,树木会死亡,只留下树形的藤蔓。应用现代化也遵循同样的模式。我们将在旧版应用周围构建一个由微服务组成的新应用,最终取代旧版应用。

下面来看看实现这一目标的不同策略。
策略1:停止深挖
“坑洞法则”指出,一旦身陷坑中,就应停止继续挖掘。当单体应用变得难以管理时,这是一条非常实用的建议。换言之,不应再扩大单体应用。这意味着在实现新功能时,不应继续向单体应用添加代码。此策略的核心思路是将新代码放入独立的微服务中。下图展示了采用此方法后的系统架构。

除了新服务和旧版单体应用外,还有另外两个组件。第一个是请求路由器,用于处理传入的HTTP请求。它类似于早期文章<.htmla>中介绍的API网关。路由器将与新功能对应的请求发送到新服务,并将旧版请求路由到单体应用。
另一个组件是粘合代码,用于将服务与单体应用集成。服务很少独立存在,通常需要访问单体应用所拥有的数据。粘合代码可位于单体应用、服务或两者之中,负责数据集成。服务使用粘合代码读取和写入单体应用所拥有的数据。
服务可通过以下三种策略访问单体应用的数据:
- 调用单体应用提供的远程API
- 直接访问单体应用的数据库
- 维护自己的数据副本,并与单体应用的数据库保持同步
这种粘合代码有时称为防腐层。这是因为服务拥有自己纯净的领域模型,而粘合代码可以防止该模型受到遗留单体应用领域模型中概念的污染。粘合代码负责在这两种不同的模型之间进行转换。“防腐层”一词最早出现在Eric Evans的必读著作《领域驱动设计》中,随后又在一篇白皮书中得到进一步阐述。开发防腐层并非易事。但如果想通过逐步扩展摆脱单体架构困境,创建防腐层至关重要。
将新功能实现为轻量级服务有多项优势。这样可以避免单体应用变得更加难以管理。该服务可以独立于单体应用进行开发、部署和扩展。每创建一个新服务,都能获得微服务架构带来的优势。
但是,这种方法并不能解决单体应用存在的问题。要解决这些问题,需要拆分单体应用。下面介绍几种拆分策略。
策略2:拆分前端和后端
缩减单体应用的一种策略是,将表示层与业务逻辑层和数据访问层分离。典型的企业应用至少包含以下三类不同的组件:
- 表示层:处理HTTP请求并实现(REST)API或基于HTML的Web UI的组件。在具有复杂用户界面的应用中,表示层通常包含大量代码。
- 业务逻辑层:构成应用程序核心并实现业务规则的组件。
- 数据访问层:用于访问数据库和消息代理等基础设施组件的组件。
表示逻辑通常与业务逻辑和数据访问逻辑明确分离。业务层具有一个由一个或多个外观组成的粗粒度API,这些外观封装了业务逻辑组件。此API构成了一个天然的拆分边界,可沿此边界将单体应用拆分为两个较小的应用。一个应用包含表示层,另一个应用包含业务逻辑和数据访问逻辑。拆分后,表示逻辑应用会远程调用业务逻辑应用。下图显示了重构前后的架构。

以这种方式拆分单体应用主要有两项优势。这样可以分别独立开发、部署和扩展这两个应用。尤其是,表现层开发人员可以快速迭代用户界面,并轻松开展A/B测试。此方法的另一项优势是会公开一个远程API,供所开发的微服务调用。
不过,这种策略只能解决部分问题。其中一个或两个应用程序很可能仍是难以管理的单体应用。需要采用第三种策略来消除剩余的一个或多个单体应用。
策略3:提取服务
第三种重构策略是将单体应用中的现有模块转变为独立的微服务。每提取一个模块并将其转变为服务,单体应用就会相应缩小。转换足够多的模块后,单体应用将不再是问题:它要么完全消失,要么缩小到足以作为一个普通服务存在。
确定模块服务化的优先级
一个庞大而复杂的单体应用由数十乃至数百个模块组成,所有模块都可以考虑拆分出来。确定优先转换哪些模块通常并不容易。一个可行的方法是先从几个易于拆分的模块入手。这样既能积累微服务方面的整体经验,也能熟悉具体的拆分流程。之后,再拆分能带来更大收益的模块。
将模块转换为服务通常很耗时。应根据预期收益对模块进行优先级排序。通常,提取频繁变更的模块更有利。将模块转换为服务后,便可独立于单体应用进行开发和部署,从而加快开发速度。
将资源需求与单体应用其他部分存在显著差异的模块提取出来也很有帮助。例如,可以将包含内存数据库的模块转换为服务,然后将其部署到配备大容量内存的主机上。同样,也可以考虑提取实现计算密集型算法的模块,以便将相应服务部署到配备大量CPU的主机上。将具有特定资源需求的模块转换为服务,可以让应用更易于扩展。
在确定要提取哪些模块时,可以寻找现有的粗粒度边界(也称为接缝)。借助这些边界,可以更轻松地以更低成本将模块转化为服务。例如,某个模块仅通过异步消息与应用程序的其余部分通信,这就是一种粗粒度边界。将此类模块转化为微服务通常相对简单,成本也较低。
如何提取模块
提取模块的第一步,是在模块与单体应用之间定义一个粗粒度接口。该接口很可能是双向API,因为单体应用需要使用服务所拥有的数据,服务也同样需要使用单体应用的数据。由于模块与应用程序其余部分之间存在错综复杂的依赖关系和细粒度交互模式,实现此类API通常颇具挑战。使用领域模型模式实现的业务逻辑尤其难以重构,因为领域模型类之间存在大量关联。通常需要对代码进行重大修改,才能解除这些依赖关系。下图展示了这一重构过程。
实现粗粒度接口后,便可将该模块转变为独立服务。为此,必须编写代码,使单体应用与服务能够通过采用进程间通信(IPC)机制的API进行通信。下图展示了重构前、重构期间和重构后的架构。

在此示例中,Module Z是待提取的候选模块。其组件由Module X使用,而Module Z使用Module Y。重构的第一步是定义一对粗粒度API。第一个接口是入站接口,供Module X调用Module Z。第二个接口是出站接口,供Module Z调用Module Y。
第二个重构步骤是将该模块转换为独立服务。入站和出站接口由使用IPC机制的代码实现。通常需要将模块Z与一个微服务底座框架结合起来构建该服务,由该框架处理服务发现等横切关注点。
提取模块后,您将获得另一个可独立于单体应用和其他服务进行开发、部署和扩展的服务。甚至可以从头重写该服务;在这种情况下,用于将该服务与单体应用集成的 API 代码会成为防腐层,负责在两个领域模型之间进行转换。每提取一个服务,就向微服务架构更进一步。随着时间的推移,单体应用将逐渐缩小,微服务的数量则会不断增加。
总结
将现有应用迁移到微服务是一种应用现代化方式。不应通过从头重写应用来迁移到微服务,而应逐步将应用重构为一组微服务。可以采用三种策略:以微服务形式实现新功能;将表示层组件与业务和数据访问组件分离;将单体应用中的现有模块转换为服务。随着时间的推移,微服务数量将不断增加,开发团队的敏捷性和开发速度也将随之提升。
编辑注:本系列共7篇文章,现已全部发布:
您还可以下载电子书,其中收录了完整系列文章,以及使用NGINX Plus实施微服务的相关信息:《微服务:从设计到部署》。另请参阅我们的微服务参考架构系列文章和微服务解决方案页面。
客座博主Chris Richardson是最初的CloudFoundry.com的创始人。该平台是早期面向Amazon EC2的Java平台即服务(PaaS)。目前,他为各组织提供咨询,帮助改进应用开发和部署方式。他还经常在以下网站撰写有关微服务的博客:https://microservices.io
相关博客文章
NGINX Plus R26 正式发布
NGINX Plus R26通过缓存JSON Web密钥集加快JWT验证,并强化TLS握手,从而提升安全性。NGINX JavaScript的新功能包括增强对异步函数的支持,以及实现WebCrypto API。
使用NGINX App Protect DoS保护gRPC应用,抵御严重的DoS攻击
gRPC具备开发人员设计和部署现代应用所需的速度与灵活性,但其开放性也使其极易受到第7层DoS攻击。NGINX App Protect DoS可保护gRPC应用免受此类破坏性攻击。
Ingress Controller 选型指南(第 2 部分):风险与面向未来的规划
了解选择错误的Ingress控制器可能带来的风险,以及如何从关键方面着手,让您的选择更好地适应未来需求。
NGINX Plus R19 正式发布
NGINX Plus R19扩展了监控功能,不仅提供更多指标,还新增了指标分析方式;增加试运行模式,用于测试限速效果;增强键值存储,以支持IP地址范围;并使限速更加动态。
NGINX现已正式加入F5
NGINX现已正式成为F5的一部分。Gus Robertson现任F5 NGINX业务部门负责人。他阐述了两家公司在人才、文化和愿景方面融合的价值,以及双方将如何携手增强NGINX的开源产品和商业产品。
