「Chris Richardson 微服务系列」使用微服务重构单体应用

编者的话|本文来自 Nginx 官方博客,是「Chris Richardson 微服务」系列的最后一篇。第一篇介绍了微服务架构模块,并且讨论了使用微服务的优缺点。随后的文章讨论了微服务的不同方面,包括使用 API 网关、进程间通讯、服务发现、事件驱动的数据管理,以及部署微服务。本篇将讨论从单体应用迁移到微服务的策略。

作者介绍:Chris Richardson,是世界著名的软件大师,经典技术著作《POJOS IN ACTION》一书的作者,也是 cloudfoundry.com 最初的创始人,Chris Richardson 与 Martin Fowler、Sam Newman、Adrian Cockcroft 等并称为世界十大软件架构师。

Chris Richardson 所著所有文章已独家授权 DaoCloud 翻译并刊载。

本系列包含 7 篇文章,介绍了微服务的设计、构建和部署,并与传统的单体架构进行了比较。本系列将分析微服务架构的各种因素,你也将了解微服务架构模型的优劣、是否适合你的项目,以及如何应用。

Chris Richardson 微服务系列全 7 篇:

  1. 微服务架构的优势与不足
  2. 使用 API 网关构建微服务
  3. 深入微服务架构的进程间通信
  4. 服务发现的可行方案以及实践案例
  5. 微服务的事件驱动数据管理
  6. 选择微服务部署策略
  7. 将单体应用改造为微服务

本期内容:

这一系列文章有助于读者更好地了解微服务架构及其优缺点,以及何时使用这一架构。微服务架构可能适合你的应用。

然而,你仍有很大可能去开发大型、复杂的单体应用,每天的开发和部署缓慢且痛苦。微服务看起来是遥远的「天堂」。幸运的是,你可以采用一些策略来摆脱单体应用的痛苦。在本篇文章中,我会介绍如何逐步把单体应用重构为微服务。

概述

将单体应用转变为微服务的过程也是将应用现代化的过程,数十年来开发者们一直致力于此。因此,当把应用重构为微服务的时候,我们可以借鉴其中的理念。

首先不要大规模地重写代码。大规模重写代码意味着你需要集中全部开发力量、从头构建全新的基于微服务的应用;听起来吸引人,但是充满风险,有可能以失败告终。正如 Martin Fowler 所言,“大规模重写唯一能够保证的只有大规模!”

相反,应当采取逐步重构单体应用的策略。逐步构建一个由微服务构成的应用,与单体应用并行运行;随着时间推移,原先由单体应用实现的功能不断收缩,最后或者完全消失,或者转变为微服务。这一方法虽然充满挑战,但风险远小于大规模重写代码。

Martin Fowler 将这一应用现代化的策略称为“杀手应用”。这一名称源自热带雨林中的杀手藤。杀手藤附生于树,直达树冠上方;树死后,留下树状的藤蔓。应用的现代化也是遵循这一模式。微服务构成的新应用围绕着遗留应用,后者最终完全不复存在。

接下来了解不同的实现策略。

策略一:停止挖坑

挖坑第一法则指出,如果发现自己掉坑里,马上停止。这一忠告也适用于难以管理的单体应用。换句话说,应该停止让单体应用继续变大,也就是在实现新功能的时候,不应该再增加代码。相反,这一策略的理念在于,把这部分新代码开发称为独立的微服务。下图展示了采用此方法的系统架构。

除了新服务和遗留应用,这个系统还包括另外两个组件。其一是请求路由,处理传入(HTTP)请求,与之前文章中描述的 API 网关类似。路由将与新功能对应的请求发送到新服务,将遗留请求发送到已有的单体应用。

另一组件是胶水代码(glue code),负责集成微服务与单体应用。微服务很少孤立存在,通常需要访问单体应用拥有的数据。胶水代码存在于单体应用或微服务中,或者两者兼有,负责数据集成。微服务使用胶水代码来读取和写入单体应用拥有的数据。

微服务可以通过以下三种方式访问单体应用的数据:

  • 调用由单体应用提供的远程 API
  • 直接访问单体应用的数据
  • 维护一份数据拷贝,与单体应用的数据库保持同步

胶水代码也被称作防崩溃层(anti-corruption layer)。对于拥有自己全新领域模型的微服务,胶水代码能够阻止其受到遗留单体应用的领域模型的污染,并且为这两种模型提供转换。防崩溃层这一术语最早出现于 Eric Evans 撰写的必读书 Domain Driven Design 中,并被提炼成为白皮书。开发防崩溃层是项不平凡的工作,要想远离单体应用的泥淖,创建防崩溃层必不可少。

以轻量级微服务的方式实现新功能有诸多优点。它能够防止单体应用变得不可管理。微服务能够独立于单体服务进行开发、部署和扩展。采用微服务能让开发者切身感受其好处。

然而,这一方法并没有解决单体应用的问题。要想解决这些问题,需要分解单体应用。

策略二:拆分前端和后端

缩小单体应用的策略之一是将表示层(presentation layer)与业务逻辑和数据访问层分离。典型的企业应用包括至少三类组件:

  • 表示层:处理 HTTP 请求并实现 (REST)API 或基于 HTML 的 Web UI。对于包含复杂用户接口的应用,表示层往往是代码的实体部分。
  • 业务逻辑层:应用的核心,实现业务逻辑
  • 数据访问层:访问诸如数据库和消息代理这样的基础架构组件

在表示逻辑与业务和数据访问逻辑之间,有着清晰的间隔。业务层的粗粒度的 API 由若干方面组成,内部封装业务逻辑组件。这个 API 是一道天然分界线,将单体应用分割成两个较小的应用。一个应用包含表示层,另一个应用包含业务和数据访问逻辑。拆分后,表示逻辑应用对业务逻辑应用远程调用。下图展示了重构前后的构架。

以这种方式切割单体应用有两大好处。首先,它使得这两个应用的开发、部署和扩展用各自独立。尤其是,它使得表示层的开发人员能够快速迭代用户接口,轻松进行 A|B 测试。其次,它暴露了远程 API,能够被微服务调用。

这种策略也只是部分解决方案,很有可能其中一个应用或两个应用变成难以管理的单体应用。这时需要使用第三种策略来消除剩余的单体应用。

策略三:提取微服务

第三种重构策略是将单体应用内现有的模块转变为独立的微服务。每当提取模块并将其转化为服务,单体应用就会收缩。一旦转化了足够的模块,单体应用也不再是问题,它或者彻底消失,或者小到成为另一个微服务。

为需要转化为微服务的模块设置优先级

大型、复杂的单体应用由数十甚至数百个模块组成,每个都是提取的对象。要弄清楚哪些模块首先被转化,往往具有挑战性。从易于提取的模块开始是个好方法,它能让开发者熟悉微服务和提取过程。然后就应该转化能从中获益最多的模块。

鉴于把模块转变为微服务非常耗时,一般会根据获益程度来给模块排序。从频繁更改的模块开始会让用户收获不菲。一旦把模块转化为微服务,也就能独立开发和部署,从而加速开发进度。

将资源需求大不相同的模块优先转化,也颇有好处。例如,把内存数据库模块转化为微服务,能够被部署在大内存主机上。同样,将实现计算算法的模块提取出来也是非常值得,这一微服务能够部署在拥有大量 CPU 的主机上。通过将对资源有着特殊需求的模块转变为微服务,应用能够易于扩展。

找出哪些模块需要优先提取后,找出现有粗粒度的边界(即分界线)也大有裨益。这些边界让模块转变为微服务更加简单、省力。例如,通过异步消息与应用的其它部分通信的模块能够相对省力、简便地转化为微服务。

如何提取模块

模块提取的第一步是定义模块和单体应用间的粒度接口。由于单体应用和微服务互相需要对方拥有的数据,因此更像是双向 API。由于模块和应用其它部分之间存在着互相依赖和细粒度的交互模型,因此实现这样的 API 充满挑战。对于重构微服务,通过领域模型实现的业务逻辑尤为挑战,开发人员需要大刀阔斧地修改代码来打破这些依赖。

粗粒度接口一旦完成,模块也就变成了独立的微服务。要做到这一点,开发人员必须编写代码,能够让单体应用和微服务通过 API 通信,API 使用进程间通信(IPC)机制。 下图展示了重构前、重构中和重构后的不同架构。

在图片中,模块 Z 将要被重构,它用到了模块 Y 的组件,同时它的组件被模块 X 使用。重构的第一步就是定义一对粗粒度 API。第一个接口是模块 X 使用的对内接口,用来唤醒模块 Z。第二个接口是模块 Z 使用的对外接口,唤醒模块 Y。

重构的第二步则是把模块转变为独立的微服务。对内和对外接口通过 IPC 机制的代码实现,开发人员可能只需要将模块 Z 与微服务支撑框架(Microservice Chassis framework)组合起来构建微服务。微服务支撑框架处理与割接相关的问题,比如服务发现。

一旦将模块提取完毕,相当于得到一个微服务,能够独立于单体应用和其它微服务进行开发、部署和扩展。如果要从头重写微服务代码,集成微服务和单体应用的 API 代码会成为这两个领域模型之间的防崩溃层。每重构一个组件,就向着微服务的方向又迈进了一步。随着时间推移,单体应用逐渐消失,微服务则越来越多。

总结

将现有单体应用迁移到微服务架构是应用的现代化。实现这一结果并不需要从头重写代码,相反,只需要渐进式地将应用重构为一组微服务。其中有三种策略可以采纳:使用微服务实现新功能、将表示组件与业务和数据访问组件拆分、以及将现有应用内的模块转变为微服务。随着时间推移,大量微服务形成,团队的敏捷和效率也会提升。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容