⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 juejin.cn/post/7442153919055364136 「我是Allen」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

背景

微服务这几年越来越火,大家聊的也比较多,我刚开始学时大家也兴奋不已,为适应公司发展把单体项目拆分为微服务,当然后来肯定是一地鸡毛。我经历了公司从电商公司到 SaaS 人再到后面集团内部架构成熟稳定,业务也是经历高速发展,技术上也发生了翻天覆地变化。我把遇到的坑总结出来,抛砖引玉引发大家的思考和交流。

另外,微服务的定义大家可能早就耳熟能详了,不熟悉的可以参考维基百科:https://zh.wikipedia.org/wiki/微服务。

聊聊我们微服务发展

单体应用也很好

初创期,为了快速验证业务价值,整个团队都在单体应用上开发,业务需求较多但并不复杂,团队7-8人4-5个服务,业务主要集中在某1-2个应用,迭代过程中也没有遇到什么问题,但第一版上线后需求逐渐增加开始暴露一些问题,从开发–>测试–>发布,遇到了代码合并冲突、代码遗漏问题,也想过一些办法效果不是很好(全靠人肉始终不是办法)。

服务过度拆分导致泛滥

业务上升期团队扩招,成员扩到20个左右,业务扩张继续在单体服务内开发已经不适合了,出现了下面这些问题

  1. 稳定性差,某些迭代 bug、Panic 是无法快速回滚,只能硬着头皮修复,例如:a 迭代先发布,b 迭代再发布,a 迭代 bug 只能修复不能回滚解决问题;
  2. 性能差,单 pod 是有 cpu、内存限制,某些模块高峰期 cpu、内存基本打满了,其它业务响应慢只能靠充钱解决,并不是长久之计;
  3. 扩展性差,多个业务都在一个服务内,也没考虑过做抽象提高扩展性,怎么方便怎么来模块之间相互耦合;
  4. 开发效率低,代码冲突、环境冲突、测试冲突和发布冲突严重,不同的迭代出现你等我,我等你的现象,开发周期严重拉长,有时候到无法容人地步。

大家可以对号入座,思考下是否正在经历?

问题明确了微服务拆分是大难题,到底应该按照啥粒度拆分合理?网上谈微服务文章多把你教会的少,基本没有参考价值,团队内也没有经验丰富的老手,只能摸着石头过河。索性我们按照功能来划分,一个功能一个微服务,有些复杂业务甚至达到 3 个微服务,没考虑太多先拆再看。

我们拆服务用了一些骚操作,开发语言是 GO,rpc 没有大规模的应用经验,平时用 get get 引包方式比较爽,a 服务调用 b 服务改成 a 服务 go get b 服务对外暴露代码,把业务代码当作三方库来用,在打鸡血的时期是非常爽的,需要某个功能吼一声“团队那个同学”帮我写一个对外暴露的函数,需求是「xxxx」。后来证明 go get 方案埋了天坑,坑的一群人哇哇叫,后来持续 4 年才逐步解决。

经过1年多服务拆分,微服务发展到 120+(下面都是团队拆分后我统计的)我们当时认为比较合理的。后面适应公司发展团队被拆小了,服务被划分到各个团队,我们团队12人有80个+应用,人均7个。

后来我们团队业务边界扩大,其它团队业务逐渐合并过来服务超过 100 个,还有异构技术栈,有部分是 java 应用(20+)后来也做了重构 java 切 go了。

回头看,微服务拆分只像一个笑话「自嘲下」,我们拆分方式也并不是微服务,只是把功能拆分到另一个服务而已,反而留下了一堆坑。

服务合并思考

由于前期疯狂拆分服务,微服务配套能力完全缺失的,可以理解为服务在裸奔。另外,一堆 go get 代码,大家都不敢轻易修改,怕改了之后无法向下兼容业务不可用,下面这几个问题困扰了我们很久(后来有一批更优秀的人进来,问题才得到解决,我乘此机会深入学习)

  1. 没有监控,业务出问题无法快速响应依赖客户反馈;
  2. 日志没有链路跟踪能力,问题排查困难;
  3. 一堆 go get 代码,不敢轻易修改,怕改了之后无法向下兼容导致业务不可用。另外 go get 了一些非常基础能力,例如「组织架构」,后来重构组织架构因为 go get 还做了双写新老库;
  4. 维护成本高,人均10个服务,某些迭代甚至几十个服务一起发布,CI/CD 自动化流程直接卡住;
  5. 服务拆分后疏于管理,代码混乱不堪稳定性差,有时研发需要花一周时间值班排查问题,不做其它业务开发。

接下来我们重新思考了微服务的坑

  1. 服务划分过细,服务关系复杂,单个服务复杂度下降了,整个系统复杂度反而增加了(a调b,b调c,c调d,d调a等)看上去就是一张大网

  2. 服务数量多,团队开发效率反而降低,人均10个服务复杂度主要体现

  • 开发功能研发要明确业务边界吧,代码应该写在对应的服务(很多初中级研发根本无法识别,需要对业务非常熟悉的人排版);
  • 需要打开多个工程,本地测时需要启动多个项目(硬件差的同学本地调试不了);
  • 发布要操作多个服务,服务之间可能有依赖关系,a必须等b发布再发布;
  1. 调用链长,性能低
  • 通过http/rpc调用,每次调用都有网络IO开销,假设单次平均调用时20ms,经过6次调用就是120ms
  1. 链路长,问题定位复杂,服务拆分后调用链路被扩散了一次调用经过多个微服务处理,其中某个服务出问题会导致调用失败,快速定位具体是那个服务故障本身就是一件比较复杂的事;

  2. 需要自动化能力才能持续交付,代码开发–>提交–>构建–>部署qa–>部署线上都需要自动化能力才能持续交付;

  3. go get 方案带来了很多隐藏问题,所以 go get 必须废弃,服务间通信使用 http/RPC 。

了解这些坑之后,针对这些坑要有对应的解决方案

  1. 基建层面,RPC、服务治理、日志、监控、持续集成/部署、运维自动化、灰度、链路跟踪、服务编排等,这些都是必须配套的;
  2. 跟拆分类似,合并不能过于激进,跟随迭代/版本逐步演进,合并不能太粗防止服务臃肿。

微服务拆分思路

早期根据业务功能拆分,微服务粒度拆分太细导致问题,这次合并肯定不能这么干。最终我们按照团队来拆,比如我们团队是三级架构域(营销业务方向),业务方向可以继续向下划分4级架构域(小组概念):获客、运营、内容、活动等,每个小方向2-3个人,每个业务方向申请1-2个微服务,流程复杂场景申请2个(例如:自动化营销方向拆了3个服务),服务拆分粒度出于几点考虑

  1. 1个微服务2-3人负责开发和维护,小组内一个主 owner,剩余研发同学负责打配合,他们是可以参与架构设计、需求讨论的,基本能保证每个研发对系统能深入了解,但要有人牵头对整条业务线负责;
  2. 2-3人组是相互 backup 的,假设组内有1个同学请假,另外两人顶上不至于出现单点故障,出问题找不到人的情况;

业务不稳定,迭代多的情况建议大家采用上述2个方案,到业务稳定期微服务代码改动不多并不需要这么多研发了,1个研发维护1个甚至多个微服务都是可以的。

讲完服务拆分粒度,下面聊聊微服务拆分逻辑

  1. 基于业务逻辑拆分,将系统逻辑梳理清楚,按职责范围划分好,每个单独的逻辑拆分一个独立的服务,以 CMS 平台为栗,有文章、h5、表单、话术库、文件系统、素材包等(10多种素材),我都将其定义成内容,虽然功能多但只拆分了1个独立服务。按逻辑拆分方式也会引入另一个问题,不同研发和架构师对职责范围划分是不同的,也可能导致服务拆分过细或过粗,不用怀疑肯定会出现这类情况;
  2. 基于变化性拆分,将系统按照稳定性进行排序,将不迭代、不变动服务拆分为稳定服务,将迭代多、变化快的拆分为变动服务,以自动化营销为栗,规则引擎开发一次未来基本不变动的拆分为稳定服务,事件引擎(编排全平台事件)、面向用户经常变动业务逻辑拆分为变动服务,后来证明这种拆分方式是合理的;
  3. 基于可靠性拆分,将系统按照可靠性进行排序,按照 P0、P1、P2 标识,将可靠性要求高的 P0 核心服务和可靠性要求低的 P1、P2 的非核心服务拆分不同的服务,以登陆服务、组织架构服务为栗,这几个服务是核心 P0 服务,需要高可用,系统一旦故障整个业务不可用,P1、P2 非核心服务可不用也只会影响该模块不会导致平台故障;
  4. 基于性能拆分,将系统按照性能要求进行排序,将性能要求高的单独拆分独立服务,避免性能高服务占用内存、网络、cpu 等资源导致其他业务异常,我们系统是出现过的,以营销自动划系统为案例,我们拆分了任务系统、流程编排系统,业务高峰期负载高不会相互影响。

聊了服务拆分逻辑应该不会让你抉择困难哈,上述逻辑 2,3,4 都是相通的,比如规则引擎基于可靠性、变化性、性能单独拆分出来都是说的通的,往往拆分服务时都会基于这3点共同抉择,自行灵活应用。

再聊服务合并,路真的很漫长

聊完服务拆分,接下来聊服务合并了,服务合并时我司基础组件已经比较成熟了配套完善,基础组件这块不赘述。服务合并需要有大毅力并且懂得规划,从2022年Q1-到2024年Q4,我一直都在做这件事儿,每个季度「服务治理」必定出现在我的季度目标内,并且按周和季度跟进。怎么判断服务应该被合并捉或者下线?聊聊我的治理思路

  1. 基于流量统计,统计平台服务流量,从高到低排序,流量低说明业务价值也不高,这部分业务下线或者合并到其它应用;
  2. 基于迭代统计,统计平台服务迭代,按照时间排序,2-3年没有迭代的业务价可能价值也不高,需要识别出来下线或者合并;
  3. 基于业务数据统计,统计该模块的业务数据,根据数据判断业务价值,逐步下线或者合并;
  4. 迭代频繁业务,采用重构策略,全新业务、全新架构替换现有业务和架构,比如:营销自动化替换营销计划、CMS 内容平台替换素材中心等,这类 case 在我们业务方向非常多,有一段时间重构项目立项全平台最多;
  5. 高优先级需求走技改单独开发-》测试-》上线。

切记、切记、切记,一定要提前规划好,制定明确目标每周跟进。服务合并也不能操之过急,持续迭代演进确保服务合并是符合当前架构演进方向的,每个季度有进度最终结果都不会差。

下面是微服务合并史

  1. 2021 年团队拆小,团队内服务 89个;
  2. 2022 年Q1,服务69个,甩了一批服务出去并且下线了几个,Q1开始重点关注服务治理工作;
  3. 2022年Q3,服务65个,下线4个经过几个季度进展缓慢;
  4. 2023年Q1,服务54个,下线11个;
  5. 。。。。
  6. 2024 年 Q4,服务 39 个,中间业务边界扩大接手了 20+ 服务,从 100+ 干到了 39 个,另外 10+ 服务还在重构。

短短几点道尽了这几年心酸,到目前为止服务治理并没有结束,未来还需持续努力,加油!!!

采访了一位老员工,经历了架构演进最痛苦阶段。

总结

微服务拆分合理是“银弹”,拆分不合理是“煤焦油”,这么说一点都不过份。

文章目录
  1. 1. 背景
  2. 2. 聊聊我们微服务发展
    1. 2.1. 单体应用也很好
    2. 2.2. 服务过度拆分导致泛滥
    3. 2.3. 服务合并思考
  3. 3. 微服务拆分思路
  4. 4. 再聊服务合并,路真的很漫长
  5. 5. 总结