美文网首页
绞杀者模式 (一)

绞杀者模式 (一)

作者: anOnion | 来源:发表于2021-04-23 22:04 被阅读0次

背景

随着系统老化、开发工具逐渐落伍、bug 堆积,项目会变得及难维护。所以“腐烂”是所有遗产项目不可避免的一环。一般企业基本不会再去碰遗产项目,但是现代很多公司却喜欢另辟蹊径——每两三年用新技术重构一遍代码。众所周知,遗产代码很难改动,那它们这么做的自信来自何处?

故事要从一个叫 Martin Fowler 的老头说起。有一次,他在热带雨林里旅游,无意间发现了一种叫“绞杀者藤蔓”的植物:

绞杀者藤蔓

绞杀者藤蔓会沿着其他树干一路向上生长,以获取光线资源;随着藤蔓的不断生长,之前的寄生树会被这种植物整个包裹,并随着阳光资源的枯竭而死亡;最后树木腐烂而逝,但是原地却留下了一颗树状的巨大藤蔓。

绞杀过程

Martin 老头回去后就把这个故事写在了博客上,并提出了一个叫“绞杀者模式”的策略:通过逐步重构单体应用(而不是推到重来的方式),逐渐构建出一个新的应用程序。这也成为了后来开发人员对遗产系统进行现代化改造的基本方针。

绞杀者模式

讲完植物,我们从实现策略上探讨一下“如何绞杀”;绞杀的大体操作如下:

  1. 创建一个门面拦截后端遗产系统的请求
  2. 通过一定的规则,门面会将特定请求分别路由到新旧系统上——反向代理
  3. 保留遗产系统的原有功能,同时在新系统中重写旧的模块,并逐渐地将请求倾斜到新系统中;迁移过程中,由于门面的隔绝,消费者照常使用现有功能,并不会有任何后台重构的感知
  4. 完成迁移后,所有请求将路由到新系统上,遗产系统被“绞死”
绞杀者模式

绞杀者的好处是:保留了遗产系统的代码,以平滑迁移的形式朝着新应用迈进;这确保了每一步前进都有回退的机会。渐进迁移的时间可能会很长,甚至可以永久性的保持部分遗产系统的功能。

实操

OK,理论很简单,实操中会有各种各样的问题,重构过程通常要持续数月甚至数年。仅仅宣称“通过绞杀者模式可以实现系统迁移”,这种话是没用的:一想到你要动所有代码,大家立马就慌了,决策者(不管懂不懂技术)都不会如此草率地行动。所以最好能有更细节的拆分步骤。所谓的“拆分”,可以从表现层、服务层、持久层等等方面入手;只要记住从最简单的部分开始,当显现出价值后,就可以持续加速改造了。

通常来说,表现层的拆分是最简单的,比如将 JSP 或 tymeleaf 这种后端渲染框架拆成 restful api + react 的形式——动静分离——就可以很快出货。我们看看具体的改造过程。

反向代理

实现绞杀者模式的第一步自然是加个门面啦。我能想到最最最简单的门面就是:专职的反向代理工具 nginx 了。反向代理我之前写过一篇文章,大家可以点这里查看。务必确保团队成员已经有了最基础的重定向知识;第一步就遇到认知障碍,这事就非常令人沮丧了。

言归正传,通常来说,第一步的反向代理无须对请求做任何处理,只要简单穿透:

Reverse Proxy

如果使用 nginx 的话,配置也很简单,把根路劲代理到遗产系统的服务上即可:

# nginx.conf

server {
  location / {
    proxy_pass https://legacy.com;
    ...
  }
}

迁移功能

一旦 HTTP 反向代理就绪,我们就可以开始抽取功能代码了。当然,迁移方式上又有好多策略,比如分离表现层、重构数据库、提取领域服务等等;由于篇幅限制,本期只讲最简单的表现层分离。

以比较原始的 JSP 应用为例,通常可以将 JSP 做“动静分离”的重构:

  • 动态部分:即原先绑定在 JSP 上的 model 数据。将它们以 Rest API 的形式暴露出去,JSP 以下的业务逻辑保持不动
  • 静态部分:即 JSP 的 HTML 模版(UI)部分。这部分以现代前端框架——如 reactJS——重新实现。重写的 UI 代码全部放到新的系统中,遗产系统中的 JSP 代码保持不动
Split

题外话,利用新技术栈实现功能模块后,对应的 CI/CD 也应在第一时间跟上;一系列 UI 测试也要在第一行代码起开始编写。不然几个月后,你会发现新系统并不会比老系统强健多少。

重定向

如果 CI 配置得当,react 代码的修改从 Merge PR 到完成新系统模块更新——现阶段事实上只有静态文件——应该可以控制在几分钟内完成。

新系统怎么集成呢?我们需要把浏览器请求的特定资源重定向到新系统上:

Redirect

新系统需要一个新的代理路劲(如/modern/)以便与旧系统区分,代理配置上加个 location 即可:

# nginx.conf

location /modern/ {
  ...
  proxy_pass http://modern.com/;
}

当然,这时候新系统依旧不会起任何效果的。原因也很简单,重构初期,html 入口基本都在遗产系统里,除非代码里 hard code /modern/ 相关请求,否则 UI 不会与新系统产生关联——当然这种修改是我们不愿意看到的。

怎么办呢?这里讲一个小技巧,利用 nginx 给所有的 html 注入一条指向 modern 的 js,配置大体如下所示:

# nginx.conf

location / {
  proxy_pass https://legacy.com;
  sub_filter '</body>' '<script type="module" src="/modern/app.react.js"></script></body>';
  sub_filter_once on;
}

只要给遗产系统的 html 注入 app.react.js,所有 UI 相关操作就可以在新系统代码内修改了;而遗产系统的 JSP 无需做任何改动。

数据绑定

上面提到了“动静分离”,那数据和模版分离后,两者怎么互相绑定呢?可能有些朋友会有疑惑,这里补充说明一下,react.js 通常利用异步请求 Rest API 的形式获取数据,并在它自己的模版上实现绑定。这是所谓的MVVM 模式,也是现代化前端框架优于 JSP 框架的重要原因。

我们看看加了 react 重构后的页面请求顺序:

  1. 浏览器提请页面
  2. 由于 nginx 默认设置,请求都会路由到遗产系统上
  3. JSP 生成 html 后返回页面
  4. nginx 会给所有 html 返回注入脚本标签<script type="module" src="/modern/app.react.js"></script>
  5. 浏览器解析到上述标签后,再发起/modern/关联的 js 的请求
  6. 这个请求会被路由到新系统上
  7. 新系统返回 app.react.js
  8. 浏览器执行上述 js,发现有 Rest API 的请求,于是再发起数据请求
  9. 数据请求也是走默认配置路线,被 nginx 指向遗产系统
  10. 遗产系统返回 Rest API 的 JSON 数据
动静分离

最后,就是在浏览器上 react 框架自动实现数据绑定了。相较于 JSP 那种一股脑后端渲染并返回一个巨大的 HTML,“动静分离”的模式在过程中相对复杂一点,新入门的朋友可能要增加点认知成本了。但最终体现在代码上的话,其实更简单;这东西写过一两个页面就能感觉到了,我这里不深入讲解了。

小结

OK,UI 重构的基本框架大致搭建完工了,之后就是根据特定业务逐步地迁移各个前端模块。
推荐在初始阶段可以将 react 当 jQuery 用——就是当 lib 用啦:通过 selector 找到遗产代码的特定 DOM,重构之;一个页面完工后,再修改 nginx.conf,用以劫持新页面到 modern 系统。如果进度顺利,在第一个页面完工后就可以体现出重构效果了——肉眼可见的加载速度。

前端迁移完后,就是后端服务的拆分了,我会在《绞杀者模式(二)》里进一步讲解,敬请关注!

碎言碎语

我曾参与过一个单体架构的遗产项目,积年累月堆积了几万个 bug;然而,作为开发人员平均每人每周的 bugfix 量仅为 1(时常还能引入点新 bug)。我想不出任何方式能在这个项目彻底玩完前减少一些账面的 bug 数,所以就专程前往厂里的一位领导干部那里请示。他告诉我,“重新定义 bug,那些 bug 就没了!”

听完后,豁然开朗:Bug 是绝对修不完的,所以不要再纠结每周修 1 个 bug 或是 2 个 bug 这种问题了。分一点时间出来——比如 20%的人力——迁移产品,在迁移的过程中逐步捋顺业务逻辑,当迁移完工后,遗产系统的 bug 就不再是 bug 了。

相关文章

  • 绞杀者模式 (一)

    背景 随着系统老化、开发工具逐渐落伍、bug 堆积,项目会变得及难维护。所以“腐烂”是所有遗产项目不可避免的一环。...

  • 服务拆分-绞杀者模式

    绞杀者模式 1 定义 老系统向新系统迁移时,可采用将老系统的部分功能使用新应用或服务替换的方式逐步进行。当老系统全...

  • 使用CDC模式改造遗留系统

    项目改造背景及挑战 在我们经历的各种遗留系统改造之旅中,使用绞杀者模式[https://martinfowler....

  • 快手、秒拍们掀起短视频“绞杀战”,内容创作者们如何活下来?

    一、短视频绞杀战 2016年被称为“短视频元年”,2017年短视频进入“绞杀战”状态。 “绞杀战”是近两年的常态,...

  • 绞杀

    秀发飞舞,眸闭颌扬 感应到了你内心的彷徨 仿佛内心世界正在流浪 什么让曾经的快乐天使 变的黯伤 什么让七彩梦中的浅...

  • 绞杀

    自马里亚纳至喜马拉雅, 我从尸寒的一堆堆骨殖中颤巍着站立: 彻底杜绝一切对爱情的期待! 我是新生;死便死了。 太阳...

  • 《微服务架构设计模式》读书笔记---第十三章:微服务架构的重构策

    对于正在经历单体地狱的团队,有一些策略可以摆脱这种现状。 绞杀者应用程序(Strangler Applicatio...

  • 刀痕

    卧轨者应当绞杀 背叛者处以极刑 辱母者却罪不至死? 来自善良母亲的谎言 欺骗了每个头顶怒发的人 冷面,或者是富于算...

  • 学生举报补课反被泄密,学校教育局沆瀣一气

    对理想主义者的唾骂,对现实主义者的颂扬,对异声者的矫正,对抗争者的无情绞杀,算是我们社会最大的特点,也是这个机器得...

  • Builder Pattern in Java

    建造者模式:建造者模式定义建造者模式应用场景实现案例Jdk中的建造者模式建造者模式的优点建造者模式的缺点 建造者模...

网友评论

      本文标题:绞杀者模式 (一)

      本文链接:https://www.haomeiwen.com/subject/fmvhrltx.html