Java - 微服务篇

开篇:先校准版本坐标

这篇笔记的主体是面试速查用的知识点,但组件选型会过期。所以先放一张版本表,后面的内容都在这个坐标系里:

项目当前状态(2026-10)
Spring Boot4.0(2025-11-20 GA),基于 Spring Framework 7,JDK 17 为基线
Spring Cloud2025.1(Oakwood) 为稳定列车,对齐 Boot 4.0;2026.0(Paddington)在里程碑阶段
Spring Cloud Alibaba2025.1.0.0,适配 Spring Boot 4
Nacos3.x(3.0 起新增 MCP Registry,3.1 跟进新版注册协议)
Seata2.6.x(Raft 高可用集群、HTTP 过滤器体系)
XXL-JOB3.x(3.0 升级到 Spring Boot 3 + JDK 17)

面试遇到「Spring Cloud 五大组件是什么」该怎么答。

那套答案(Eureka + Ribbon + Hystrix + Zuul + Config)是 2018 年前后 Netflix OSS 全家桶的记忆。今天如果当作”当前方案”背出来,是很明显的知识陈旧信号。正确的答法是先说能力分类,再说现在谁来实现:

注册发现 + 配置中心 → Nacos|网关 → Spring Cloud Gateway|负载均衡 → Spring Cloud LoadBalancer|熔断限流降级 → Sentinel 或 Resilience4j|链路追踪 → Micrometer Tracing + OpenTelemetry

能力是稳定的,组件是会更替的。记住前者,后者随时能查。


4.1 Spring Cloud 组件全景

能力现在的主流实现上一代(已退场或有变化)变化说明
注册中心Nacos、ConsulEurekaEureka 2.0 中止开发,1.x 已停止演进
配置中心Nacos Config、ApolloSpring Cloud Config ServerConfig Server 仍可用,但国内基本被 Nacos 取代
网关Spring Cloud GatewayZuul 1Zuul 1 基于阻塞 IO,已淘汰
服务间调用RestClient / WebClient / 声明式 @HttpExchangeRestTemplate、OpenFeignOpenFeign 仍在用,但已不是唯一选择
负载均衡Spring Cloud LoadBalancerRibbonRibbon 于 2020 年(Ilford / 2020.0.x)被移除
熔断降级Sentinel(国内)/ Resilience4j(Spring 官方默认)HystrixHystrix 最后版本 1.5.18(2018),已停更
链路追踪Micrometer Tracing + OpenTelemetrySleuthBoot 3 起 Sleuth 不再适用
分布式事务Seata 2.x——模式不变,能力增强(Raft 集群)

这张表怎么用:左边是”现在用什么”,右边是”为什么换”。

面试里最能加分的一句是:“Ribbon / Hystrix 解决的问题依然存在,只是实现换了。” 客户端负载均衡、熔断降级、网关路由这些能力不会因为组件退役而消失 —— 相反,Micrometer Tracing 统一到 OpenTelemetry 之后,跨语言链路的兼容性反而比以前更好。

下文对 Ribbon、Hystrix 的策略细节做了保留,因为理解它们的设计思路,才能理解后继者在哪些地方做了取舍。


4.2 注册中心(Nacos vs Eureka)

注册中心的作用:

  • 服务注册:保存服务名称、IP、端口等信息
  • 服务发现:消费者拉取服务列表,有集群时通过负载均衡选择调用
  • 服务监控:服务提供者定期发送心跳,超时无心跳则剔除

Nacos vs Eureka

共同点:

  • 都支持服务注册和服务拉取
  • 都支持服务提供者心跳方式做健康检测

区别:

维度NacosEureka
健康检测临时实例心跳 + 非临时实例主动检测仅心跳
消息推送服务列表变更主动推送,更及时定时拉取
CAP 模式默认 AP,存在非临时实例时 CPAP
配置中心✅ 支持❌ 不支持
通信协议2.x 起 gRPC 长连接HTTP 短轮询

健康检查方式决定故障发现延迟。 心跳模式最慢要几十秒才能摘除一个死实例,主动探测更快。几十秒 × 每秒调用量 = 故障期间被白白浪费的请求数 —— 这是选型时经常被忽略的一项。

Nacos 3.x 的变化

Nacos 3.0 的重心从”纯微服务注册中心”往”AI 时代的服务/工具注册中心”挪了:

  • MCP Registry:把 MCP Server 也纳入注册与发现。配合本博客前面几篇 MCP 文章看会更直观 —— 当 Agent 要连多个 MCP Server 时,“去哪儿找有哪些工具”变成了和”服务发现”同构的问题,Nacos 把它当成同一类基础设施来做。
  • 安全默认收紧:3.0 起默认开启鉴权,不再是可以裸奔上线就完事的版本。升级时最常见的报错是启动时提示 NACOS_AUTH_IDENTITY_KEY must be set —— 必须先把 NACOS_AUTH_IDENTITY_KEY / NACOS_AUTH_IDENTITY_VALUE / NACOS_AUTH_TOKEN 三个密钥配好。另外 3.x 早期版本曾有一处部分 API 鉴权范围错误,官方发布了风险说明和修复版本,3.0 系列请升级到已修复的补丁版,不要停在首发版本上。
  • Kubernetes 集成增强:与控制面生态的对接更顺。

冷知识但有用:Nacos 3.0 自身要求 Spring Boot 3 + JDK 17。如果你的 Spring Cloud Alibaba 版本还停在 Boot 2.x,Nacos 客户端和服务端会形成版本错配,升级顺序建议是 Boot → Spring Cloud → Spring Cloud Alibaba → Nacos 服务端。


4.3 服务间调用与负载均衡

负载均衡 = 消费者从注册中心拿到多个服务提供者地址后,决定这次请求发给哪个实例。分两种:

  • 服务端负载均衡:Nginx、LVS,转发到后端
  • 客户端负载均衡:调用方自己持有实例列表并选择,Spring Cloud LoadBalancer 属于这种

服务间调用方式

负载均衡决定”发给谁”,调用方式决定”怎么发”。Java 生态里现役的有四种:

方式同步 / 响应式状态说明
RestClient同步✅ 推荐用于新代码Spring 6.1 引入,是 RestTemplate 的现代替代
WebClient响应式✅ 推荐非阻塞,适合高并发 / 流式场景
声明式 HTTP 接口(@HttpExchange)取决于底层客户端✅ Spring 原生Spring 6.0 引入,写接口 + 注解,框架生成实现
OpenFeign同步⚠️ 仍在用,但不再是唯一选择Spring Cloud 体系里的老牌方案;新项目可权衡是否改用原生声明式接口
RestTemplate同步维护模式仍可用,但新代码建议用 RestClient

声明式调用的写法已经统一得差不多了:

// Spring 原生:定义一个接口,加注解,注入即用
@HttpExchange("/users")
public interface UserClient {

    @GetExchange("/{id}")
    User getById(@PathVariable Long id);
}

// 注册
@Configuration
class ClientConfig {
    @Bean
    UserClient userClient(RestClient.Builder builder, LoadBalancedRestClientBuilderCustomizer lb) {
        RestClient restClient = builder.baseUrl("http://user-service").build();
        return HttpServiceProxyFactory
            .builderFor(RestClientAdapter.create(restClient))
            .build()
            .createClient(UserClient.class);
    }
}

关键点:baseUrl 写的是服务名而不是 IP,客户端负载均衡在这里介入,把服务名解析成具体实例。这正好接上下面 Ribbon / LoadBalancer 的内容 —— 调用方式和负载均衡是两个独立的选择,可以自由组合。

Ribbon 时代的策略(了解即可)

策略说明
WeightedResponseTimeRule按权重,响应时间越长权重越小
RandomRule随机选择
BestAvailableRule忽略短路的,选择并发数较低的
RetryRule重试机制
AvailabilityFilteringRule先过滤非健康,再选连接数较小的
ZoneAvoidanceRule按 Zone 分类,Zone 内轮询(默认)

Ribbon 已于 2020 年从 Spring Cloud 移除,取而代之的是 Spring Cloud LoadBalancer。

变化不只是换了个名字 —— 策略集大幅收缩了。上表那些策略都是 Ribbon 时代的产物,Spring Cloud LoadBalancer 开箱只提供轮询(RoundRobin)和随机(Random)两种。

现在怎么做负载均衡

需求做法
默认均匀分发RoundRobinLoadBalancer,开箱即用
随机RandomLoadBalancer
按实例权重(Nacos 控制台配的权重)自定义 ReactorServiceInstanceLoadBalancer,读 ServiceInstance 的 weight 元数据
同机房 / 灰度 / 金丝雀自定义选择器,基于 metadata 过滤后再做轮询
// 自定义加权轮询的骨架:注册成 LoadBalancerClientConfiguration 里的 Bean
public class WeightedLoadBalancer implements ReactorServiceInstanceLoadBalancer {

    private final ObjectProvider<ServiceInstanceListSupplier> supplier;

    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        return supplier.getIfAvailable().get()
            .next()
            .map(instances -> new DefaultResponse(pickByWeight(instances)));
    }
}

这个收缩其实反映了设计取向的变化:负载均衡策略本身不该那么复杂,真正的保护应该交给熔断、限流和超时。Ribbon 时代那种”按响应时间加权”的策略,在有了熔断器之后价值就没那么大了。

换句话说:复杂流量治理从”负载均衡器”这一层,下沉到了”独立治理组件 + 网关”这一层。


4.4 服务雪崩与熔断降级

服务雪崩:分布式系统中某个基础服务故障 → 故障像多米诺骨牌沿调用链蔓延 → 大面积瘫痪。

服务降级

方式说明
返回兜底数据超时或异常时返回预设默认值
开关降级配置中心动态关闭非核心功能
读写降级暂停非核心写入,限制只读缓存
限流降级直接拒绝多余请求

降级和熔断的区别常被问:熔断是”切断”,按状态机自动判断;降级是”兜底”,是熔断之后返回什么。 熔断是机制,降级是策略。

熔断器状态机(通用模型)

三步走:关闭(CLOSED) → 开启(OPEN) → 半开(HALF_OPEN)

  • CLOSED:正常放行,统计失败率
  • OPEN:直接拒绝,走降级逻辑,同时开一个冷却计时器
  • HALF_OPEN:冷却结束,放少量探测请求进来 —— 成功则回到 CLOSED,失败则继续 OPEN

这套模型是通用的,Hystrix、Sentinel、Resilience4j 都是同一套。变的是触发条件的默认值和可配置粒度。

现在用哪个

方案生态位置特点适合
Sentinel阿里开源,国内主流控制台可视化、规则动态下发、限流 + 熔断 + 热点参数限流一体已有 Nacos 生态、需要动态规则和可视化
Resilience4jSpring Cloud Circuit Breaker 的默认实现函数式、轻量,组合式装饰器追求和 Spring Boot 原生集成、不想引入额外控制台
Hystrix已停更——只作为理解历史用

两个版本细节值得记:

  • Sentinel 的默认熔断条件是:20 秒统计窗口内,异常比例超过 50% 且请求数 ≥ 5 才熔断,熔断时长默认 5 秒。它还支持慢调用比例作为熔断条件 —— Hystrix 没有这个能力。
  • Resilience4j 2.4.0 专门提供了 resilience4j-spring-boot4 模块来支持 Spring Boot 4。升 Boot 4 时别只升主依赖,这个模块必须一起换。

另外,Sentinel 也覆盖限流(见 4.5)。国内实践中通常限流和熔断都用 Sentinel 一套搞定,不再像 Hystrix 时代那样分两个组件。

一个容易踩的坑:熔断配置要匹配真实失败模式

熔断器只看”失败率”,但真实故障分两类:

  • 快速失败(下游直接拒绝连接):失败率立刻飙升,熔断器秒开 ✅
  • 慢响应(下游不报错,只是变慢):失败率可能一直是 0,但线程全被占满,雪崩照样发生 ❌

第二类必须靠”慢调用比例”或超时来兜底。 只配异常比例的熔断,对慢故障是无效的 —— 这是个面试和实战都常被问到的点。


4.5 限流

限流实现方法

层面方案算法
Tomcat设置最大连接数 / 最大线程数计数
Nginxlimit_req / limit_conn漏桶(请求排队、匀速处理)
网关Spring Cloud Gateway RequestRateLimiter令牌桶(可应对突发)
应用层Sentinel / Resilience4j RateLimiter滑动窗口 / 令牌桶
分布式Redis + Lua滑动窗口 / 令牌桶(多实例共享计数)

漏桶 vs 令牌桶(面试高频)

漏桶(Leaky Bucket)令牌桶(Token Bucket)
速率恒定流出速率恒定生成令牌,允许积攒
突发流量超出速率就直接丢或排队允许短时突发(桶里攒了令牌)
典型实现Nginx limit_reqGateway、Guava RateLimiter

一句话:漏桶平滑,令牌桶容忍突发。 面向外部用户的网关一般用令牌桶(用户点一下按钮就来一波),保护下游内部服务一般用漏桶或滑动窗口(下游必须匀速才不垮)。

分布式限流

单机限流的问题很明显:10 个实例,每个配 100 QPS,总量就成了 1000 QPS,而你想保护的下游可能只扛得住 300。

标准做法是 Redis + Lua 做共享计数,Lua 保证”读 + 判断 + 写”的原子性:

-- 滑动窗口限流的简化示意(key 按秒切片)
local current = redis.call('INCR', KEYS[1])
if current == 1 then
  redis.call('EXPIRE', KEYS[1], ARGV[1])
end
if current > tonumber(ARGV[2]) then
  return 0
end
return 1

代价是每次请求多一次 Redis 往返。所以常见的折中是 “单机限流保底 + 网关分布式限流兜底”:单机限流处理突发,网关做全局总量控制。


4.6 CAP 与 BASE

CAP 定理:一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance),三个指标无法同时满足。

注意一个常见误述:P 在分布式系统里是必须的(网络分区是物理事实,不是你能选的),所以真正的选择是 分区发生时,要 C 还是要 A。说”三选二”是不准确的。

BASE 理论:对 CAP 的解决思路。

思想说明
Basically Available(基本可用)故障时允许损失部分可用性,保证核心可用
Soft State(软状态)允许出现中间状态(临时不一致)
Eventually Consistent(最终一致性)不保证强一致,但最终达到数据一致

解决分布式事务的思想

思想模式做法
最终一致(AP)各分支分别执行提交,不一致再恢复异步、高性能
强一致(CP)各分支执行完等待彼此结果,统一提交或回滚同步、低性能

4.7 分布式事务(Seata / Saga / Outbox)

Seata 架构

三个核心角色:

角色全称职责
TCTransaction Coordinator(事务协调者)维护全局和分支事务状态,协调提交或回滚
TMTransaction Manager(事务管理器)定义全局事务范围,发起提交或回滚
RMResource Manager(资源管理器)管理分支事务资源,注册分支、报告状态、执行提交回滚

Seata 三种模式

模式CAP原理性能侵入性
XACP各分支互相等待,保证强一致差低
ATAP基于 undo log 实现,一阶段提交 + 二阶段异步补偿好最低
TCCAP需人工编码 Try/Confirm/Cancel较好最高

AT 是最常用的:业务代码几乎无感,代价是依赖 undo log,且不是所有 SQL 都能被正确解析。

TCC 只在资金类场景值得用 —— 它要求你为每个操作写三个方法,还要处理空回滚、悬挂、幂等三个经典问题,实现成本远高于 AT。

Seata 2.x 的演进

  • 2.0 起 TCC 架构重构,并引入 Raft 高可用集群(TC 不再是单点)
  • 2.6 增加 HTTP 过滤器体系、Raft 集群能力增强、更多数据库兼容(SQL Server、达梦等)
  • 配置注册从早期的 Nacos/Consul/ZK 组合,逐步收敛到与 Nacos 的深度集成

Saga:无中心协调器的补偿模式

Seata 的 Saga 模式(以及自研 Saga)走的是另一条路:一串本地事务 + 每一步配一个补偿动作,没有全局锁。

1. 订单服务:创建订单(本地事务)        → 发 OrderCreated
2. 库存服务:扣减库存(本地事务)        → 发 InventoryReserved
3. 支付服务:发起扣款(本地事务)        → 发 PaymentCompleted
4. 订单服务:订单状态改为「已确认」

任何一步失败,就沿反方向发补偿事件。

这是补偿,不是回滚。 中间状态对外可见,所以业务上必须能接受中间态,每一步的补偿也必须幂等(补偿可能执行多次)。

细节展开见另开的一篇:《微服务该怎么拆》第 7.4 节。

MQ 解决分布式事务:Transactional Outbox(事务性发件箱)

A 服务写数据时,在同一事务内把”要发消息”这件事也落库,再由后台进程或 CDC 投递 —— 异步,性能最好。

⚠️ 常见错误写法:直接 mq.send() 是不能和数据库事务绑定的。消息发出去了、事务回滚了,就会不一致。

// ❌ 错误:最后一行不在事务里,中间挂掉就丢事件
orderRepo.save(order);
mq.send("OrderCreated", order);

标准做法是把消息先写进自己库里的 outbox 表(和业务数据同一个本地事务):

@Transactional
void createOrder(Order order) {
    orderRepo.save(order);
    outboxRepo.save(new OutboxEvent("OrderCreated", toJson(order))); // 同一事务
}

再由后台进程轮询、或 CDC(Debezium 之类订阅 binlog)投递到 MQ。

配套要求是消费端必须幂等(见 4.8)—— 因为 MQ 的投递语义是 at-least-once,重复消费是常态而非异常。

详细展开(为什么不能用 Saga 跳过这一步、去重表怎么写)见另开的一篇:《微服务该怎么拆》第七节。

Spring Modulith 内置了这套能力 —— 如果你的服务还处在模块化单体阶段,它能把”事件 + outbox”做成一等公民。

横向对比

方案一致性性能侵入性适用
XA强一致差低传统银行核心、强一致要求
Seata AT最终一致好最低大多数业务的主力方案
TCC最终一致(较强)较好最高资金、库存扣减等高价值操作
Saga最终一致最好中长流程、跨多服务
Outbox + 事件最终一致好中只需要”通知”,不要求同步返回

选型的一句话:能不做分布式事务就不做 —— 先把服务拆对,让跨服务写变成一件稀有的事(详见《微服务该怎么拆》)。分布式事务是拆错的补偿,不是拆对的奖赏。


4.8 接口幂等性

幂等:对同一操作执行一次和执行多次,产生的影响完全相同。

为什么必须幂等

分布式系统里消息投递至少一次(at-least-once),超时无法区分”对方没收到”和”对方收到了但响应丢了”,所以重试是唯一选择,而重试就意味着可能重复。

重复消费是常态,不是异常。幂等不是优化,是必需品。

RESTful API 幂等性

请求方式幂等性
GET✅ 天然幂等
POST❌ 新增操作,不幂等(除非带幂等键)
PUT✅(绝对值更新)/ ❌(增量更新)
DELETE✅ 根据唯一值删除,幂等
PATCH❌ 通常是增量,不幂等

方案一:Token + Redis

处理业务的同时删除 token,下次调用时没有 token 就不会执行逻辑。

适用的其实是前端重复提交(下单按钮连点),因为服务端能拿到一个”一次性凭证”。

方案二:分布式锁

public void saveOrder(Item item) throws InterruptedException {
    RLock lock = redissonClient.getLock("heimalock");
    boolean isLock = lock.tryLock(10, TimeUnit.SECONDS);
    try {
        if (!isLock) {
            log.info("下单操作获取锁失败, order:{}", item);
            throw new RuntimeException("新增或修改失败");
        }
        // 下单操作
    } finally {
        lock.unlock();
    }
}

这个例子只解决了并发同时到达的情况。重复投递可能间隔几分钟甚至几小时,锁早就释放了 —— 所以它不能替代幂等。

方案三:唯一索引 / 去重表(最通用)

实际生产中最常用的就是这一种 —— 前两种方案在消息消费做幂等的场景下都不适用:消费者拿不到”token”,重复投递也不受锁保护。

做法是给每个操作一个业务唯一键,插一张去重表,靠数据库的唯一约束拦住重复:

-- event_id 上有唯一索引
INSERT INTO processed_events (event_id) VALUES (?);
-- 唯一键冲突 → 说明处理过了,直接返回成功

为什么推荐它:不依赖应用逻辑正确、不依赖 Redis 可用性,一致性由数据库保证。代价是多一张表和一次写入。

方案四:状态机 / 版本号

有些场景没有天然的业务唯一键,但状态本身带约束:

  • 状态机:只允许合法跃迁(已支付的订单不能再支付)。重复事件打过来时,因为状态已经变了,操作自然不生效。
  • 乐观锁:更新时带上 version,版本不匹配就失败。适合”并发更新同一条记录”。

怎么选

方案适用不适用
Token + Redis前端重复提交(下单按钮连点)消息消费幂等
分布式锁并发写同一条记录消息消费幂等(重复投递时间跨度大)
唯一索引 / 去重表消息消费幂等、接口重试无业务唯一键的场景
状态机 / 乐观锁有生命周期或版本号的实体无状态的操作

关于幂等为什么是”地基”级别的东西(at-least-once 是物理限制、exactly-once 为什么做不到),见**《微服务该怎么拆》第 7.6 节**。


4.9 分布式任务调度(XXL-JOB)

分布式任务调度 = 把分散的、批量的后台定时任务纳入统一管理调度平台,动态分配到多个计算节点执行。

核心架构

组件职责
调度中心(Scheduler)任务配置、触发、分片分配、状态监控
执行器(Executor/Worker)接收指令,执行业务逻辑,上报结果
存储层(Storage)持久化任务状态和日志

XXL-JOB 3.x 的变化

  • 3.0(2025 年初):整体升级到 Spring Boot 3 + JDK 17,老版本 v2.4.x 的数据模型和通信协议保持兼容,可以平滑升级(但建议按官方说明调整 xxl_job_log 的索引)。
  • 3.3.x:补了优雅停机(停机时不再粗暴中断正在跑的任务)、Docker Compose 一键部署,以及一系列健壮性修复。
  • 调度逻辑本身没变 —— 中心化调度 + 执行器注册,和 2.x 是同一套模型。

XXL-JOB 路由策略

路由策略:当同一任务部署了多个执行器时,调度中心决定把任务发给哪台机器的规则。

策略说明
第一个 / 最后一个固定一台,调试时用
轮询依次分发,最常用
随机随机选一台
一致性 HASH按参数固定落到某台,适合有本地缓存的场景
最不经常使用 / 最近最久未使用按使用频率选
故障转移失败后自动切换下一台
忙碌转移跳过正在忙的执行器
分片广播见下

大数据量任务处理 — 分片广播

轮询 vs 分片广播:轮询是把”整个任务”轮流交给不同机器;分片广播是把”一个大任务”拆成多份,所有机器同时并行处理。

分片广播:部署多个实例,路由策略选分片广播,按取模方式分摊到各实例。执行器代码里通过 ShardingUtil.getShardingVo() 拿到当前分片序号和总数,只处理属于自己的那部分数据。

典型场景:全量用户对账、历史数据清理。注意分片是逻辑切分,不是数据隔离 —— 某个实例挂了,它负责的那片数据这一轮就没人处理,需要有补跑机制。


4.10 可观测性(新增)

拆成微服务之后,“一次请求到底经过了哪几个服务、卡在哪一跳” 从一个 debug 断点的问题,变成了一个跨服务关联的问题。这一层没做好,拆分就是负收益。

三支柱

支柱关键点Java 生态
日志 Logs结构化 + trace ID 贯穿Logback + MDC,trace ID 由 Tracing 注入
指标 MetricsRED 方法:Rate / Errors / DurationMicrometer + Prometheus
链路追踪 Traces一次请求的完整调用图与每跳耗时Micrometer Tracing + OpenTelemetry

trace ID 是分水岭。 没有它,你手上只是一堆服务各自的日志文件;有了它,才能把一次故障的所有证据串成一条线。

为什么是 OpenTelemetry

Micrometer Tracing 的底层抽象可以接 Brave 或 OTel。选 OTel 的理由是跨语言:Java 服务追踪一个 Go 服务、一个 Python 服务,用的协议和上下文传播格式是统一的。

这是少数值得”提前站队”的选型 —— 一旦链路数据格式统一,后面用什么后端(Jaeger / Tempo / 商业 APM)都可以换。

一个实际建议

先别急着上全量采样。 高 QPS 服务做 100% 采样,trace 存储成本会先于收益爆炸。常见策略是:错误请求 + 慢请求全采,正常请求低比例采样。


4.11 服务网格、Kubernetes 与 AI 时代的治理(新增)

什么时候会从 SDK 治理转向服务网格

阶段治理方式痛点
服务少(< 20)每个服务引 SDK(Sentinel、Resilience4j)无
服务多(几十上百)SDK 仍然是主力,但……多语言栈要各写一套;治理逻辑升级要所有服务跟着发版
服务很多 + 多语言治理下沉到 sidecar(Istio / Linkerd)多一层代理,延迟和运维复杂度上升

服务网格的核心思路:把超时、重试、熔断、mTLS、流量灰度这些能力从业务进程里搬到 sidecar 代理,业务代码零侵入。控制面统一下发规则,所有服务的治理行为一致。

但不要过早引入。 服务数量和语言栈都不复杂时,服务网格带来的复杂度(代理生命周期、资源开销、调试链路变长)远大于收益。它解决的是”治理逻辑和业务代码耦合导致升级困难”这个规模化问题。

Kubernetes 带来的变化

容器化之后,很多”服务发现”的活被 Kubernetes 的 Service / DNS 接走了一部分:

  • 同一集群内,调用方可以直接按 Service 名访问,不必再经过注册中心的客户端负载均衡
  • 但跨集群、跨云、混合部署(虚拟机 + 容器)的场景,仍然需要 Nacos 这类独立注册中心
  • 健康检查也从应用层心跳,部分转移到了 kubelet 的 liveness / readiness probe

所以国内主流形态通常是 Kubernetes + Nacos 并存:K8s 管调度和集群内发现,Nacos 管跨环境的服务注册与配置。这不是重复造轮子,是两种维度的发现。

MCP Registry:注册中心的下一类”实例”

Nacos 3.0 新增的 MCP Registry 值得单独提一句,因为它说明注册中心这个抽象正在扩张。

传统注册中心注册的是”服务实例”(某个 IP:Port 上跑着一个能响应 HTTP 的进程)。当 Agent 要调用多个 MCP Server 时,需要回答的问题结构上完全一样:有哪些工具可用?它们在哪儿?哪个还活着?

graph LR
    A["传统服务发现"] --> B["服务名 → 实例列表 → 选一个调用"]
    C["MCP Registry"] --> D["工具名 → MCP Server 列表 → 选一个调用"]
  • 服务端注册(Server 启动时上报自己提供哪些 tools / resources)
  • 客户端发现(Agent 运行时按需拉取工具清单)
  • 健康与权限(沿用 Nacos 已有的鉴权体系)

这个变化对面试的意义:如果你在做 AI 应用方向,被问到”服务发现”时能顺着说到 “Agent 时代工具发现是同构问题”,会比只背 Nacos vs Eureka 有说服力得多。本博客前面几篇 MCP 文章讲的是协议层,这里补的是基础设施层。


4.12 面试速查:高频问题的现代答案

问题一句话答案展开见
Spring Cloud 五大组件?按能力答,不按 Netflix 组件名答开篇
注册中心选型?国内 Nacos,国外 Consul;Eureka 已停止演进4.2
服务间怎么调用?新代码用 RestClient / WebClient / @HttpExchange,OpenFeign 已非唯一选择4.3
负载均衡有哪些策略?LoadBalancer 开箱只有轮询和随机,加权/灰度要自定义4.3
熔断和降级区别?熔断是切断机制,降级是兜底策略4.4
熔断器状态机?CLOSED → OPEN → HALF_OPEN,三种实现通用4.4
Sentinel 和 Resilience4j 怎么选?要控制台和动态规则选 Sentinel,要原生集成选 Resilience4j4.4
漏桶和令牌桶区别?漏桶匀速,令牌桶容忍突发4.5
分布式限流怎么做?Redis + Lua 原子计数,网关兜底4.5
分布式事务有哪些方案?XA / AT / TCC / Saga / Outbox,能不做就不做4.7
为什么要有 Outbox?双写问题:写库和发消息不在一个事务里4.7
幂等怎么做?消息消费用去重表(唯一索引),前端提交用 token4.8
为什么做不到 exactly-once?超时无法区分”没收到”和”响应丢了”,只能 at-least-once4.8
链路追踪用什么?Micrometer Tracing + OpenTelemetry,Sleuth 已淘汰4.10
什么时候用服务网格?服务多且多语言,SDK 治理成了负担时4.11
MCP 和注册中心什么关系?MCP Registry 把”工具发现”做成了同构的服务发现4.11