Spring 全家桶 列表全部文章
Spring 全家桶

Spring Cloud 微服务笔记:注册发现、配置、网关、熔断限流与调用链

微服务治理的一份实操笔记:Nacos 注册与配置中心、Gateway 网关路由与限流、OpenFeign 调用与超时重试、Sentinel 熔断降级、分布式链路追踪,以及微服务拆分粒度和线上雪崩的复盘。

更多

微服务不是「把单体拆开部署」就完事了,真正的成本在治理:服务怎么找到彼此、配置怎么下发、一个服务挂了怎么不拖垮一片。这篇按这个顺序整理。

一、整体结构

客户端 Gateway 网关 路由 · 鉴权 · 限流 业务服务 A 业务服务 B OpenFeign 调用 + Sentinel 熔断降级 Nacos 注册中心 服务注册 / 健康检查 / 上下线 Nacos 配置中心 动态配置 / 灰度 / 命名空间 链路追踪 + 日志 TraceId 贯穿全链路,ELK 聚合

二、注册中心:Nacos

spring:
  application:
    name: order-service
  cloud:
    nacos:
      discovery:
        server-addr: 10.0.0.10:8848
        namespace: prod
        group: DEFAULT_GROUP
        heart-beat-interval: 5000      # 心跳间隔
      config:
        server-addr: 10.0.0.10:8848
        file-extension: yml
        namespace: prod
        refresh-enabled: true

配置动态刷新:类上加 @RefreshScope,或者用 @ConfigurationProperties + @EnableConfigurationProperties(推荐后者,不需要 @RefreshScope)。

踩过的坑:

  • namespace 和 group 不一致是最常见的「服务找不到」原因 —— 两个服务在不同 namespace 下互不可见,控制台能看到实例但调不通。
  • 临时实例 vs 持久实例:默认临时实例靠心跳,服务正常关闭会自动下线;持久实例需要手动下线,灰度发布时容易残留。
  • Nacos 2.x 多了 gRPC 端口(9848),除了 8848 还要放行 9848/9849,否则客户端能连上但注册失败。这是升级 2.x 最常见的网络问题。

三、网关:Gateway

spring:
  cloud:
    gateway:
      routes:
        - id: order
          uri: lb://order-service
          predicates:
            - Path=/api/order/**
          filters:
            - StripPrefix=1
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 100    # 每秒令牌数
                redis-rate-limiter.burstCapacity: 200
      default-filters:
        - DedupeResponseHeader=Access-Control-Allow-Origin

要点:

  • lb:// 走注册中心负载均衡;StripPrefix=1 剥掉前缀。
  • 限流用 Redis 令牌桶(RequestRateLimiter + spring-boot-starter-data-redis-reactive)。
  • 跨域配置不要重复:网关配了 CORS,下游服务就别再配一次,否则响应头重复浏览器直接报错。
  • 网关不要写业务逻辑,只做路由、鉴权、限流、日志。业务逻辑下沉到服务。

四、服务调用:OpenFeign

@FeignClient(name = "user-service", path = "/user",
             fallbackFactory = UserClientFallbackFactory.class)
public interface UserClient {
    @GetMapping("/{id}")
    Result<User> get(@PathVariable Long id);
}
spring:
  cloud:
    openfeign:
      client:
        config:
          default:
            connectTimeout: 2000      # ★ 连接超时一定要设,默认很长
            readTimeout: 5000
            loggerLevel: basic

Feign 的必配置项:

  1. 超时必须显式设置。默认连接超时 10s、读超时 60s,下游一慢就把上游线程全占住。
  2. 重试要谨慎:默认不重试,如果开了,务必确认下游接口幂等,否则重复下单。
  3. 日志级别按需开:full 会打印请求体和响应体,生产上别开(性能和隐私)。
  4. 熔断降级用 fallbackFactory:比 fallback 多拿到异常对象,能区分是超时还是业务异常。

五、熔断限流:Sentinel

能力 配置 说明
限流(QPS/线程数) 控制台规则 超了直接失败(快速失败)或排队(匀速排队)
熔断(慢调用比例) RT + 比例阈值 下游变慢时自动断开,避免拖垮上游
熔断(异常比例/数) 阈值 + 时间窗 连续报错就熔断
系统自适应 CPU / Load / RT 整机保护,最后一道防线

熔断的核心价值是快速失败 + 自动恢复:下游不可用时,与其让请求全部卡在超时上,不如立刻返回,留出资源给能成功的请求。

六、链路追踪

没有 TraceId 的微服务,排查问题就是灾难。做法:

  1. 网关生成 TraceId(或用 Micrometer Tracing / SkyWalking 自动埋点),放进 MDC。
  2. Feign 调用时用拦截器把 TraceId 写到请求头传递。
  3. 异步/线程池场景用 TaskDecorator 传递 MDC(前面 Spring Boot 那篇提过)。
  4. MQ 场景把 TraceId 放进消息头,消费端取出写回 MDC。
  5. 日志格式里带 %X{traceId},ELK 里就能一次搜出整条链路。
// Feign 传递
@Bean
public RequestInterceptor traceInterceptor() {
    return template -> template.header("X-Trace-Id", MDC.get("traceId"));
}

七、生产复盘:一次由慢 SQL 引发的雪崩

脱敏自某营销 SCRM 中台的一次事故。

现象:晚上 8 点营销活动开始,几分钟内多个服务接连告警,最后整个后台不可用。

链路还原(靠链路追踪看出来的):

  1. 活动触发人群筛选,一个慢 SQL(没走索引)让「人群服务」RT 从 50ms 涨到 5s。
  2. 上游「营销引擎」用 Feign 调用人群服务,超时 5s,线程池迅速被占满。
  3. 营销引擎不可用 → 网关大量请求堆积 → 网关线程池也满。
  4. 最后整个后台打不开 —— 一个 SQL 拖垮全站。

根因:没有任何一层做了资源隔离和快速失败。Feign 超时没设、线程池共用、没有熔断。

改造清单:

  • 所有 Feign 调用统一设置超时(连接 2s / 读 5s),并按下游服务隔离线程池。
  • 接入 Sentinel 熔断,慢调用比例超过 50% 自动断开。
  • 慢 SQL 加索引 + 大人群走离线任务,不再实时查。
  • 网关层加 QPS 限流,保护后端。
  • 核心链路做降级预案:人群服务不可用时,退化成「全量人群 + 异步补偿」,而不是直接失败。

结论:微服务的稳定性 = 超时 + 隔离 + 熔断 + 降级 四件事。缺一件,故障就会沿着调用链一路放大。

八、拆分粒度的建议

  • 按业务能力拆,不按技术分层拆。拆成「订单服务/用户服务」合理,拆成「controller 服务/dao 服务」是灾难。
  • 先粗后细:拿不准就先放一起,等业务边界清楚了再拆。拆错的代价远大于晚拆。
  • 共享库是隐形耦合:两个服务共用一个数据库,等于没拆。数据归属要清晰,跨服务只能通过接口。
  • 分布式事务能避免就避免:优先用「本地事务 + MQ 最终一致」,实在不行再上 Seata。
— 感谢读到这里 —发现错误,或想补充?告诉我 →
TURN KNOWLEDGE INTO SOMETHING REAL

有想法,也想把它做出来?

从技术方案到上线交付,聊聊你的下一个项目。

聊聊需求

一起聊聊

搜索文章

输入关键词,按标题和摘要即时筛选。

浏览分类文章归档

阅读设置

字号
行宽
专注模式

专注模式会隐藏页头与侧栏,只留正文。设置保存在当前浏览器。

稍后阅读

保存在当前浏览器,留给下一次安静阅读。

总浏览 -- 次 · 总访客 -- 人 · 今日浏览 -- 次 · 今日访客 -- 人