微服务架构


一、单体架构 vs 微服务架构

1.1 什么是单体架构

把所有功能模块(用户、订单、支付、库存……)打包成一个应用部署。就像一家餐厅——厨师、服务员、收银员全挤在一个屋子里,共享同一个厨房(数据库)、同一套工具。

优点:部署简单、调试方便、一个项目就能跑起来。

缺点:代码耦合严重,改一行代码要重新部署整个应用;一个模块出了 Bug 可能把整个系统拖垮;技术栈被锁死,不能单独升级某个模块。

1.2 什么是微服务架构

把一个大系统拆成多个独立的小服务,每个服务只负责一件事,各自部署、各自维护。就像一家连锁餐饮集团——中央厨房(API Gateway)负责接单,各个分店(微服务)独立运营,互不干扰。

维度单体架构微服务架构
部署一起部署,改一个全重来独立部署,改哪个重启哪个
技术栈全部统一每个服务可以用不同语言/框架
扩展整体扩容,浪费资源按需扩容——订单服务扛不住就只扩订单
故障隔离一个模块挂了全站挂一个服务挂了,其他服务正常运行
开发效率初期快,后期代码量大了变慢初期慢(要搭基础设施),后期各团队并行开发快
复杂度代码复杂但架构简单代码可能简单但运维复杂

一句话讲清:“单体架构适合早期快速验证,微服务适合业务复杂到一定程度后的拆分。不是越早拆越好——过早微服务化等于给自己找一堆运维麻烦。“


二、服务拆分原则

2.1 按业务领域拆(DDD 思想)

不要按技术层拆(数据层、接口层、逻辑层),而是按业务能力拆:


graph TD

  subgraph 电商系统

    User[用户服务<br/>注册/登录/用户信息]

    Product[商品服务<br/>上架/搜索/详情]

    Order[订单服务<br/>下单/支付/退款]

    Stock[库存服务<br/>扣减/补货]

    Logistics[物流服务<br/>发货/物流追踪]

  end

  User -.-> DB1[(用户DB)]

  Product -.-> DB2[(商品DB)]

  Order -.-> DB3[(订单DB)]

  Stock -.-> DB4[(库存DB)]

  Logistics -.-> DB5[(物流DB)]

2.2 拆分的三条铁律

原则说明违反的后果
高内聚一个服务内的功能应该紧密相关用户服务里混了订单逻辑 → 改订单要动用户服务
低耦合服务之间只通过 API 通信,不共享数据库订单服务直接读库存表 → 库存表结构一改订单就崩
单一职责一个服务只做一件事商品搜索和商品推荐放一个服务 → 搜索挂了推荐也挂

三、服务注册与发现

3.1 问题:服务怎么找到彼此?

微服务架构下,服务实例的 IP 地址和端口是动态变化(容器随时扩缩容)。不能在代码里写死 http://192.168.1.100:8080——这个 IP 明天可能就变了。

3.2 解决方案:注册中心


sequenceDiagram

  participant OrderSvc as 订单服务

  participant Registry as 注册中心

  participant StockSvc as 库存服务

  Note over OrderSvc,Registry: ① 服务启动

  OrderSvc->>Registry: 报到!地址: 192.168.x.x

  StockSvc->>Registry: 报到!地址: 192.168.x.y

  Note over OrderSvc,StockSvc: ② 服务调用

  OrderSvc->>Registry: 我要找库存服务

  Registry-->>OrderSvc: 地址列表 [x.y, x.z]

  OrderSvc->>OrderSvc: 负载均衡选一个

  Note over OrderSvc,StockSvc: ③ 服务下线

  StockSvc--xRegistry: 心跳超时

  Registry->>Registry: 从列表移除

  OrderSvc->>Registry: 找库存服务

  Registry-->>OrderSvc: 列表已更新(不含挂的实例)

3.3 常见注册中心对比

注册中心开发者一致性模型CAP 选择特点
EurekaNetflixAP可用性优先自我保护机制,临时节点
Nacos阿里巴巴CP/AP 可切换灵活支持配置中心,社区活跃
ZookeeperApacheCP一致性优先强一致但写性能低
ConsulHashiCorpCP一致性优先内置健康检查和 KV 存储

一句话讲清:“注册中心选型看场景——需要强一致用 Zookeeper/Consul,需要高可用用 Eureka,想功能全就用 Nacos。阿里系项目普遍用 Nacos,因为它同时解决了注册中心和配置中心两个问题。“


四、API Gateway(网关)

4.1 为什么需要网关

如果没有网关,客户端(App/网页)需要直接知道每个微服务的地址——几十个服务的地址全写在前端代码里,改一个地址就要重新发版。

网关就是所有请求的统一入口——客户端只认网关一个地址,网关负责把请求转发到后面的微服务。

4.2 网关的核心功能

功能说明类比
路由转发根据 URL 把请求转到对应的微服务酒店前台——“301 房间?找客房部”
负载均衡同一个服务有多个实例时,均匀分配请求高速收费站——车多就多开几个窗口
认证鉴权统一验证 Token,没登录的不让进门卫——没工作证不让进大楼
限流熔断防止某个服务被打爆影响全局保险丝——电流过大自动断开
协议转换对外暴露 HTTP,内部用 gRPC翻译官——对外说中文,对内说英文

4.3 常见网关方案

方案特点
Spring Cloud GatewayJava 生态首选,响应式架构
Kong基于 Nginx/OpenResty,高性能,插件丰富
Nginx轻量级网关,适合简单路由场景
APISIX国产开源,基于 Nginx + Lua,动态配置

五、服务间通信

5.1 同步通信:REST vs gRPC

维度REST (HTTP/JSON)gRPC (HTTP/2 + Protobuf)
协议HTTP/1.1HTTP/2
数据格式JSON(文本,可读)Protobuf(二进制,不可读但极快)
性能一般比 REST 快 5-10 倍
跨语言天然支持(HTTP 标准)需要生成代码(Proto 文件定义接口)
学习成本中等
适用场景对外 API、前后端交互服务内部通信、高性能要求

一句话讲清:“REST 适合对外暴露的 API——简单、跨平台、调试方便。gRPC 适合内部微服务之间的通信——二进制协议性能高,Proto 文件定义接口还能做编译期类型检查。两者不矛盾,网关对外用 REST,内部用 gRPC。“

5.2 异步通信:消息队列

服务之间不需要实时返回结果的场景,用消息队列解耦。比如用户下单后不需要等库存扣减完成——把消息扔到 MQ 就返回,库存服务自己慢慢消费。

具体 MQ 选型参见 06-消息队列MQ


六、服务熔断与降级

6.1 熔断器模式(Circuit Breaker)

当下游服务出问题时,不要让请求继续傻等——直接断开,返回兜底数据。


stateDiagram-v2

  [*] --> Closed

  Closed --> Open: 失败率超过阈值

  Open --> HalfOpen: 冷却时间到

  HalfOpen --> Closed: 试探成功

  HalfOpen --> Open: 试探失败

  state Closed {

    [*] --> 正常放行所有请求

  }

  state Open {

    [*] --> 直接拒绝请求返回兜底数据

  }

  state HalfOpen {

    [*] --> 放一个请求试探

  }

6.2 降级策略

系统扛不住时,主动砍掉非核心功能,保住核心链路:

降级级别策略示例
一级降级返回缓存数据商品评价服务挂了 → 返回缓存里的评价
二级降级返回默认数据推荐服务挂了 → 返回热门商品列表
三级降级功能直接关闭积分服务挂了 → “积分功能维护中”

6.3 常见熔断框架

框架开发者特点
HystrixNetflix经典但已停止维护
Sentinel阿里巴巴实时监控+规则动态推送,Spring Cloud 生态
Resilience4j社区轻量级,函数式编程风格,Hystrix 替代品

详细对比参见 08-限流与熔断降级


七、分布式事务

7.1 问题:跨服务的数据一致性

订单服务创建订单和库存服务扣减库存不在同一个数据库——没法用一个本地事务搞定。如果订单创建了但库存没扣,数据就不一致了。

7.2 解决方案一:Saga 模式

把一个长事务拆成一系列本地小事务,每个小事务都有对应的补偿操作。正向执行失败时,反向调用补偿操作回滚。


graph LR

  subgraph Forward[正向]

    S1[创建订单] --> S2[扣减库存]

    S2 --> S3[扣减余额]

    S3 --> Done[完成]

  end

  subgraph Backward[反向 补偿]

    S2x[补余额] --> S1x[补库存]

    S1x --> Cancel[取消订单]

  end

  S1 -. 失败 .-> Cancel

  S2 -. 失败 .-> S2x

  S3 -. 失败 .-> S3x[补余额]

  S3x --> S2x

优点:每个服务只关心自己的本地事务,实现简单。

缺点:最终一致性,中间状态对外可见;补偿逻辑复杂且容易出 Bug。

7.3 解决方案二:TCC(Try-Confirm-Cancel)

两阶段提交的变种——先把资源预留起来,确认没问题再真正提交。


graph LR

  T[Try 预留] -->|冻结100元<br/>钱在账户被锁住| C[Confirm 确认]

  T -->|失败| Cancel[Cancel 取消]

  C -->|真正扣减100元| Done[完成]

  Cancel -->|解冻100元恢复原状| Rollback[回滚]

  C -->|失败| Cancel

优点:资源预留保证了一致性。

缺点:每个服务都要实现 Try/Confirm/Cancel 三个接口,开发量大。

7.4 三种方案对比

方案一致性实现难度性能适用场景
2PC强一致差(锁资源)同一数据库内的跨表事务
TCC强一致高(三个接口)资金相关,需要预留机制
Saga最终一致长事务,业务流程可补偿

八、服务网格(Service Mesh)

8.1 什么是 Service Mesh

把微服务之间通信的复杂逻辑(熔断、限流、负载均衡、监控)从应用代码里抽出来,下沉到基础设施层。应用代码只管写业务逻辑,不用管通信怎么搞。

8.2 Sidecar 模式

每个服务实例旁边都挂一个代理进程(Sidecar),所有进出流量都经过这个代理。代理之间自动处理路由、熔断、监控。


graph LR

  subgraph Without[没有Service Mesh]

    WA[服务A] -->|代码调代码| WB[服务B]

    WA -.->|A代码要写重试超时熔断| R1[A代码耦合]

  end

  subgraph With[有Service Mesh]

    SA[服务A] --> SA_S[Sidecar A]

    SA_S -->|网络| SB_S[Sidecar B]

    SB_S --> SB[服务B]

    SA_S -.->|重试超时熔断在Sidecar| R2[服务代码干净]

  end

8.3 Istio vs Linkerd

维度IstioLinkerd
开发者Google + IBMBuoyant
底层代理EnvoyLinkerd2-proxy (Rust)
功能丰富度极高(流量管理、安全、可观测)精简(核心通信)
资源消耗较高极低
学习曲线陡峭平缓
适用场景大规模复杂微服务中小规模,轻量需求

一句话讲清:“Service Mesh 的核心价值是让业务代码和基础设施解耦。业务开发不用关心服务间通信的复杂性,运维团队统一管理流量策略。但 Service Mesh 引入了额外的延迟和运维成本,不是所有项目都需要——中小项目用好 Spring Cloud + Sentinel 就够了。“


九、快速问答

问题一句话答案
微服务和单体架构怎么选?早期用单体快速验证,业务复杂到一定程度再拆微服务;过早微服务化是自找麻烦
微服务拆分原则是什么?高内聚低耦合,按业务领域拆,一个服务只做一件事,不共享数据库
服务注册与发现是什么?服务启动时向注册中心报到,调用时先问注册中心目标在哪——解决动态 IP 问题
Eureka 和 Nacos 区别?Eureka 只做注册(AP),Nacos 同时做注册+配置中心,支持 CP/AP 切换
API Gateway 的作用?统一入口——路由转发、负载均衡、认证鉴权、限流熔断,客户端只认网关一个地址
REST 和 gRPC 怎么选?REST 对外 API(简单跨平台),gRPC 内部通信(高性能二进制协议)
什么是熔断器模式?下游出问题→自动断开请求→返回兜底数据→冷却后试探恢复;三态:关闭→打开→半开
Hystrix 和 Sentinel 区别?Hystrix 已停维护,Sentinel 是阿里开源的替代品,支持实时监控和动态规则推送
分布式事务怎么解决?2PC(强一致但慢)、TCC(预留资源)、Saga(最终一致+补偿)——根据一致性要求选型
Service Mesh 解决什么问题?把熔断/限流/路由等通信逻辑从应用代码抽到 Sidecar 代理,业务代码只写业务逻辑
微服务有哪些常见问题?服务雪崩、数据一致性、链路追踪、配置管理、部署复杂——都需要配套基础设施解决

速记卡(面试闪卡)

Q1:一句话讲清「微服务架构」到底是什么?

A:微服务把单体大系统拆成多个独立小服务,各自部署维护;适合业务复杂到一定程度,过早拆是自找运维麻烦。

Q2:一、单体 vs 微服务(monolith vs microservice) —— 怎么理解?

A:单体像一家小餐馆——厨师收银全挤一屋,部署简单但改一行要重发整个应用,一个 Bug 拖垮全场。微服务像连锁集团——中央厨房(网关)接单,各分店独立运营互不干扰,改哪个重启哪个、挂一个不影响其他。不是越早拆越好。

Q3:二、拆分原则与注册发现(DDD & registry) —— 怎么理解?

A:按业务领域拆(DDD),不是按技术层:高内聚(功能紧密相关)、低耦合(只走 API、不共享库)、单一职责。服务 IP 动态变,不能写死——启动时向注册中心(Eureka/Nacos/Zookeeper)报到,调用时先问注册中心拿到地址列表再做负载均衡。

Q4:三、网关与通信(API Gateway & gRPC) —— 怎么理解?

A:网关是所有请求的统一入口:路由转发、负载均衡、认证鉴权、限流熔断、协议转换(对外 REST 中文、对内 gRPC 英文)。服务间同步用 REST(对外)或 gRPC(内部,快 5-10 倍二进制);不需实时返回就扔消息队列解耦。

Q5:四、熔断、分布式事务与 Service Mesh —— 怎么理解?

A:下游挂了用熔断器(Closed→Open→HalfOpen)断流返兜底,扛不住就降级砍非核心。跨库一致性用 2PC(强一致慢)/TCC(预留资源)/Saga(最终一致+补偿)。Service Mesh 把通信逻辑下沉到 Sidecar 代理,业务代码只写业务,但中小项目用 Spring Cloud+Sentinel 就够了。

Q6:核心速记主线有哪些?

  • 概念:单体拆成独立小服务,各部署各维护

  • 拆分:按业务领域,高内聚低耦合单一职责

  • 发现:注册中心报到,调用先问地址

  • 通信:网关统一入口;REST 对外 gRPC 对内

  • 容错:熔断降级 + 分布式事务(2PC/TCC/Saga)

口诀

A:微服务把单体拆,

各店独立互不碍;

注册发现动态来,

熔断降级保态在。

相关链接