微服务熵减 —— 道阻且长

389 阅读14分钟

微服务的熵增

宇宙第一定律(熵增定律):系统的熵会随着时间而增加,直至严重混乱甚至灭亡。

人活着就是在对抗熵增,生命以负熵为生。 —— 薛定谔

业务选择了微服务

单体应用在较大的业务复杂度时会体现出较低的生产力,一定程度上,微服务是业务发展的必然选择。 11.png

任何复杂的系统都不是一朝一夕建成的,服务复杂度会随着业务的发展而上升,可以粗略拆分为以下几个阶段。

业务增长阶段业务复杂度产研人数系统复杂度服务数量
初创期少。多为 单体服务 或 有限的几个服务
快速发展期中 → 高中 → 多。面向未来的架构设计,最容易迎来服务的爆炸式增长, 急速熵增
成熟期多。新增的微服务的频率会明显降低,但是可能已经出现了服务过微
衰退期高 → 中多。心有余而力不足

在业务发展过程中,业务复杂度逐步升高,产研人数也逐步增多,微服务拆分是一个必然,拆分的原因包含但不局限于以下方面:

  • 团队自治
  • 领域拆分
  • 故障隔离
  • 部署提效
  • ······ 12.png

微服务的演进 — 服务过微

微服务虽好,不要贪多。什么是“服务过微”,其实就是服务太多了,维护不动了。

PPT中的微服务: 13.jpeg

现实中的微服务:  

想象一下,你能基于下面的微服务梳理出产品的主流程吗?

我把导致服务过微的原因分为几类:

  • 组织架构:业务架构决定组织架构,而 组织架构决定系统架构。 组织架构的拆分或变化会导致微服务数量的分分合合
    • 组织架构拆分的足够细,微服务的数量就一定多
    • 比如写论文或者写文档,只要目录拆分的足够多,那么文档就会足够长
  • 未来式设计:如下图所示,系统复杂度会高于业务复杂度。我们的系统设计都是拥抱未来的,这是正常且更符合发展规律的
  • 维护人数:服务数量本质上和维护人员数量正相关。你不能奢望10个人维护一个服务,同时也不能奢望1个人维护10个服务

熵现状

我们从以下几个指标度量目前的微服务的熵: 

  • 人均负担
    • 人均服务数
    • 人均代码行数
  • 项目债务
    • 项目平均rpc数量
    • 无用方法比例

微服务之间通过rpc来进行领域的串联。一旦服务过多,且对外提供的业务领域接口不能得到有效管控,最终会导致的问题就是:

  • 系统依赖多且杂:改不动,下不掉
  • 系统链路不清晰:难以合理的进行业务架构和系统架构的合理分层
  • 对老人的过度依赖:依赖经验,容易踩坑
  • 成本:包括维护成本 和 系统成本

治理目标

微服务治理在治理什么?

我们谈「微服务治理」究竟在谈论什么?

考虑下面的场景?

  • OKR推进底层技术改造的升级  —— 工作量太大,做不完
  • 我们想推进大表治理 —— 工作量太大,做不完
  • 推进系统的单测建设  —— 工作量太大,做不完

以上都是我们团队亲身经历的问题,痛定思痛之后觉得问题的本质在于2个关键指标:人均服务数量 & 人均代码量。后续在后续较长的一段时间范围内,我们团队都会将精力优先放在「熵减」这件事情上,自此便开始了持续的删减之路。

如果只是复制合并微服务的代码本身,而不去优化治理,可能可以减少部分维护成本,但不是微服务治理最重要的目标。我们在聊治理「服务过微」,绝对不是治理微服务的数量本身,而是我们的 系统复杂度以及团队成员的维护成本

治理不是停下,是为了更好的再出发,那么我们什么时候需要进行治理?当我们不那么敏捷的时候,也许就是需要治理的时候

  • 一个并不复杂的需求的上线,关联多个团队,多个项目 —— 沟通成本高,上线复杂
  • 团队内成员负责的人均项目数过多,技术事项无法推动

宏服务

饿了么在Qcon的分享中,提到了他们团队的目标是人均 0.3 个服务。那么我们呢?笔者所在团队之前也做过微服务合并,实际上是在将 微服务 转变为代码仓库中的 component,将领域间的复杂度转变为领域内的复杂度,进而可以按照业务逐步降低领域内的复杂度。

我理解这就是「宏服务」的体现。宏服务意味着:

  • 更大的DDD边界

  • 单个服务的开发者要>=3 (来源于饿了么分享)

    • 多个开发者共同维护一个项目也更便于互相backup项目内的业务模块
  • 奥卡姆剃刀原则:如无必要,勿增实体

  • 关注CI/CD效率

  • 关注流量的隔离设计

  • 更加严格的流程管控 和 领域规范

业务复杂度不会消失,只会转移,我认为我们在做的微服务治理,本质上就是微服务转变为宏服务,从而实现更轻量的领域间依赖,也更便于领域内的系统优化。

而在前端和服务端,宏服务的体现可能也会有一些差异。前端可能尝试 monorepo,服务端可能也需要类似的项目合并。我们团队内服务端和前端合并微服务之后的工程结构本质上很相似,体现在以下几个方面:

  • 基于业务领域拆分目录结构
  • 业务领域之间有隔离和依赖机制
  • 尽可能的代码复用
  • 微领域 → 宏领域。减少微领域之间的领域概念,避免系统间的不合理依赖

个人觉得,我们做微服务的治理目标,也应当遵循上面宏服务的原则,尽可能的把人均项目数减少到1(可能需要按照各个业务团队自己的债务衡量)。

熵减之路

结合团队经历,以及业内实践学习,我将服务的熵减之路分为以下几个阶段。

  • 控 - 控增量,团队内部对齐标准,关注指标和大盘
  • 删 - 能删尽删,减少废弃代码和服务的负担
  • 合 - 合并同类项。将同一类型的场景代码合并到一个component之中统一管理,要求单业务场景单仓库
  • 优 - 优化重构。合并完成之后,相似的代码都会放在一起,但是还需要审视是否需要更进一步的业务抽象,从而彻底完成 「**多套 → 1套」**的演进。这一步完成之后,我们才算是真正做到了熵减
graph LR   	
控 --> 删     
删 --> 合    
合 --> 优

控 — 控制增量

控制增量的问题可以从现在开始,而且团队的管理者一定要开始先关注这个问题,否则就没办法控制增量。

饿了么的做法:

  • 倡导简单:量化代码指标,定期统计宣导
  • 奖励标杆:团队文化氛围,确保从上到下的理念一致性 15.png

对我们而言,我觉得也可以有以下几个工作可以跟进:

  • 量化服务质量:团队需要制定自己的项目度量标准,并将项目质量数字化。这个评估也需要增加「无用代码」的权重
  • 对齐团队标准:管理者需要营造持续熵减的氛围,并从上到下对齐。比如「删」一个没有流量到底有没有收益这件事,需要对齐认知!!!
  • 团队主管关注:如果团队主管不关注,认为现阶段这不是一个问题,那么这件事情基本无从谈起
  • 大盘和提醒:这个事情主要是基于量化的服务质量来做大盘和题型,主要目的就是让大家知道哪些服务有问题,主管也可以阶段性的跟进

做好了增量的控制,整体的熵减工作才会真的有意义

删 — 能删尽删

有大量的文章都在教我们如何加代码,产品也只会关注需求的新增或者变更,但很少有人真正关注「删」。这些废弃的代码会给维护人带来很大的负担。

「删」的主要思路是打扫干净屋子再请客,也是我认为这几个过程中ROI最高的阶段。收益而言,删业务  > 删接口 > 删方法 > 删代码行

删业务

如果某个业务场景已经不再使用了,优先删这个业务下的所有代码。可以想一想业务场景中是否维护了多套版本的业务,比如售卖体系、海报体系、文案配置体系等等。不用特别担心工作量,可以长期持续化的推进,否则这些债务永远得不到解决,服务的继任者也会循环往复的内心暗暗讨伐前任。

这个过程可能需要很多的确认和沟通工作,但是结果肯定会远超预期。

删接口

微服务治理的一个巨大的收益就是减少一个领域对外暴露的概念,从而减轻领域之间的耦合度。而入口层的下线可以有效的减轻领域概念的暴露程度,因此非常适合前期集中处理。

接口维度的删除最好可以有量化的指标可以跟进,比如我们已经建设了基于项目代码+接口调用信息得到了一些接口层面的指标,包含无流量的http、rpc、job等。这些入口层的方法下线能够是最能减轻领域复杂度的方式。

在执行接口的删除过程中,如果一个业务的多个接口没有流量,最好确认并推动整个业务的下线,而避免只下线单个接口。

注:无流量接口指标可以纳入项目质量分的计算公式之中,保证研发在日常也能时刻关注新的熵增。

即使缺乏项目质量分体系,或者某些接口指标不在计算公式内,团队负责人也需要阶段性的关注团队内接口层面的变化,运动式治理。

删方法

实际上,尽可能的删完废弃的业务和入口之后,无用代码的比例已经基本可以达到20%以下,但是项目里仍存在大量的无用方法,因此可以进一步推进无用方法的删除。

下线前的人工确认必不可缺!

服务端

建议各团队建立自己的无流量方法统计平台。整体的思路是在对项目的方法插桩,从而可以在运行时统计方法的调用量级。如果目前服务还未接入可以考虑推进。可以参考去哪儿的实践:去哪儿网系统瘦身技术揭秘

一般而言,删除无用的方法包含两种:

  • 静态无引用:直接删除
  • 有引用 & 无流量:在做完入口层的接口删除之后,剩下的无流量方法往往对应着一个条件分支。此时需要投入大量精力用于跟产品的沟通和确认。(因此可以优先解决无用比例过高的项目) 16.png

前端

前端而言,我认为对于大部分中小型公司,建造方法维度的流量检测收益较低,不需要太多的关注。

删代码行

删代码行是在「删方法」的基础上做进一步的优化,需要更高的技术能力支撑,我也认为对大多数的业务而言roi较低,不需要做这么细粒度的管控。暂时还未支持该能力,也没有计划投入。

附阿里的行级流量检测示例(使用jacoco):

17.png

删服务

如下图所示,标价橙色的为可以废弃的业务。业务A有多个场景,但是分散在服务A和服务B,业务C维护在服务C中。执行完上述的步骤之后,服务C已经没有太多重要的业务代码,因此可以进一步推动服务C的下线。

18.png

合 — 合并同类项

执行「删」之后,需要维护的代码会有降低,但是微服务的数量并不会有明显减少。因此需要进一步执行「合」的动作。

「合」会分为几个步骤执行:

  • 梳理业务场景 : 梳理现有业务,并按照「宏服务」的粒度重新划分领域,并基于领域制定业务所属的宏服务
  • 业务合并 : 找到业务的宏服务,将散落在其他服务的业务合并到宏服务之中
  • 服务删减 : 执行完业务合并之后,预期所有规划外的服务都将被废弃,这些服务都将被删减,仅保留第一步梳理的有限个宏服务

如上图所示,服务A和服务B的合并大概分为以下的过程:

  • 梳理业务:明确服务B作为业务A的宏服务
  • 业务合并:将业务A中的A1从服务A迁移到服务B
  • 服务删减:下线服务A

合的过程会耗费比较多的精力,但是是系统开始变得收敛可维护的关键环节。之后,微服务的数量将降低到一个合理的区间。

优 — 重构优化

在上面的例子中,业务A1迁移到服务B之后,服务A会下线。假设业务A1和业务A4的领域是一致的,需要将两个业务合并到一个component之中,合并后的状态大致如下图中的第二部分。此时,一个业务领域可能存在多套不同的版本。「优」实际上是熵减的最终目标,以便尽可能的代码复用, 期望将同领域的业务按照一种领域模型进行组织,从而减少维护成本。 19.png

这一环节需要依赖实际的业务场景,并依此决定后续的领域模型。这里的component1, component5, component6 实际上就是在 「合」这个过程中就已经可以决定的,剩下的部分就是按照优先级分别去推进各个模块的重构和优化,是一个道阻且长的过程。

这一阶段完成之后,领域的边界应该可以达到足够清晰的状态,后续可以进一步关注项目质量和稳定性,推进自动化测试的建设。

写在最后

关于是否需要推进熵减的治理

对于团队管理者来说,我感觉需要审慎的关注大家的负担和债务,如果压力确实比较大,就需要逐步的去推进熵减的工作。

对于一线研发,如果你面临上文所述类似的问题,那么团队其他人大概率也会面临同样的问题。找老板沟通,提出问题,并给出解决方案。

关于服务过微的思考

如果现在互联网的形态仍然像过去的黄金10年一样,资本都在注入,各个公司都在招兵买马,拓展业务,有很多的业务方向和研发,那么还会存在现在大量关于"服务过微"的讨论吗?

时代的发展决定了技术的潮流,也决定了从业者的生存空间。服务需要研发来维护,微服务多了,需要的研发就多了。反之,业务缩减了,研发变少了,服务也就过剩了。

最后还有个想法,从 「微服务」 → 「宏服务」 的架构模式演进,一定程度上也意味着从业者的生存空间和能力模型的变化

希望大家立足当下,做好准备,拥抱未来~

参考文献

服务精简归拢——走出微服务误区与治理实践

去哪儿网系统瘦身技术揭秘

更多信息请关注我~

image.png