微服务概念与治理
开篇:为什么要从单体转向微服务?
想象一下你经营着一家小饭馆,厨房里一个厨师搞定所有菜品。生意好的时候,一个人忙不过来,你想加人,但厨房就那么大,两个厨师挤在一起反而更慢。更要命的是,这位厨师请假了,整个饭馆就得歇业。
这就是单体架构的困境:
- 代码量巨大,改一行要重新部署整个系统
- 扩展只能整体扩展,CPU 密集的模块和 IO 密集的模块被迫绑在一起
- 一个模块内存泄漏,整个应用崩溃
- 团队协作困难,十几个人改同一个项目,分支合并冲突不断
- 技术栈被锁死,想引入新技术需要对整个项目大改
而微服务的思路是:把大厨房拆成多个档口,炒菜的、炖汤的、做甜品的各自独立。每个档口可以单独扩容、单独升级,一个档口出问题不影响其他档口继续营业。
比如把一个电商系统,拆成用户服务、商品服务、订单服务、支付服务、物流服务,每一个都是独立的应用,有自己的代码仓库、数据库、部署流程。服务之间通过 HTTP/RPC/MQ 通信,实现高内聚、低耦合。
微服务的诞生并非偶然。它是互联网高速发展、敏捷和持续交付方法论的深入人心、虚拟化技术与容器化(Docker/K8s)的快速发展,以及传统单体架构无法适应快速变化等多重因素共同推动的产物。
一、微服务 vs 单体 vs SOA
在架构演进的历史上,大致经历了三个阶段。我们用一张表来对比它们的核心差异:
| 维度 | 单体架构 | SOA | 微服务 |
|---|---|---|---|
| 部署方式 | 一个大包整体部署 | 按服务部署,但共享 ESB | 每个服务独立部署,独立进程 |
| 通信方式 | 进程内调用 | 通过 ESB(企业服务总线) | 轻量级协议(HTTP/RPC/MQ) |
| 数据管理 | 共享一个数据库 | 可能共享 | 每个服务有自己的数据库 |
| 团队组织 | 大团队协作 | 按技术层划分 | 按业务能力划分(康威定律) |
| 技术栈 | 统一 | 较统一 | 可异构,每个服务自由选择 |
| 迭代速度 | 慢,牵一发动全身 | 中等 | 快,独立开发、独立部署 |
SOA 和微服务的关键区别
SOA(面向服务的架构)是 Gartner 在上世纪 90 年代提出的概念。它的核心思想是将系统中的功能抽象为"服务",通过服务的组合来构建应用。
但 SOA 有个大问题:ESB(企业服务总线)太重了。所有服务之间的通信都要经过 ESB,路由规则、消息转换、协议适配全部堆在 ESB 上面。ESB 本身就成了单点瓶颈和运维黑洞。
微服务是对 SOA 的进化:
- 去掉 ESB:把路由、消息解析等逻辑放回服务本身(Smart endpoints, dumb pipes)
- 更彻底的拆分:服务粒度更细,每个服务可独立部署
- 更强调快速交付:SOA 关注服务重用,微服务同时关注敏捷迭代
Martin Fowler 对微服务的定义:将单个应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,通过轻量级机制通信,围绕业务能力构建,可独立部署。
微服务和分布式的关系
这两个概念经常被混淆。分布式是把一个集中式系统拆分成多个系统,对外整体提供服务。用户感知上就像访问一台计算机一样。
而分布式架构有很多具体实现形式,包括 C/S 架构、P2P 架构、SOA 架构、微服务架构、Serverless 架构等。微服务是分布式架构的一种。
所以可以说"微服务一定是分布式的,但分布式不一定是微服务"。
康威定律:组织决定架构
说到微服务,不得不提康威定律。1967 年 Melvin Conway 就提出:
设计系统的组织,其产生的设计等同于组织之内、组织之间的沟通结构。
简单说:你的团队长什么样,你的系统就长什么样。 Apple 的组织架构造就了优雅统一的产品,微软曾经的多事业部架构造就了割裂的产品体验。
康威定律的实践建议:
- 提升沟通效率:能 2 个人讲清楚的事不要拉更多人,每个人每个系统都有明确分工
- MVP 迭代:好的架构都是慢慢迭代出来的,不要指望一步到位
- 按业务划分团队:让团队自然地自治内聚,明确的业务边界减少跨团队沟通
- 小而美的团队:亚马逊的"两个披萨原则"——如果两个披萨不够一个团队吃的,这个团队就太大了。一般 7-8 人左右
所以很多人说,康威定律在半个世纪前就奠定了微服务架构的理论基础。 你想要什么样的系统设计,就架构什么样的团队。
二、服务拆分原则
把单体拆成微服务,不是随意乱砍。就像切蛋糕,切得好大家都有份,切得不好就成了一堆碎渣。
核心原则有三条:
1. 单一职责(Single Responsibility)
每个服务只做一件事,做好一件事。用户服务管用户,订单服务管订单,库存服务管库存,职责清晰不越界。
2. 限界上下文(Bounded Context)
这是 DDD(领域驱动设计)里的概念。每个服务围绕一个明确的业务边界来设计,在边界内部高内聚,边界之间通过明确的接口通信。
比如"订单"这个概念,在订单服务里是完整的订单对象(含状态、金额、地址),在物流服务里可能只是一个"物流单号+地址"。不同服务对同一概念有不同的理解,这就是限界上下文。
3. 数据隔离
每个服务拥有自己的数据库(或者至少自己的 schema),不允许直接访问其他服务的数据库。需要数据?通过接口调用来拿。
除此之外,还要考虑:
- 按组织架构拆:谁负责谁维护,做到最小沟通成本。有些公司做了中台,就会有单独的中台微服务
- 按应用类型拆:在线业务和离线业务分开,避免互相影响
- 按技术架构拆:不同技术栈、不同语言的模块独立出来,有利于独立维护
- 避免循环依赖:A 调 B,B 又调 A,这是设计的"坏味道"
循环依赖的危害与解决
微服务之间的循环依赖是指两个或多个服务互相调用。比如订单服务调用库存服务检查库存,库存服务又调用订单服务更新状态,这就形成了循环。
循环依赖会带来四大问题:
- 流量放大:本来 100 QPS 的请求,因为互调可能变成 200 QPS,无形中被放大了流量
- 性能劣化:互相等待响应,接口 RT 被拖长,用户体验变差
- 级联故障:一个服务挂了,错误通过依赖链传播到其他服务,增加级联故障风险
- 发布困难:一般被依赖的应用先发布。但循环依赖下谁先发都有问题
解决办法:
- 重新设计:审视职责划分是否合理,一个系统又在上游又在下游,一定是设计有问题
- 消息解耦:把同步调用改成异步消息,通过 MQ 解耦双向依赖
- 引入共享服务(中介服务):两个服务都依赖第三方,而不是互相依赖。比如引入"台账服务",上游系统查数据直接查台账,不需要依赖其他业务系统
- 抽取共享库:如果两个服务共享一些通用逻辑,可以抽成 SDK 各自引用,不通过接口调用
如何识别循环依赖?
- 画一张清晰的架构图,标明每个系统的上下游,依赖只能从上到下
- 通过链路追踪查看调用链,如果一次 trace 中同一个应用被调用多次,就可能存在循环依赖
- 在设计评审、发布评审、代码评审中重点关注
三、微服务核心问题
把单体拆成微服务后,虽然每个服务变简单了,但系统整体的复杂度反而上升了。就像把一个大工厂拆成十个小作坊,每个作坊干活简单了,但怎么协调这十个作坊就成了新问题。
| 问题 | 说明 | 典型方案 |
|---|---|---|
| 服务注册与发现 | 服务那么多,谁在哪里? | Nacos、Eureka、Consul、ZK |
| 配置管理 | 配置散落各处怎么管? | Nacos、Apollo |
| API 网关 | 统一入口,路由、鉴权、限流 | Spring Cloud Gateway、Kong |
| 负载均衡 | 多个实例怎么分配请求? | Ribbon、Spring Cloud LoadBalancer |
| 容错保护 | 下游挂了怎么办? | Sentinel、Hystrix |
| 分布式事务 | 跨服务怎么保证数据一致? | Seata、本地消息表 |
| 链路追踪 | 一个请求经过了哪些服务? | SkyWalking、Zipkin |
| 服务通信 | 服务之间怎么调用? | OpenFeign、Dubbo、gRPC、MQ |
微服务之间的通信方式
微服务做了进程拆分,就不能再进程内直接调用了。主流通信方式有四种:
1. HTTP 调用
最常见的方式。用 RESTful API 或 OpenFeign 进行声明式调用,SpringBoot 生态天然支持。
常用工具:
- RestTemplate / OkHttp / Apache HttpClient:手动构造 HTTP 请求
- OpenFeign:通过注解定义接口,像调本地方法一样调远程服务
- @HttpExchange:Spring 6.0 新推出的内置 HTTP 客户端,用来替代 OpenFeign
注意:从 Spring Cloud 2022 开始,OpenFeign 被视为"功能完结",只修 bug 不加新特性。
2. RPC 调用
如 Dubbo、gRPC、Thrift。通常基于二进制协议,性能比 HTTP 更好,适合内部服务间高频调用。
- Dubbo:阿里开源,Java 生态,提供注册发现、负载均衡、容错
- gRPC:Google 开源,基于 HTTP/2,支持多语言,支持双向流
- Thrift:Facebook 开源,跨语言 RPC
3. 消息队列
如 RocketMQ、Kafka、RabbitMQ。适合异步场景,天然解耦,还能削峰填谷。
4. Service Mesh
如 Istio、Linkerd。通过 Sidecar 代理管理服务间通信,不侵入业务代码。适合多语言异构系统。
Service Mesh 在异构系统中用得比较多,比如一家公司技术栈很杂(Java + C++ + Python + PHP),通过 Service Mesh 可以用统一的架构体系把不同语言的服务整合到一起。
选择建议:对性能要求高选 RPC;需要异步解耦选 MQ;跨语言异构选 Service Mesh;简单场景选 HTTP。
四、SpringCloud vs SpringCloud Alibaba vs Dubbo
这三者是目前 Java 微服务领域最主流的技术体系。它们不是互斥的,更多的是互补关系。
| 能力 | Spring Cloud(Netflix 系) | Spring Cloud Alibaba | Dubbo |
|---|---|---|---|
| 注册中心 | Eureka(已停更) | Nacos | Nacos / ZK |
| 配置中心 | Spring Cloud Config | Nacos | -- |
| 负载均衡 | Ribbon(已停更) | 集成 Ribbon / LoadBalancer | 内置 |
| 远程调用 | OpenFeign(功能完结) | OpenFeign / Dubbo | Dubbo RPC |
| 熔断降级 | Hystrix(已停更) | Sentinel | 内置 |
| 网关 | Zuul / Gateway | Gateway | -- |
| 分布式事务 | -- | Seata | -- |
| 链路追踪 | Sleuth + Zipkin | SkyWalking / Zipkin | SkyWalking |
可以看到,Netflix 系的很多组件已经停更了(Eureka、Ribbon、Hystrix 都不维护了)。目前的主流趋势是 Spring Cloud Alibaba 的全家桶,或者搭配 Dubbo 做 RPC 调用。
推荐组合拳:Nacos(注册/配置) + Sentinel(容错) + Seata(事务) + Gateway(网关) + OpenFeign 或 Dubbo(调用)。
五、服务治理全景
服务治理是微服务架构的"基础设施",就像城市需要交通系统、供水系统、安保系统一样,微服务也需要一整套治理体系来保障运行。
限流、降级、熔断:保命三板斧
这三者经常一起被提到,但它们解决的问题不同:
| 维度 | 限流 | 降级 | 熔断 |
|---|---|---|---|
| 目的 | 控制流量,防止被打垮 | 关闭非核心功能,保核心 | 切断故障源,防止雪崩 |
| 类比 | 高速公路收费站限流 | 停电时只保住冰箱 | 电路保险丝 |
| 发起方 | 被调用方(服务端) | 双方都可以 | 调用方(客户端) |
| 手段 | 滑动窗口/漏桶/令牌桶 | 通过开关控制功能关闭 | 熔断器(关闭/打开/半开) |
| 典型场景 | 对每个调用方设 QPS 上限 | 大促关闭退款功能 | 支付渠道异常时自动切断 |
| 常用工具 | Sentinel、Guava RateLimiter | Sentinel、Hystrix | Sentinel、Hystrix |
举几个真实例子:
- 限流:查询用户信息的服务,给集团内外多个调用方使用,为了保证自身可用性,对每个调用方做 QPS 限流
- 降级:双十一大促期间,淘宝关闭退款功能,把资源留给核心的下单流程
- 熔断:收银台发现微信支付失败率突增、超时严重,临时把微信支付这个渠道熔断掉
如何防止服务雪崩
雪崩是一种极具破坏性的"连锁故障":下游基础服务出问题(响应慢或宕机),上游依赖它的服务因等待响应而阻塞线程、耗尽资源,进而也崩溃,像多米诺骨牌一样沿调用链向上蔓延,最终拖垮整个系统。
雪崩的常见诱因:
- 某个系统出现慢 SQL 或 CPU 打满
- 突发流量超出承受能力
- 上游存在不合理的批任务调用或疯狂重试
- 服务之间存在循环依赖
防雪崩四步法:
- 减少强依赖:能用 MQ 异步的,就不要同步调用
- 优化重试策略:采用指数退避,不要疯狂重试加剧故障
- 避免循环依赖:单向调用链更安全
- 限流降级熔断一把梭:如果下游崩了我不跟着崩,最彻底的兜底方案
灰度发布与蓝绿部署
新版本上线是微服务运维中的高频操作,常见的发布策略有两种:
蓝绿部署:准备两套完全相同的环境(蓝/绿)。初始状态用户流量打到蓝环境,新版本部署到绿环境,测试通过后切流。切流可以逐步进行,先切一部分观察,再逐步扩大。
- 优点:回滚极快,只需把流量切回蓝环境
- 缺点:资源浪费,一半机器闲置
灰度发布(金丝雀发布):在现有集群中选一部分机器发新版本,引流一小部分用户验证。验证通过后逐步扩大比例,最终全量发布。
- 优点:不需要闲置环境,资源利用率高
- 缺点:回滚需要重新部署,不如蓝绿快
灰度发布是目前最主流的做法。配合快速回滚机制(发布前记录基线,回滚时基于基线快速部署),可以兼顾安全和效率。
CI/CD:持续集成与持续部署
微服务数量多,手动发布不现实,必须依赖自动化。CI/CD 是 DevOps 文化的核心实践:
- 持续集成(CI):开发者提交代码后,自动构建、自动跑单元测试,确保代码能正确集成
- 持续交付(CD - Delivery):在 CI 基础上,自动部署到类生产环境(Staging),随时可以手动触发发布
- 持续部署(CD - Deployment):在交付基础上,进一步自动化,代码合并后直接自动发布到生产环境
三者是递进关系,自动化程度逐级提升。
DevOps 的本质是打破开发和运维的壁垒。以前开发负责写代码,运维负责发布部署,出了问题各看各的。DevOps 让开发参与运维,对自己的服务全生命周期负责。之所以能这样做,是因为 CI/CD 等自动化工具越来越成熟。
分布式链路追踪
一次用户请求可能经过 5 个、10 个甚至更多的微服务。当出现问题时,到底是哪个服务出了问题?耗时瓶颈在哪里?这就需要链路追踪。
核心思路很简单——解决两个问题:
1. 生成全局 traceId
在请求入口(HTTP 入口、定时任务调度入口等)生成一个全局唯一的 traceId。
2. 传递 traceId
每经过一个服务就带着这个 traceId 往下传。traceId 通过 HTTP 请求头、RPC 请求头、MQ 消息头进行传递。在服务内部,通过 ThreadLocal(多线程场景用 TransmittableThreadLocal)在线程间透传。
除了 traceId,每个服务节点还会记录一个 Span,包含操作名称、开始/结束时间、状态码、标签等信息。通过 traceId 把所有 Span 串起来,就能还原完整的调用链。
常用工具:SkyWalking(国产,功能强大)、Zipkin(Twitter 开源,轻量)。
服务注册与发现选型
注册中心是微服务架构中的"通讯录",所有服务启动时把自己注册上去,调用方通过注册中心查找目标服务的地址。
主流注册中心对比:
| 维度 | Nacos | Eureka | Consul | Zookeeper |
|---|---|---|---|---|
| CAP 模型 | CP + AP 双模式 | AP | CP | CP |
| 健康检查 | TCP/HTTP/心跳 | 心跳 | TCP/HTTP/gRPC | Keep Alive |
| 一致性算法 | Raft + Distro | Gossip | Raft | ZAB |
| 雪崩保护 | 有 | 有 | 无 | 无 |
| Spring Cloud 集成 | 支持 | 支持 | 支持 | 支持 |
| Dubbo 集成 | 支持 | 不支持 | 支持 | 支持 |
| 维护状态 | 活跃 | 已停更 | 活跃 | 活跃 |
选型建议:
- 如果用 Spring Cloud Alibaba 或 Dubbo,首选 Nacos,生态最契合
- 如果对一致性要求极高,考虑 Consul 或 Zookeeper
- Eureka 已停更,SpringBoot 3.0 后不再维护,新项目不建议使用
- Nacos 同时支持 AP 和 CP,是目前最灵活的选择
配置中心的作用
在微服务架构中,配置散落在几十甚至上百个服务的配置文件里,修改一个配置就要改代码、提交、走发布流程,太痛苦了。
配置中心的核心价值:集中管理 + 动态生效。把需要动态调整的配置(业务开关、限流阈值、线程池大小、日志级别等)放到配置中心,修改后实时推送到所有服务,无需重启。
| 配置类型 | 建议放哪里 |
|---|---|
| 应用元信息(名称/版本) | 本地配置文件 |
| 中间件连接信息(DB/Redis) | 本地配置文件 |
| 业务开关/策略配置 | 配置中心 |
| 运行时调优参数(线程池/超时) | 配置中心 |
| 日志/监控采样策略 | 配置中心 |
主流配置中心:Nacos(推荐,同时兼任注册中心)、Apollo(携程开源,功能完善)。
常见面试题精选
Q1:微服务的优缺点分别是什么?
优点:独立部署、技术栈灵活、团队自治、按需扩展、故障隔离。缺点:分布式复杂度高(网络延迟、数据一致性)、运维成本大(几十上百个服务要监控管理)、排查问题困难(一个请求跨多个服务)。
Q2:如何判断系统是否需要微服务?
团队少于 10 人、日活不到万级、业务模型还在快速变化的阶段,不建议上微服务。单体架构加上良好的模块化设计,足以应对。微服务是为了解决规模化问题,不要为了微服务而微服务。
Q3:微服务拆分的粒度怎么把握?
不要太粗(等于没拆),也不要太细(维护成本爆炸)。一个经验法则:一个服务由一个"两个披萨团队"(5-8 人)能独立维护为宜。另外,可以先粗后细,随着业务理解加深逐步拆分。
Q4:什么是 DevOps?和微服务什么关系?
DevOps 是一种开发文化,强调开发和运维紧密结合,通过自动化工具实现快速、频繁、可靠的软件交付。微服务架构天然需要 DevOps 来支撑——服务数量多、部署频繁,没有自动化的 CI/CD 流程,微服务根本玩不转。
Q5:康威定律在微服务中有什么指导意义?
康威定律告诉我们:系统架构反映组织沟通结构。所以在做微服务拆分时,要同步调整团队组织:按业务线划分团队,每个团队负责自己的一组服务的全生命周期(开发、测试、部署、运维)。先调组织,再调架构。
小结
微服务不是银弹,它解决了单体的痛点,也带来了新的复杂度。理解下面这几个要点,就抓住了微服务的精髓:
- 拆分有原则:单一职责、限界上下文、数据隔离,不是随便乱砍
- 治理是关键:注册发现、配置管理、容错保护、链路追踪,缺一不可
- 组织决定架构:康威定律告诉我们,先调组织再调架构
- 保命三板斧:限流、降级、熔断,是微服务稳定性的最后防线
- 自动化是前提:没有 CI/CD 和 DevOps 支撑的微服务,只会让事情变得更糟
- 没有最好的方案,只有最合适的:根据业务规模和团队能力选择技术栈
Q6:限流一般做在调用方还是被调用方?
一般做在被调用方(服务端)。因为被调用方最清楚自己能扛多少流量。但也有例外:当下游服务能力有限时(比如大促依赖银行接口),调用方也可以主动限流,避免把下游打挂。
Q7:微服务架构下如何做服务监控?
通常需要三个维度的监控:指标监控(Prometheus + Grafana,采集 CPU/内存/QPS/RT 等指标)、日志监控(ELK 或 SLS,集中收集和检索日志)、链路追踪(SkyWalking/Zipkin,追踪请求的完整调用链)。三者结合,才能全面掌握微服务的运行状态。
Q8:微服务和 Serverless 有什么关系?
Serverless(无服务架构)是分布式架构的另一种形态。它将基础设施管理完全交给云平台,开发者只需要写业务逻辑(以函数形式运行),通过事件驱动触发。Serverless 适合快速开发、弹性扩展和按需计费的场景。微服务和 Serverless 不是替代关系,而是可以结合使用:微服务中的某些轻量级功能(如消息处理、定时任务)可以用 Serverless 函数实现。
面试时如果被问到"微服务怎么选型",不要背方案,而是从业务场景出发,说出为什么选、怎么权衡。面试官更看重你的思考过程,而不是你背了多少组件名称。