上一节里,我们介绍了两大类型的系统升级重构方案,还介绍了如何进行重构版本的上线,以及如何平滑地完成新老版本切换的方案。在本节里,将会具体介绍如何判断系统发展到什么阶段需要重构,以及如何实施重构。
简单、通用的微服务架构如下图 1 所示,它包含一个应用服务和一个数据库作为存储。

图 1:简单、通用的微服务架构图
当上图中展示的架构出现如下一些问题时,可以采用上一节中提到的“微服务中纯代码维度的升级重构”。
(2)一个类中的某一个方法有上百或者上千行代码;
(3)代码中没有注释;
(4)为了防止影响历史业务,新需求开发均需将原有部分能复用的代码拷贝后,才能修改。
此时,可以将出现上述问题的微服务进行代码重构。具体来说,可以采用 GoF 的 23 种设计模式、SOLID 原则将包含上千上万行代码的类进行重构。此时,重构后的微服务的上线即可对应上一节里提到的“第一种重构类型:纯代码重构”。它的架构如下图 2 所示:

图 2:纯代码升级重构
完成纯代码的重构后,在日常维护中,当你发现有以下问题时,可以考虑进行包含存储的重构。
上述这两类便为包含存储的重构升级,第一个是从数据库升级到缓存,第二个是从数据库升级到 ElasticSearch。它们的架构如下图 3 所示:

图 3:包含存储的架构升级
完成上述两种重构之后,接下来就需要思考什么时候进行另一种存储类型均为数据库存储,但表结构不同的重构了。
在介绍具体实施步骤前,来聊聊:什么是烟囱式架构。
这里以即时通信作为讨论案例。在 PC 时代,QQ 在即时通信市场的占有率是绝对领先者,相信你也使用过。从技术的抽象层面来看,QQ 主要提供消息发送和消息接收的功能,消息可以是图片、表情、文字、视频、语音等内容。支撑 QQ 消息发送和接收的简版架构如下图 4 所示:

图 4:简版的消息发送和接收架构图

图 5:简版的微信消息发送和接收的架构图
从图 5 中不难发现,除了编号 1 的微信 App 和编号 2 的网络接入层模块与图 4 有差异,其余各模块的功能基本上与图 4 类似。网络接入层之所以有差异,是因为接入层需要进行通信协议的解析,而 QQ 和微信的网络通信协议是根据各自的客户端进行定制的,因此会有格式上的差异。
除了微信外,现在大部分在线游戏也都提供了即时通信的能力。因此,游戏团队也需要按上述类似的架构实现自己的消息发送和接收业务系统。
类似上述介绍的这种模式:即系统架构大体上类似,其中只有个别模块存在差异,但各个研发团队还是从零开始建设全部模块的方式,称为烟囱式架构。为了方便你理解,我把 QQ、微信以及游戏语音的架构放在一张图中,如下图 6 所示:

图 6:即时通信的烟囱式架构示意图
从上图可以看出,烟囱式架构是一种象形比喻,各个业务研发团队(如 QQ、微信、游戏语音团队)维护了一个类似烟囱式的、包含重复模块的系统架构。除了即时通信这个案例,还有很多其他会产生烟囱式架构的场景,比如电商,电商除了 PC、App、M 页面版本,现在还有很多购物场景,比如自动贩卖机、微信里的分享链接、小程序等。在实现上,如果他们的研发团队是封闭地进行自研,那么也会产生如下图 7 所示的电商版烟囱式架构。

图 7:电商版烟囱式架构示意图
经过前面的分析,烟囱式架构存在的问题已经比较明显了,即存在大量的模块重复,进而导致人力重复、成本增加。此时,为了解决这个问题,便可以启动本小节开头提到的升级重构:均为数据库存储,但表结构不同的重构了。
以上述消息接收模块为例,它包含了一个代码进程和对应的消息存储(假设为 MySQL)。为了解决此模块的重复,可以合并 QQ、微信和游戏语音里此模块的代码。同时在前期设计时,各个业务只考虑自己的消息格式,所以它们的数据库的表结构是偏定制的,无法直接被复用,因此在重构时,还需要设计一套全新的、兼容原有三个版本的数据库。此时消息接收模块的重构架构如下图 8 所示:

图 8:消息接收模块融合架构图(三个模块+数据库合并成一个)
完成上述模块的重构融合升级之后,消息发送模块也可以进行类似的融合重构。当所有的可复用模块均完成升级重构后,上述三个不同的即时通信软件的架构演化成如下图 9 的形态:

图 9:融合的系统架构
融合后,三款软件有差异的系统模块依然各自维护,但消息接收和发送模块已经融合为一套。此时,从多个模块融合形成的、支持不同类型使用方的模块,称为平台化模块。而这个重构过程,有一个高大上的名称:从烟囱式架构朝着平台化演化。
升级重构完成平台化之后,后续三款软件涉及消息接收和发送的新需求,都由平台化模块直接支持。这种需求支撑的模式,看起来十分像这几年兴起的中台化架构,但并不是。下面我们具体分析一下,你可以从以下两点来理解。
下面,继续以上一小节的案例作为讨论对象。完成平台化,消灭重复技术之后,如果你遇到以下几种情况,则需要进一步重构,以便完成从平台化到中台化的演化。
仍以上述的数据接收模块为例,可以在平台化之后,将数据接收模块对外的接口流程进行梳理并可视化地展现出来。格式为如下图 10 所示:

图 10:接收数据的接口的可视化展示
流程:数据合法性校验 → 图片压缩 → 图片尺寸裁剪 → 图片转存到 CDN → 语音自动识别成文字 → 保存。
上述这个流程图,大多数情况是在需求提出时由产品经理进行绘制,而在代码上线后便不会再实时更新。而中台化之后,需要开发建设一套业务可视化平台,将业务平台中的代码流程可视化地登记到可视化系统中,同时要保证可视化平台能够在业务代码修改后,实时更新相对应的流程。在实现上,编写业务代码时,可以增加一些代码标记,供可视化平台进行自动化扫描,进而识别业务流程,最终更新到可视化平台的显示界面上。
通过将业务能力可视化之后,前面提到的因为平台化融合了太多代码,导致代码量多、业务无法直接从代码中抽取的问题便解决了。因为可视化之后,业务逻辑可以直接在可视化平台上展现出来,业务方和产品经理不需要和研发来回沟通上周的时间来确认需求,可以极大地降低沟通时间,提升效率。
在上述图 10 的流程中,可以看到有些是实心的圆圈,有些是空心的圆圈。空心表示代码执行到此流程节点时会直接跳过,而实心表示会执行此流程节点。流程节点是为空心还是实心,是可配置的,此配置功能可以落地在上述介绍的可视化平台里。
上图 10 的可视化、可配置化流程只有一份,但假设微信在保存消息数据时,不希望图片被压缩而用原图保存;而 QQ 在保存消息数据时,不希望图片尺寸被裁剪。此时,如何通过配置化解决这样的需求呢?答案便是:基于业务身份进行业务流程的配置化。
业务身份是指给 QQ、微信及游戏语音等每一个复用中台能力的应用,都分配一个全局唯一的名称。在进行配置的时候,按业务身份进行隔离,每一个业务身份都拥有属于自己的上述流程的配置,如下图 11 所示:

图 11:按业务身份隔离的私有化流程配置图
在执行时,各个即时通信应用在调用保存消息接口时,需传入自己的业务身份标识。对应的中台模块会根据业务身份获取相对应的配置,并按配置去编排属于此业务身份的流程。
再回顾一下本小节开头提出的平台化架构会遇到的问题。
罗马不是一日建成的,系统建设也是一样,它是随着业务的发展不断演化而来的。当业务体量较小且没有类似像 QQ 和微信的多个前台应用时,没有必要在建设初期就采用平台化或中台化的建设方案。因为它们的建设人力成本和消耗的机器资源也更高。
一个系统在建设时,假如预期未来的三到五年的用户量并不会增长太大,可以先采用烟囱式的架构,快速地满足业务需求。当发展到一定体量后,再发起从烟囱式到平台化及中台化演化即可。毕竟能够发展到百万、千万用户体量的系统是少数,所有的系统都提前建设会存在较大可能的成本浪费。
最后,我再给你留一个讨论话题:当前你所负责的系统处在什么样的阶段,是烟囱式、平台化或中台化的架构吗?它存在什么样的问题,你觉得是否有必要准备启动升级计划了。欢迎你在评论区说出你的想法,我们一起讨论。
本节就到这里,感谢你学习本节内容,接下来是本系列的加餐与本系列的最后一篇内容,和你聊聊技术人发展的话题:如何准备晋升答辩,以及抓住本质,是成为技术专家的不二法则。