读后感之《从单体应用到微服务》02

原文转自我自己的个人公众号:https://mp.weixin.qq.com/s/ctxKoEey3tbTwaoMpCK_xw

由于我是从公众号上直接复制粘贴过来的,排版上可能有问题。推荐使用上方连接查看原文。

 

简介:原书名字是《Monolith To Microservices》,是大神Sam Newman的新书,目前还没有中文版本。原本是想写一个简短的读后感的,但是写着写着,发现书中的内容真的是太经典了,浅尝辄止的描述完全不能体现本书的价值。于是就改成了用我自己的语言对书中每一章的内容进行了精炼。因此这个读后感也可以作为原书的精简版来看,只不过用的是我自己的语言总结的。也是由于这个原因,这篇文章越写字数越多,最后接近三万字,花费的时间也很多。为了便于阅读,分成4部分来发。

注:本文中的图片截自原书

 

第三章、拆分单体应用

        在第二章中,书中重点讨论了在什么情况下应该考虑使用微服务架构,或者说微服务架构对你来说是否是一个好主意。当我们确定要使用微服务架构时,却发现在我们的企业中存在大量不符合微服务架构要求的单体应用。甚至有些时候,一些应用对我们来说是个黑盒。这时应该如何对它们进行微服务改造呢?

        总体的原则是:逐步迁移。每一步都只做一点改进。

        首先要做的决策是,是否确定要对已存在的单体应用进行微服务拆分。有时候,如果单体应用是另一个厂商提供的,对你完全是个黑盒的应用,或者实现该单体应用的技术目前在企业内已经不存在了,这些情况改造会非常困难。这时你需要考虑是对其及逆行改造还是推翻重写。通常来说,推翻重写需要非常慎重的考虑,但现实中,这个决定往往做的太早了。大多数情况都应该考虑对单体应用进行迁移改造。这就需要一些模式的支撑。单体应用向微服务迁移主要分为代码迁移和数据迁移两部分,本章重点关注代码迁移,下一张讨论数据迁移模式。

模式:扼杀无花果应用

        这种方式得名于大神Martin Fowler,他有感于一种无花果,这种无花果的种子会落在树的上部分的树干上,然后慢慢下降到地面的树根上,逐步包裹原来的树,现有的树作为新无花果的支撑架构,最终原来的树会死掉、烂掉,新的无花果树会自给自足的生存下来。

        借鉴这个思想,在迁移时,用一个新系统将原来的单体系统包裹起来,两个系统共存,新系统逐步接管单体系统的能力分离出微服务应用,当能力迁移完成,丢弃掉旧的单体应用。这个模式主要分为三步,如下图:

        首先,识别单体应用中希望被迁移的部分;然后在一个微服务应用中实现这部分的功能;最后将这部分功能的流量从单体应用切换到微服务应用中。

        这个模式的好处是,即便将微服务应用部署到生产环境中,只要不切换流量,就不会对现有系统造成任何影响,即部署而不发布,这为充分的测试提供了条件。可以将流量拷贝一份给微服务应用,通过对比单体应用和微服务应用的处理过程和结果来判断功能是否已准备好替换。如果迁移的功能需要调用单体应用内的其它功能,最好的做法是在单体应用中提供API供微服务应用调用。

        这个模式的另一个好处是可回滚,在流量切到微服务应用后,发现实现的有问题,可以立刻再将流量切回单体应用。

        扼杀无花果模式通过新老系统并存,并且不需要对原有系统进行任何改造,非常适合对黑盒应用进行能力迁移。不过这个模式也有局限性,如果想要提取的功能在单体应用中被其功能大量引用,这种情况不太适合使用该模式。

        具体的实现步骤如下:

  1. 加入代理

    除非系统中已经有代理,否则应首先加入代理,这一步不会对原有系统造成任何影响,如下图:

     

    图片

  2. 迁移功能

    有了第一步的代理后,微服务应用就可以部署到生产环境了,但是不能发布,因此它应对所有请求返回501状态,表示功能尚未实现。一点一点的进行功能实现,在生产环境进行相应的测试。如下图:

     

  3. 重定向请求

    一旦功能迁移完毕,就可以将流量切换到微服务应用上了。如下图所示:

     

    图片

        整体过程如下图所示:

图片

模式:UI合成

        在单体应用向微服务应用迁移时,系统的UI界面可以作为迁移的契机。比如可以按照UI的模块进行功能迁移。具体的合成方式有:

  • 页面合成:将某个页面中的功能整体迁移。

  • 组件合成:将页面中某一部分的功能进行迁移。

  • 微前端:微前端的意图是在前端设计和开发时,也参照微服务架构的思想对前端进行拆分。这个话题比较大,后面再开一个专题详细介绍。

模式:根据抽象建立分支

        扼杀无花果模式是在应用边界提供代理的方式将请求路由到不同的后端负载。当我们希望进一步在应用内部进行拆分时,有些人会在另一个代码分支中进行开发,当开发好后再合并到主干上。这样做的风险是开发新功能可能会跨越多个主干版本,分支的声明周期越长,跨越的主干版本越多,在合并的时候风险就越大,并且这样做违背了持续集成的原则。这时可以使用根据抽象建立分支的方式(实际应用中,建议优先使用“扼杀无花果模式”),该模式需要修改源代码,分为五个步骤(下图引自原书,用例是发票功能和支付功能调用通知功能,这里需要将通知功能拆分出去):

步骤1:创建一个抽象

在希望改变的代码之上建立一个抽象层

图片

步骤2:使用抽象

创建后抽象后,就需要改变原功能的客户端,让它们改用第一步建立的抽象进行调用。

图片

步骤3:创建一个新的实现

接下来就可以在代码中加入新的实现,这个新的实现可以调用另一个微服务。尽管目前代码库中有两个实现,但是同一时刻我们只允许其中一个生效。也就是说,在新的实现开发过程中,我们都让原有的实现生效。这里可以使用一个配置项来做开关。

图片

步骤4:切换实现

当我们的新实现编写完成后,就可以通过开关将原有的实现切换为新的实现了。

图片

这就像扼杀无花果模式一样,当流量都切换到新的实现上后,旧的实现就可以移除了。如果发现新实现有问题,可以通过开关再切回旧的实现。

步骤5:清理

当确定新    的实现没有问题,并将所有流量都切换到新实现上后,我们就可以溢出旧的实现了,这一步一定要做,要保证简洁性。我曾经参与的一个项目中,就因为大家不对废除的代码和配置进行清理,导致一段时间以后分不清哪些有用哪些没用了,尤其是配置属性。

图片

图片

通常这样就可以了,不过还可以进一步将抽象也清理掉,因为它同一时刻只会有一个实现,就是我们当前的新实现。

图片

这个模式有一个变种,可以设计一个自动切换方式,在验证阶段,将老的实现作为新实现的后备,当新实现出现问题时,自动切换到旧实现完成调用。

图片

模式:平行运行

        不同于上面提到的那两个模式,它们虽然有多个实现,但是同一时刻只会有一个生效。“平行运行”模式会让多个实现同时处理请求,老的实现将结果返回给调用方,同时将这个结果和新实现的结果进行比对,以此保证新实现的正确性。该模式通常应用于发布的新功能存在高风险的时候。例如在飞行控制系统中,同一个功能可能有多个实现同时接收请求,然后这些实现对结果进行比对,根据一个选举策略,选出最正确的一个结果返回。

        这个模式是一种“渐进式发布”,是“Dark Launching”发布的一种,意图是在客户无感知的前体现,将功能发不出去,页面显示上对客户隐藏,通过日志来收集功能的运行结果。该模式不同于“金丝雀”发布,“金丝雀”的目的是当出现问题的时候不至于影响太大。

        在对比结果的时候,不但需要对正确性进行校验,还需要对非功能性指标进行审查,比如各种性能指标。

        下图是书中的一个列子:

图片

模式:装饰合作者

        这个模式的原理类似于设计模式中的装饰模式,意图是在不改变原有实现的前提下对功能进行扩展。与装饰模式的不同在于,该模式主要关注点是在对结果的进一步处理。比如已存在一个单体应用,这时需要对单体应用的结果进行一些额外的处理,这时可以添加一个代理层,然后在返回结果之前,调用新的微服务完成功能扩展,然后将结果再返回给调用方。下图描述了为已存在的订单系统添加积分的场景:

图片

        如果使用了“扼杀无花果”模式,则代理就已经有了,只需要对代理进行一点调整就可以了。但这里需要注意的是,不能在代理中添加过多的业务逻辑,这样就违背了微服务的原则。

        应用该模式的时候,会出现这样一种情况,就是添加的微服务需要更多的参数,但单体引用的请求和响应中均不包含该参数,这时就需要新的微服务再通过单体应用的某个访问点去查找需要的参数,如下图所示:

       这样做的结果是形成了一个循环依赖,通常这种做法是不推荐的,当出现这种情况的时候,最好的解决方案是对单体应用进行一些修改,以提供新添加的微服务所需要的参数。所以在这种情况发生时,需要进行权衡,并且需要谨慎的分析是否应该使用该模式。

模式:变更数据获取

        为单体应用的结果进行扩展的另一种方法是获取数据库中变化的数据,然后对这些数据进行后续的处理。如下图所示:

图片

        在实现这个模式的时候有多种选择:

  • 数据库触发器:大部分关系型数据库都支持触发器,可以通过向数据库中添加触发器的方式,来将结果传递到新的微服务中完成后续功能。但过多的引入触发器会造成更多的维护问题,以及会使系统难以理解。因此需要非常保守的使用这种实现方式。

  • 轮询事务日志:几乎所有的主流事务数据库都使用事务日志来记录数据库的变化,这个日志通常是一个文件。目前有很多工具可以对事务文件进行解析,抽取其中的信息,这些工具通常是一些数据复制工具,其中一些可以将信息提交到MQ中,这样就可以流转到我们的微服务中进行处理。这种实现方式的问题是,每一种数据库的日志文件格式都不一样,但这种方式也是最平滑的一种方式,对原有的单体应用和数据库完全不需要任何修改。

  • 批量拷贝:最简单的方式是通过程序,定期的扫描数据库中变化的数据,然后进行后续的处理。这需要系统能够容忍延迟,并且能够访问数据库的schema信息,或者可以查看数据库中的元数据表,以此来查看数据库中的变化。但是通常需要人工的为记录添加时间戳,这个工作量可能会比较大。

        通过上面的描述,我们可以看出,这种方式在需要复制数据的时候比较有用,可以复用复制数据的工具来监控事务日志,以及在需要扩展单体应用但是不能使用“绞杀”或“装饰”来拦截原有请求,也不能修改数据库的时候这种方式会体现出优势。但也应该尽量避免使用该模式,原因是实现的方式存在许多缺点,会是整个系统变得复杂。

第三章总结

        本章提供了一些模式,可以用于增量的解耦一个已存在的单体应用,逐步迁移到微服务的实现。通常在实际工作中,需要将多种模式混合使用才能达到预定的目的,很少可以使用一个模式应用全部的场景。但在迁移过程中,不但应用需要解耦,数据库也需要相应的解耦,第四章会提供一些模式,用于对数据库进行增量的解耦。

 

更多内容请关注我的公众号:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值