专栏名称: 芋道源码
纯 Java 源码分享公众号,目前有「Dubbo」「SpringCloud」「Java 并发」「RocketMQ」「Sharding-JDBC」「MyCAT」「Elastic-Job」「SkyWalking」「Spring」等等
目录
相关文章推荐
芋道源码  ·  微服务的血与泪,从拆分到合并 ·  昨天  
芋道源码  ·  为什么官方不推荐使用 @Autowired? ·  昨天  
芋道源码  ·  疯传Java界,堪称最强! ·  4 天前  
芋道源码  ·  替代ELK:ClickHouse+Kafka ... ·  5 天前  
芋道源码  ·  SpringBoot + ... ·  5 天前  
51好读  ›  专栏  ›  芋道源码

微服务的血与泪,从拆分到合并

芋道源码  · 公众号  · Java  · 2024-12-20 09:30

正文

👉 这是一个或许对你有用的社群

🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入芋道快速开发平台知识星球。下面是星球提供的部分资料: 

👉这是一个或许对你有用的开源项目

国产 Star 破 10w+ 的开源项目,前端包括管理后台 + 微信小程序,后端支持单体和微服务架构。

功能涵盖 RBAC 权限、SaaS 多租户、数据权限、商城、支付、工作流、大屏报表、微信公众号、CRM 等等功能:

  • Boot 仓库:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • Cloud 仓库:https://gitee.com/zhijiantianya/yudao-cloud
  • 视频教程:https://doc.iocoder.cn
【国内首批】支持 JDK 21 + SpringBoot 3.2.2、JDK 8 + Spring Boot 2.7.18 双版本 

来源:juejin.cn/post/
7442153919055364136


背景

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

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

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 视频教程:https://doc.iocoder.cn/video/

聊聊我们微服务发展

单体应用也很好

初创期,为了快速验证业务价值,整个团队都在单体应用上开发,业务需求较多但并不复杂,团队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. 跟拆分类似,合并不能过于激进,跟随迭代/版本逐步演进,合并不能太粗防止服务臃肿。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud
  • 视频教程:https://doc.iocoder.cn/video/

微服务拆分思路

早期根据业务功能拆分,微服务粒度拆分太细导致问题,这次合并肯定不能这么干。最终我们按照团队来拆,比如我们团队是三级架构域(营销业务方向),业务方向可以继续向下划分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+ 服务还在重构。

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

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

总结

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


欢迎加入我的知识星球,全面提升技术能力。

👉 加入方式,长按”或“扫描”下方二维码噢

星球的内容包括:项目实战、面试招聘、源码解析、学习路线。

文章有帮助的话,在看,转发吧。

谢谢支持哟 (*^__^*)