欢迎光临
我们一直在努力

10w字详解springcloud全家桶(详细流程+配置+代码)

目录

Spring Cloud Gateway 完整入门示例

环境版本

项目准备

第一步:业务服务 demo‑service(被网关代理)

pom.xml

application.yml

Controller

启动类 DemoServiceApplication

第二步:Gateway 网关服务 gateway‑server

方式一:yml 配置路由(最常用)application.yml

配置解释

测试访问

方式二:Java 代码编写路由(硬编码配置 RouteLocator)

常用 Predicate(断言)示例

常用内置 Filter

自定义全局过滤器 GlobalFilter(重要!鉴权、日志)

✅【正确示例 GlobalFilter】

❌【错误示例 1:使用 HttpServletRequest Servlet API】

❌【错误示例 2:同步阻塞代码 Thread.sleep/ RestTemplate】

对接注册中心 Nacos(lb 负载均衡)

前置

 gateway 接入 nacos 需要新增依赖

完整请求流程

测试

请求完整流程梳理

Spring Cloud Gateway 请求限流

依赖

核心配置:内置 RedisRateLimiter

示例 1:路由级别限流

示例 2:全局限流

示例 3:自定义返回 JSON(默认限流返回空 429,改成返回业务 JSON)

常见坑点

请求流程(带限流完整链路,Nacos+Gateway+Redis 限流)

Spring Cloud Gateway 主要核心功能概括

1. 请求路由转发(最基础)

2. 断言 Predicate(匹配请求条件)

3. 过滤器(内置路由过滤器 + 自定义过滤器)

内置 Filter

GlobalFilter 全局过滤器

4. 统一跨域处理

5. 请求限流

6. 熔断降级(Resilience4j)

7. 请求超时控制

8. 网关统一异常处理

9. 链路追踪透传 TraceId

10. 动态路由

11. IP 黑白名单拦截

12. 网关健康检查

❌ Gateway 不做什么(边界,面试高频)

Gateway 完整的整体工作流程汇总

Nacos 完整讲解

一、Nacos 下载启动

1. 下载

2. 启动模式

第一部分:Nacos 服务注册发现(Naming)

1. 依赖

2. application.yml 配置

Gateway 网关使用 Nacos(前面讲过)

3. 其他服务如何调用注册到 Nacos 的服务

方式 1:OpenFeign(推荐,微服务内部调用)

方式 2:LoadBalancerClient + RestTemplate

Nacos 服务发现核心概念

服务发现完整流程

第二部分:Nacos 配置中心 Config

1. 依赖

2. bootstrap.yml(优先级高于 application.yml)

DataId 拼接规则(重点)

3. 代码读取配置,实现自动刷新

Nacos‑Config 核心概念

Nacos 配置中心完整流程

第三部分:Gateway 网关读取 Nacos 配置中心

Nacos 常见踩坑

Nacos 在微服务整体位置(结合 Gateway)

SpringCloud Gateway + Nacos‑Config 实现动态路由

1.Gateway 网关完整 pom 新增依赖

2.bootstrap.yml(网关模块,重点!)

3.Nacos 控制台创建配置

4. 动态路由核心代码

5. 测试动态路由功能

6. 开启 actuator 查看当前生效路由

完整流程(Nacos 动态路由)

Seata 完整详解

Seata 核心角色

一、Seata‑Server 服务端安装启动(TC)

重要:Seata Server 有两种存储模式

1)修改 conf/application.yml seata‑server 配置

二、AT 模式(自动事务,业务几乎无侵入,重点)

1. 每个业务数据库执行 undo_log 建表语句

2. 微服务客户端 pom 依赖(TM、RM)

3. 微服务 application.yml 配置

AT 模式业务代码示例

① 订单服务(TM,发起全局事务)

② Storage 库存服务 RM

③ Account 账户服务 RM

AT 模式完整执行流程(重点面试)

TCC 模式(手动补偿模式)

Saga 模式:长事务

XA 模式:数据库原生两阶段提交

微服务整体架构图汇总(Gateway + Nacos + Seata)

Sentinel 完整详解

一、Sentinel‑Dashboard 控制台安装

微服务客户端 pom 依赖

核心概念

二、流量控制(流控规则 FlowRule)

流控模式

流控阈值类型

流控效果

@SentinelResource 注解自定义资源

三、熔断降级规则 DegradeRule(重点)

OpenFeign 整合 Sentinel 熔断

四、热点参数限流

五、系统规则 SystemRule

六、授权规则 AuthorityRule

七、Sentinel 规则持久化到 Nacos(生产必做)

客户端 application.yml

八、Sentinel 全局异常处理器

九、Sentinel 与 Gateway 网关整合

十、Sentinel 核心原理(面试重点)

十一、Sentinel 常见坑

十二、整套微服务技术栈汇总回顾

Spring Cloud LoadBalancer 详解

一、依赖说明

二、基础使用方式

方式 1:RestTemplate + LoadBalancer

方式 2:OpenFeign(底层自动使用 LoadBalancer)

方式 3:手动使用 LoadBalancerClient API

三、LoadBalancer 内置负载均衡策略

修改默认负载均衡策略

四、Nacos 权重负载均衡(重点面试)

五、LoadBalancer 核心概念

六、自定义负载均衡策略

1)自定义普通阻塞策略(RestTemplate / Feign)

七、健康实例过滤

八、LoadBalancer 重试机制

九、LoadBalancer 和 Ribbon 对比

十、常见坑点

十一、完整调用链路回顾(OpenFeign + LoadBalancer + Nacos)

十二、放到整套微服务架构视角

面试高频问题

Micrometer + Prometheus + Grafana 微服务监控完整详解

组件角色分工

一、SpringBoot 微服务客户端改造(Micrometer + Actuator + Prometheus)

pom.xml

application.yml 配置

内置自动采集指标(开箱即用)

Sentinel 对接 Micrometer(可选)

Seata 对接 Micrometer(可选)

自定义业务指标(Counter、Timer)

二、安装 Prometheus

Docker 启动 Prometheus

简单 PromQL 示例

三、安装 Grafana

步骤 1:添加 Prometheus 数据源

步骤 2:导入 SpringBoot 通用监控大盘模板

步骤 3:自己新建自定义业务面板

步骤 4:配置告警规则

四、整套数据流完整流程

五、关键概念 Micrometer

Meter 几种类型

Tag 标签

常见 PromQL 函数

六、生产环境注意事项 & 坑点

常见问题

七、整合进整套微服务完整架构总图

面试高频题

PromQL 速查表 + Prometheus Nacos‑SD 完整配置

一、常用 PromQL 速查表

🔹 HTTP 请求指标(SpringMVC / Gateway)

🔹 JVM 指标

🔹 Tomcat 容器指标

🔹 Sentinel 指标(引入 spring‑cloud‑alibaba‑sentinel‑micrometer)

🔹 Seata 分布式事务指标(seata‑micrometer)

🔹 自定义业务指标示例

🔹 常用函数说明

二、Prometheus 对接 Nacos‑SD 自动发现(生产必备)

方案:prometheus‑nacos‑sd(sidecar 模式)

步骤 1:docker 启动 prometheus‑nacos‑sd

步骤 2:修改 prometheus.yml,使用 file_sd_configs 读取文件

三、Prometheus 告警规则示例(alert.rules.yml)

Alertmanager(接收告警,转发钉钉 / 企业微信 / 邮件)

四、Grafana 推荐 Dashboard 模板 ID

五、生产避坑清单

六、完整架构回顾

Micrometer Tracing 详解

组件分工

SpringBoot3 依赖

application.yml 配置

Zipkin 部署(docker)

日志打印 TraceId / SpanId

手动创建自定义 Span(业务埋点)

链路传播

Micrometer Tracing + Metrics 联动

完整可观测三大支柱对比

补全整套微服务完整架构图(最终完整版)

面试高频(Micrometer Tracing)

常见坑

SpringCloud 微服务全套面试背诵版

一、Nacos(注册中心 + 配置中心)

Q1 Nacos 作为注册中心原理

Q2 Nacos 配置中心原理

Q3 Nacos AP 还是 CP

Q4 Nacos 和 Eureka 对比

常见坑

二、Spring Cloud Gateway(网关)

Q1 Gateway 作用

Q2 核心组件

Q3 Gateway 和 Zuul 区别

Q4 Gateway 底层负载均衡

Q5 Gateway 整合 Sentinel

Q6 可观测

三、OpenFeign + LoadBalancer

Q1 OpenFeign 是什么

Q2 @FeignClient 工作流程

Q3 LoadBalancer(替代 Ribbon)

Q4 客户端负载均衡 vs 服务端负载均衡

Q5 Feign 超时配置

Q6 Feign 调用 traceId 传递

四、Sentinel(流量控制、熔断降级)

Q1 Sentinel 核心能力

Q2 Sentinel 工作机制

Q3 熔断和限流区别

Q4 Sentinel 和 Hystrix 对比

Q5 可观测

五、Seata‑AT 分布式事务

Q1 Seata AT 模式工作原理(重点)

Q2 AT 模式优缺点

Q3 Seata XID 怎么跨服务传递

Q4 Seata 可观测

Q5 Seata 常见问题

六、可观测三大支柱:Metrics + Tracing + Logging

6.1 Micrometer‑Metrics(Prometheus+Grafana)

Q1 Micrometer 是什么

Q2 四种 Meter 类型

Q3 Prometheus(时序数据库)

Q4 Grafana

6.2 Micrometer‑Tracing 分布式链路追踪

Q1 Sleuth 去哪了?

Q2 TraceId / SpanId 概念

Q3 采样率 sampling.probability

Q4 自动埋点

Q5 Zipkin

6.3 Logging 日志

Q6 三者如何联动

高频综合面试题

Q1 微服务完整请求链路(背熟)

Q2 微服务雪崩怎么防护

Q3 分布式事务方案选型

Q4 客户端负载均衡流程

Q5 Pull 模式 vs Push 模式 (Prometheus)

Q6 Counter 为什么必须 rate ()

Q7 什么是高基数 Tag,危害

Q8 为什么 Tracing 生产不能 100% 采样

Q9 你这套微服务监控告警怎么做

微服务线上踩坑清单 + 故障排查思路

通用故障排查总流程(标准思路,面试也可以直接讲)

一、JVM 相关故障

1. 频繁 FullGC / OOM 内存溢出

现象

排查步骤

规避

2. GC 停顿过长,请求超时

3. 线程数暴涨,Tomcat 线程耗尽

二、Nacos 相关踩坑

1. 服务实例上下线延迟

2. Nacos 配置不生效,@RefreshScope 失效

3. 权重灰度不生效

4. Nacos 长轮询超时警告

三、OpenFeign + LoadBalancer 故障

1. Feign 随机超时

2. LoadBalancer 重试造成重复数据(高频坑!)

3. @LoadBalanced 漏加,RestTemplate 报错 unknown‑host

4. Feign 丢失 Header(traceId / XID 丢失)

5. Feign 调用报错 No instance available

四、Sentinel 线上踩坑

1. 规则不生效

2. 熔断不触发

3. Sentinel 误限流

4. sentinel‑micrometer 指标没有数据

五、Seata‑AT 分布式事务高频坑(非常容易出线上问题)

1. XID 丢失,分布式事务失效

2. undo_log 没有生成,回滚失效

3. 大事务问题

4. 事务悬挂、空回滚

5. Seata‑Server 宕机

六、可观测体系(Micrometer / Prometheus / Zipkin)踩坑

1. Prometheus target 显示 DOWN

2. Grafana 图表无数据

3. 高基数 Tag,Prometheus CPU / 内存暴涨

4. Zipkin 看不到完整链路,traceId 断链

5. 日志看不到 traceId

6. Prometheus 存储爆满

7. actuator 端点对外暴露

七、Gateway 网关踩坑

八、典型线上故障场景模拟(面试经常问)

场景 1:接口大量 504 超时

场景 2:分布式事务部分回滚,数据不一致

场景 3:流量突增系统雪崩

场景 4:偶发随机报错,很难复现

九、线上故障排查工具清单(实际工作使用)

十、生产环境检查清单(上线前核对)

基础架构类

Q1:介绍下你项目用的微服务技术栈,整体架构是怎样的?

Q2:Spring‑Cloud‑LoadBalancer 和 Ribbon 区别,为什么不用 Ribbon?

Q3:OpenFeign 的工作流程,会遇到哪些坑?

Q4:Gateway 和 Zuul 对比,Gateway 核心组件?

容错、雪崩、限流熔断

Q5:什么是服务雪崩?怎么防护?

Q6:Sentinel 限流和熔断降级区别?Sentinel 对比 Hystrix?

Q7:Sentinel 滑动窗口原理简单说下

分布式事务 Seata‑AT

Q8:讲下 Seata AT 模式原理,三个角色,执行流程

Q9:Seata‑AT 线上常见问题?

Q10:分布式事务有哪些方案,怎么选型?

可观测体系 Metrics & Tracing

Q11:Micrometer 是什么?Metrics 四种 Meter 类型?

Q12:Prometheus Pull 模式和 Push 模式区别?

Q13:什么是高基数 Tag,有什么危害?

Q14:SpringBoot3 链路追踪,Sleuth 去哪里了?TraceId、SpanId 概念?

Q15:指标、链路、日志三者怎么联动排查问题?

Q16:Zipkin 看不到链路,可能的原因?

故障排查类(高频)

Q17:线上接口大量 504 超时,你的排查思路?

Q18:偶发数据不一致,Seata 分布式事务部分回滚,排查思路?

Q19:线上故障通用排查流程

Q20:线上生产上线前检查哪些关键点?

如何保证微服务架构高可用

1. 基础设施 & 部署层面

2. 网关层高可用(Spring Cloud Gateway)

3. 服务调用与容错治理(Sentinel + LoadBalancer)

4. 业务代码层面保障

5. 数据层高可用

6. 可观测:监控、告警、故障快速发现(Micrometer+Prometheus+Grafana+Zipkin)

7. 运维保障

面试精简背诵版(直接口述)

微服务降级与熔断设计(面试背诵版)

一、概念区分(面试必问)

熔断(Circuit‑Breaker)

降级(Fallback)

二、Sentinel 中熔断降级实现(实际项目怎么做)

降级策略分类

三、网关层面降级熔断(Spring Cloud Gateway + Sentinel)

四、业务落地注意点(面试高频坑)

五、面试标准简答(可以直接口述)

六、延伸面试题

Q:熔断和限流的区别?

微服务架构中的服务治理(面试背诵版)

1. 服务注册与发现治理(Nacos)

2. 配置治理(Nacos Config)

3. 流量治理(Gateway + Sentinel + LoadBalancer)

4. 容错治理

5. 分布式事务治理(Seata‑AT)

6. 可观测治理(三大支柱 Metrics + Tracing + Logging)

7. 发布、运维与安全治理

面试精简口述版(直接背)

延伸面试题

Q:服务治理和服务注册发现的区别?

Q:微服务治理会带来哪些开销?

微服务接口设计原则(API 设计、版本兼容、契约)面试背诵版

一、接口设计基本原则

二、入参 & 出参设计

入参

出参

分页

三、异常设计(非常容易踩坑)

四、接口版本管理(兼容升级高频考点)

兼容修改(安全,可以直接发布)

不兼容修改(禁止直接发布,会导致旧调用方报错)

版本号三种方案

五、服务契约(契约设计)

反模式:公共 DTO jar 包

六、接口安全设计

七、面试口述精简版

延伸面试题

Q:为什么不要直接返回数据库 DO 给外部调用?

Q:什么是向后兼容?

Q:如果必须做不兼容接口变更怎么办?

微服务常见设计陷阱与反模式(面试背诵版)

一、调用反模式

1. 循环远程调用(循环 Feign)

2. 同步链路过长

3. 滥用重试

4. 不设置超时

二、服务拆分反模式

1. 过度拆分(拆得过细)

2. 共享数据库反模式

3. 上帝服务(单体没拆干净)

三、分布式事务反模式(Seata‑AT 高频坑)

1. 大事务:@GlobalTransactional 包裹远程 Feign 调用

2. 异步线程丢失 XID 上下文

3. AT 模式使用 DDL 语句

四、接口设计反模式

1. 直接返回数据库 DO 实体对外

2. 公共 DTO Jar 包强耦合

3. 接口不做向后兼容,随意修改老接口字段

4. 超大入参、全量查询不分页

五、流量治理反模式(Sentinel)

1. 把 Sentinel 内存规则当生产配置

2. 混淆单机 QPS 与集群 QPS

3. fallback 里面写复杂业务逻辑

六、可观测反模式

1. 高基数 Tag

2. Tracing 采样率生产设置 1.0 全采样

3. actuator 端点暴露公网

七、注册中心 & 部署反模式

1. 不做优雅停机

2. Nacos 单机部署

八、面试口述版

延伸面试题

Q:微服务最大的痛点是什么?


Spring Cloud Gateway 完整入门示例

Spring Cloud Gateway 是 Spring Cloud 的网关组件,基于 Spring‑WebFlux(响应式,非阻塞),用来做请求路由、鉴权、限流、重写路径、负载均衡。

核心三大概念:

  • Route(路由):路由规则,id、目标 uri、断言、过滤器
  • Predicate(断言):条件,满足条件才走这条路由(路径、请求头、时间、method 等)
  • Filter(过滤器):请求 / 响应修改,前置过滤、后置过滤
  • ⚠️⚠️⚠️ Gateway 不能用 Spring‑MVC (tomcat),必须用 webflux;引入依赖不要引入 spring‑boot‑starter‑web!

    环境版本

    • Spring Boot:3.2.x
    • Spring Cloud:2023.0.x
    • JDK 17

    项目准备

    一共两个服务:

  • gateway‑server:网关服务,端口 8080
  • demo‑service:后端业务服务,端口 9001,提供简单接口
  • 第一步:业务服务 demo‑service(被网关代理)

    pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.5</version>
    <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>demo-service</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <properties>
    <java.version>17</java.version>
    </properties>

    <dependencies>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    </dependencies>
    </project>

    Q:为什么gateway pom 没有看到 spring‑webflux 依赖?

    A:

    因为spring‑cloud‑starter‑gateway 内部已经传递依赖引入spring‑boot‑starter‑webflux,不需要手动写。

    application.yml

    server:
    port: 9001

    Controller

    package com.example.demoservice;

    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;

    @RestController
    @RequestMapping("/api")
    public class DemoController {

    @GetMapping("/hello")
    public String hello() {
    // 直接获取网关透传过来的token header
    //String token = request.getHeader("token");
    return "【业务服务 hello】";
    }

    @GetMapping("/user/{id}")
    public String getUser(@PathVariable Long id) {
    // 直接获取网关透传过来的token header
    //String token = request.getHeader("token");
    return "【查询用户】id=" + id;
    }
    }

    启动类 DemoServiceApplication

    package com.example.demoservice;

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;

    @SpringBootApplication
    public class DemoServiceApplication {
    public static void main(String[] args) {
    SpringApplication.run(DemoServiceApplication.class, args);
    }
    }

    启动后访问:http://127.0.0.1:9001/api/hello 可以拿到返回。

    第二步:Gateway 网关服务 gateway‑server

    ❗重要:不要引入 spring‑boot‑starter‑web!gateway 使用 webflux。

    pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.5</version>
    <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>gateway-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <properties>
    <java.version>17</java.version>
    <spring-cloud.version>2023.0.1</spring-cloud.version>
    </properties>

    <dependencies>
    <!– spring cloud gateway核心依赖 –>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    </dependencies>

    <dependencyManagement>
    <dependencies>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-dependencies</artifactId>
    <version>${spring-cloud.version}</version>
    <type>pom</type>
    <scope>import</scope>
    </dependency>
    </dependencies>
    </dependencyManagement>
    </project>

    方式一:yml 配置路由(最常用)application.yml

    网关端口 8080

    server:
    port: 8080

    spring:
    cloud:
    gateway:
    routes:
    # 路由1:转发到demo‑service服务
    – id: demo-service-route # 路由唯一id,随便写
    uri: http://127.0.0.1:9001 # 目标服务地址
    predicates: # 断言,条件匹配
    – Path=/demo/** # 请求路径以 /demo/** 开头,匹配成功进入这条路由
    filters: # 过滤器
    – StripPrefix=1 # 去掉第一层路径:/demo/api/hello → /api/hello

    配置解释
    • id:路由标识,唯一即可
    • uri:转发目标地址;对接 nacos 时写 lb://demo-service 做负载均衡
    • predicates:断言。Path=/demo/**,只要 url 匹配 /demo/xxx 就走该路由
    • StripPrefix=1:去掉路径第一层。
      • 浏览器请求网关:http://127.0.0.1:8080/demo/api/hello
      • stripPrefix=1 把 /demo 截掉,转发后端真实地址:http://127.0.0.1:9001/api/hello
    测试访问
  • 先启动 demo‑service (9001)
  • 启动 gateway‑server (8080)
  • 访问网关地址:

    http://127.0.0.1:8080/demo/api/hello

    返回:【业务服务 hello】 ✔️

    访问:

    http://127.0.0.1:8080/demo/api/user/100

    返回:【查询用户】id=100 ✔️

    请求流程:

    浏览器 → Gateway (8080) → 断言匹配 Path → StripPrefix 过滤器修改路径 → 转发到 9001 业务服务 → 返回结果原路返回网关 → 返回浏览器。

    方式二:Java 代码编写路由(硬编码配置 RouteLocator)

    注释掉 yml 里面 routes,使用 java 配置类实现路由。

    package com.example.gatewayserver;

    import org.springframework.cloud.gateway.route.RouteLocator;
    import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;

    @Configuration
    public class GatewayRouteConfig {

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
    return builder.routes()
    .route("demo-service-route", r -> r
    .path("/demo/**") // 断言Path匹配
    .filters(f -> f.stripPrefix(1)) // 过滤器去掉第一层路径
    .uri("http://127.0.0.1:9001") // 转发目标地址
    )
    .build();
    }
    }

    效果和 yml 完全一样。项目一般优先 yml 配置,便于动态刷新;代码方式适合写复杂逻辑。


    常用 Predicate(断言)示例

    predicates:
    # 路径匹配
    – Path=/demo/**
    # 请求方法 GET才匹配
    – Method=GET
    # 请求头必须携带token
    – Header=token, .+
    # 查询参数携带name
    – Query=name

    常用内置 Filter

    filters:
    – StripPrefix=1 # 去掉N层路径
    – AddRequestHeader=X‑From‑Gateway, demo-gateway # 添加请求头传给下游
    – AddResponseHeader=X‑Resp‑Gateway, ok # 返回给前端增加响应头
    – RewritePath=/old/(?<path>.*), /$\\{path} # 路径重写

    示例 AddRequestHeader:

    spring:
    cloud:
    gateway:
    routes:
    – id: demo-service-route
    uri: http://127.0.0.1:9001
    predicates:
    – Path=/demo/**
    filters:
    – StripPrefix=1
    – AddRequestHeader=X-Gateway-Source, my-gateway

    下游 demo‑service Controller 就可以拿到请求头 X‑Gateway‑Source。


    自定义全局过滤器 GlobalFilter(重要!鉴权、日志)

    GlobalFilter 全局生效,所有进入网关请求都会执行。模拟简单 token 校验。

    package com.example.gatewayserver;

    import org.springframework.cloud.gateway.filter.GatewayFilterChain;
    import org.springframework.cloud.gateway.filter.GlobalFilter;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.http.server.reactive.ServerHttpResponse;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Mono;

    @Configuration
    public class AuthGlobalFilter {

    @Bean
    public GlobalFilter tokenCheckFilter() {
    return (ServerWebExchange exchange, GatewayFilterChain chain) -> {
    ServerHttpRequest request = exchange.getRequest();
    ServerHttpResponse response = exchange.getResponse();

    // 获取请求头token
    String token = request.getHeaders().getFirst("token");
    if (token == null || token.isBlank()) {
    // 没有token,401拒绝放行
    response.setStatusCode(HttpStatus.UNAUTHORIZED);
    return response.setComplete();
    }

    // 构建新request:新增/覆盖header,传给下游服务
    // ServerHttpRequest newRequest = request.mutate()
    // .header("token", token) // 原样携带token;也可以覆盖:.header("token","new‑xxx")
    //.header("X‑Gateway‑Pass", "yes") // 自定义新增header传给下游
    // .build();

    // 使用新的exchange继续链路
    ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
    return chain.filter(newExchange);
    // token存在,放行请求,继续走后面路由过滤器
    return chain.filter(exchange);
    };
    }
    }

    网关校验 token 后,把 token 往下游业务服务传递

    当前代码:网关拿到 header token:abc123,只是做校验,默认 header 会原样透传给下游,下游 DemoController 直接就能拿到 token。

    测试:

    # 不带token,返回401 Unauthorized
    curl -v http://127.0.0.1:8080/demo/api/hello

    # 带上token,正常转发业务服务
    curl -H "token: abc123" http://127.0.0.1:8080/demo/api/hello

    # 携带token header,网关放行,下游拿到token
    curl -v -H "token: abc123456" http://127.0.0.1:8080/demo/api/hello

    -H   设置请求头(发给服务端)

    -v   打印全部请求 + 响应报文

    ⚠️注意:

  • Gateway 是响应式,只能用 ServerHttpRequest / ServerHttpResponse / ServerWebExchange
  • 禁止使用 Servlet API:HttpServletRequest、HttpServletResponse(网关环境根本没有 servlet 容器)
  • 禁止写同步阻塞代码:Thread.sleep()、RestTemplate、JDBC 同步查询,会阻塞 Reactor‑Netty 事件线程,网关性能雪崩。
  • ✅【正确示例 GlobalFilter】

    @Bean
    public GlobalFilter correctFilter() {
    return (exchange, chain) -> {
    ServerHttpRequest request = exchange.getRequest();
    String token = request.getHeaders().getFirst("token");
    if(token == null || token.isBlank()){
    exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
    return exchange.getResponse().setComplete();
    }
    return chain.filter(exchange);
    };
    }

    ❌【错误示例 1:使用 HttpServletRequest Servlet API】

    网关没有 servlet 容器,直接报错,ClassCastException。

    @Bean
    public GlobalFilter wrongFilter1() {
    return (exchange, chain) -> {
    // ❌ 错误:Gateway不是Servlet环境,拿不到HttpServletRequest
    HttpServletRequest servletRequest = (HttpServletRequest) exchange.getRequest();
    String token = servletRequest.getHeader("token");
    return chain.filter(exchange);
    };
    }

    ❌【错误示例 2:同步阻塞代码 Thread.sleep/ RestTemplate】

    sleep 会把 Netty IO 线程卡住,高并发网关直接卡死。

    @Bean
    public GlobalFilter wrongFilter2() {
    return (exchange, chain) -> {
    ServerHttpRequest request = exchange.getRequest();
    String token = request.getHeaders().getFirst("token");

    // ❌ 阻塞!占用Netty事件循环线程
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    throw new RuntimeException(e);
    }

    // ❌ RestTemplate同步http调用也是阻塞,严禁在filter内使用;要用WebClient
    // restTemplate.getForObject("http://xxx",String.class);

    if(token == null){
    exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
    return exchange.getResponse().setComplete();
    }
    return chain.filter(exchange);
    };
    }

    如果网关 filter 内部需要调用外部接口做 token 校验,必须使用 WebClient(响应式非阻塞),返回 Mono。

    // 正确调用外部http伪代码
    WebClient webClient = WebClient.create();
    Mono<Boolean> checkMono = webClient.get()
    .uri("http://auth-server/check?token="+token)
    .retrieve()
    .bodyToMono(Boolean.class);

    return checkMono.flatMap(isValid -> {
    if(isValid){
    return chain.filter(exchange);
    }else{
    exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
    return exchange.getResponse().setComplete();
    }
    });

    对接注册中心 Nacos(lb 负载均衡)

    前置

  • Nacos 服务已启动 127.0.0.1:8848
  • demo‑service 业务服务接入 nacos,服务名:demo‑service
  • gateway 网关接入 nacos discovery
  • gateway 路由配置:
  • 上面例子 uri 写死 ip 端口;微服务环境配合 nacos,uri 写 lb://服务名

    spring:
    cloud:
    gateway:
    routes:
    – id: demo-service-route
    uri: lb://demo-service # lb代表loadbalance负载均衡,从nacos找demo‑service服务
    predicates:
    – Path=/demo/**
    filters:
    – StripPrefix=1

     gateway 接入 nacos 需要新增依赖

    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>

    application.yml

    spring:
    cloud:
    nacos:
    discovery:
    server-addr: 127.0.0.1:8848

    完整请求流程

    客户端 curl -H "token:abc123" http://127.0.0.1:8080/demo/api/hello
    ↓
    1. 请求到达Gateway(Reactor‑Netty)
    ↓
    2. 执行【GlobalFilter全局过滤器】:token校验
    – token为空直接返回401;token合法继续往下
    – 可以mutate修改request header,构造新exchange
    ↓
    3. Gateway遍历所有Route路由,执行Predicate断言匹配
    Path=/demo/** 匹配成功,选中这条 nacos路由 demo‑nacos‑route
    ↓
    4. uri=lb://demo‑service 触发Spring‑Cloud LoadBalancer负载均衡
    a. Gateway请求Nacos Server,拉取 `demo‑service` 的所有可用实例列表(ip:port)
    b. 负载均衡算法(轮询/随机)选出其中一个实例:例如 127.0.0.1:9001
    ↓
    5. 执行该路由下局部Filter:StripPrefix=1
    请求路径:`/demo/api/hello` → 修改为 `/api/hello`
    ↓
    6. Gateway把修改完成的请求转发给选中的业务实例:http://127.0.0.1:9001/api/hello
    请求头token原样透传到下游DemoController
    ↓
    7. demo‑service(Spring‑MVC/Tomcat)执行业务接口返回字符串
    ↓
    8. 响应原路返回网关,执行后置Filter逻辑
    ↓
    9. Gateway把响应body+responseHeader返回给客户端

    测试

  • 启动 Nacos
  • 启动 demo‑service(注册 nacos)
  • 启动 gateway(注册 nacos)
  • 启动调用方服务 A 访问服务 A 接口:http://服务AIP:端口/callGateway,拿到下游返回。
  • 请求完整流程梳理

  • 客户端发起请求 GET http://127.0.0.1:8080/demo/api/hello
  • 请求到达 Gateway
  • GlobalFilter 全局过滤器执行(token 校验等)
  • 遍历 routes,执行predicates 断言,匹配到 demo‑service‑route
  • 执行该 route 下的 filters:StripPrefix、AddRequestHeader 等
  • 将修改后的请求转发给 uri 指定下游服务
  • 下游业务服务处理完成返回响应
  • 执行后置 filter,响应返回客户端。
  • 访问 http://127.0.0.1:xxxx/callGateway

    流程:

    调用方服务 → http 请求 → Gateway 网关(校验 token 全局过滤器)→ 路由转发 → demo‑service 业务服务 → 返回结果给调用方。

    常见踩坑:

  • ❗网关项目引入 spring‑boot‑starter‑web,启动报错:Gateway 依赖 webflux,不能共存 web。
  • StripPrefix 参数写错,下游接口 404,路径没有裁剪正确。
  • GlobalFilter 里面写阻塞代码(如 RestTemplate),会性能问题,必须用 webflux/reactor。
  • yml 缩进错误,路由规则不生效。
  • Spring Cloud 版本和 Boot 版本不匹配,网关启动异常。
  • ❗️❗️❗️

    微服务最佳实践:业务服务之间尽量不要跨网关调用。

    业务服务之间直接 OpenFeign 调用;

    网关只负责接收外部客户端请求(浏览器、APP)。

    业务服务内部互相调用不走网关,可以避免额外 token 校验、多一层网络开销。


    Spring Cloud Gateway 请求限流

    Gateway 官方内置 Redis‑RateLimiter 限流过滤器,基于 Redis + Lua 脚本实现令牌桶限流,是生产最常用方案。

    底层原理:令牌桶算法,Lua 脚本保证 Redis 操作原子性;需要 Redis 环境。

    两种粒度:

  • 路由维度限流:写在 yml 路由 filters,针对某一条路由限流
  • 全局限流:自定义 GlobalFilter,全部接口统一限流
  • 限流维度:

        可以按 IP、用户 token、自定义 key 做限流。

    依赖

    gateway 本身,额外引入 redis 依赖

    <!– gateway –>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    <!– redis 限流必须 –>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
    </dependency>

    ⚠️ 必须用 reactive redis(响应式 redis),不能用普通 spring‑boot‑starter‑data‑redis,会阻塞!

    application.yml redis 配置

    spring:
    data:
    redis:
    host: 127.0.0.1
    port: 6379
    password:

    核心配置:内置 RedisRateLimiter

    RedisRateLimiter 参数说明

    • replenishRate:令牌桶每秒填充令牌数(允许每秒多少请求)
    • burstCapacity:令牌桶最大容量(允许瞬间突发请求峰值)
    • key‑resolver:限流 key 解析器,决定按什么维度限流(IP /token/ 用户 ID)

    举例:

    replenishRate=2,burstCapacity=5:

    每秒放 2 个令牌,桶最多存 5 个。

    正常每秒 2 请求;瞬间最多扛 5 并发。


    示例 1:路由级别限流

    (yml 配置 + 自定义 KeyResolver)

    场景:

    /demo/** 这条路由做限流,按客户端 IP 限流。

    每个 IP 每秒 2 请求,最大突发 5。

    第一步写自定义 KeyResolver Bean

    package com.example.gatewayserver;

    import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Mono;

    @Configuration
    public class RateLimitConfig {

    /**
    * 根据客户端IP作为限流key
    */
    @Bean
    public KeyResolver ipKeyResolver() {
    return exchange -> {
    // 获取真实客户端IP
    String ip = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
    return Mono.just(ip);
    };
    }

    /**
    * 根据token做限流key:按用户粒度限流(可选)
    */
    @Bean
    public KeyResolver tokenKeyResolver() {
    return exchange -> {
    String token = exchange.getRequest().getHeaders().getFirst("token");
    if(token != null){
    return Mono.just(token);
    }
    return Mono.just("anonymous");
    };
    }
    }

    yml 路由配置,启用限流过滤器

    spring:
    cloud:
    gateway:
    routes:
    – id: demo-service-route
    uri: lb://demo-service
    predicates:
    – Path=/demo/**
    filters:
    – StripPrefix=1
    # Redis限流过滤器
    – name: RequestRateLimiter
    args:
    # 每秒生成令牌数
    replenishRate: 2
    # 令牌桶最大容量
    burstCapacity: 5
    # 指定使用哪个KeyResolver Bean名字,对应上面 @Bean名称 ipKeyResolver
    key-resolver: "#{@ipKeyResolver}"

    #{@ipKeyResolver} 是 SpEL 表达式,从 Spring 容器拿到名字叫ipKeyResolver的 Bean。

    限流被触发效果

    请求超过阈值,网关直接返回:

    • HTTP 状态码:429 Too Many Requests
    • 不会转发下游服务。

    测试:

    疯狂并发请求 curl -H "token:abc123" http://127.0.0.1:8080/demo/api/hello,

    部分请求返回 429。


    示例 2:全局限流

    (所有路由全部生效,自定义 GlobalFilter 包装 RedisRateLimiter)

    上面是单条路由配置;如果希望全部接口统一限流,不需要每个路由写 filter,自己写 GlobalFilter。

    @Bean
    public GlobalFilter globalRateFilter(RedisRateLimiter redisRateLimiter){
    return (exchange, chain) -> {
    Mono<String> keyMono = Mono.just(
    exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()
    );
    return keyMono.flatMap(key -> {
    return redisRateLimiter.isAllowed("global-limit", key)
    .flatMap(response -> {
    if(response.isAllowed()){
    // 放行
    return chain.filter(exchange);
    }else{
    // 限流,返回429
    exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
    return exchange.getResponse().setComplete();
    }
    });
    });
    };
    }

    同时配置 RedisRateLimiter 参数 Bean:

    @Bean
    public RedisRateLimiter redisRateLimiter(){
    // replenishRate, burstCapacity
    return new RedisRateLimiter(2,5);
    }


    示例 3:自定义返回 JSON(默认限流返回空 429,改成返回业务 JSON)

    默认限流只有 429 状态码,没有 body,前端不好解析。

    方案:全局异常处理器 GatewayErrorWebExceptionHandler,捕获限流异常,返回自定义 JSON。

    package com.example.gatewayserver;

    import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.annotation.Order;
    import org.springframework.core.io.buffer.DataBuffer;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.MediaType;
    import org.springframework.http.server.reactive.ServerHttpResponse;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Mono;

    @Configuration
    @Order(-1) // 优先级必须高于默认异常处理器
    public class GatewayCustomExceptionHandler implements ErrorWebExceptionHandler {

    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
    ServerHttpResponse response = exchange.getResponse();
    response.getHeaders().setContentType(MediaType.APPLICATION_JSON);

    // 限流异常:io.github.resilience4j… 或者网关限流会设置status=429
    if(response.getStatusCode() == HttpStatus.TOO_MANY_REQUESTS){
    String json = "{\\"code\\":429,\\"msg\\":\\"请求过于频繁,请稍后重试\\",\\"data\\":null}";
    DataBuffer buffer = response.bufferFactory().wrap(json.getBytes());
    return response.writeWith(Mono.just(buffer));
    }

    return Mono.error(ex);
    }
    }


    Redis 里面存储什么?

    RedisRateLimiter 使用 2 个 key(Lua 脚本)

  • request_rate_limiter.{key}.tokens:当前剩余令牌数量
  • request_rate_limiter.{key}.timestamp:上一次更新时间戳
  • key 就是 KeyResolver 返回值,例如客户端 IP。

    常见坑点

  • ❗错误依赖 spring‑boot‑starter‑data‑redis(非响应式),网关高并发直接阻塞,必须 spring‑boot‑starter‑data‑redis‑reactive。
  • SpEL 写错:#{@ipKeyResolver},Bean 名字必须和 @Bean 方法名一致。
  • 拿到的 IP 是 127.0.0.1:网关部署在 Nginx 之后,没有配置转发真实 IP 头,需要 Nginx 配置 X‑Forwarded‑For,代码解析 header 拿真实 IP。
  • replenishRate=0:直接全部拒绝;burstCapacity 不能小于 replenishRate。
  • 网关多实例:Redis 共享,多网关实例限流是全局生效;不用每个实例单独计数
  • 请求流程(带限流完整链路,Nacos+Gateway+Redis 限流)

    客户端 curl -H "token:abc123" http://127.0.0.1:8080/demo/api/hello
    ↓
    1.Netty接收请求
    ↓
    2.GlobalFilter:token校验过滤器,token非法返回401;合法继续
    ↓
    3.路由Predicate断言匹配路由 demo‑service‑route
    ↓
    4.执行路由过滤器 RequestRateLimiter
    4‑1.执行ipKeyResolver获取限流key=客户端IP
    4‑2.执行Redis Lua脚本:查询、增减令牌
    4‑3.令牌不足:返回429(自定义JSON响应),请求终止,不转发下游
    4‑4.令牌充足:放行
    ↓
    5.StripPrefix=1 修改路径
    ↓
    6.uri=lb://demo‑service,负载均衡拿到业务服务实例地址
    ↓
    7.转发请求到demo‑service业务服务
    ↓
    8.响应原路返回客户端

    Spring Cloud Gateway 主要核心功能概括

    Gateway 作为微服务 API 网关,

    定位:流量入口,所有外网请求统一进来,做前置处理,再转发给后端微服务。

    底层:Spring‑WebFlux Reactor‑Netty,响应式非阻塞。

    前面讲过:路由 Route、断言 Predicate、过滤器 Filter、全局过滤器 GlobalFilter、Redis 令牌桶限流。

    下面梳理全部常用核心能力 + 简单示例、使用场景。

    1. 请求路由转发(最基础)

    • 根据路径、请求头、请求方法、参数等条件,把请求转发到不同微服务。
    • 支持写死 http://ip:port;支持注册中心负载均衡 lb://服务名(Nacos/Eureka)。
    • 支持路径重写、路径裁剪 StripPrefix。

    filters:
    – StripPrefix=2
    – RewritePath=/api/(?<p>.*), /$\\{p}

    2. 断言 Predicate(匹配请求条件)

    内置多种断言,可以组合条件,满足条件才命中路由。

    • Path:路径匹配
    • Method:请求方法 GET/POST
    • Header:请求头匹配
    • Query:请求参数
    • Cookie:cookie
    • RemoteAddr:客户端 IP
    • After / Before:时间范围(例如只允许某个时间段访问接口)

    predicates:
    – Path=/user/**
    – Method=GET,POST
    – Header=X‑Token, \\d+
    – Query=name

    3. 过滤器(内置路由过滤器 + 自定义过滤器)

    内置 Filter
    • 修改请求头:AddRequestHeader、RemoveRequestHeader
    • 修改响应头:AddResponseHeader
    • 重定向、修改响应状态码
    • 请求限流 RequestRateLimiter
    • 重试 Retry:下游服务调用失败自动重试
    • 请求超时配置
    • 熔断(配合 Resilience4j)

    示例:重试过滤器,下游 5xx 错误最多重试 2 次

    filters:
    – name: Retry
    args:
    retries: 2
    statuses: SERVER_ERROR

    GlobalFilter 全局过滤器

    所有路由全部生效,用来实现:

    • Token 鉴权、登录校验
    • 请求日志打印(打印请求 url、ip、耗时)
    • 请求 ID 透传(traceId 链路追踪)
    • 统一请求加密解密
    • 黑白 IP 拦截

    示例:打印请求耗时全局过滤器

    @Bean
    public GlobalFilter logTimeFilter(){
    return (exchange, chain)->{
    long start = System.currentTimeMillis();
    return chain.filter(exchange)
    .then(Mono.fromRunnable(() -> {
    long cost = System.currentTimeMillis() – start;
    System.out.println("请求路径:"+exchange.getRequest().getPath()+",耗时:"+cost);
    }));
    };
    }

    4. 统一跨域处理

    CORS 网关层统一处理跨域,下游微服务不需要再写跨域配置。

    application.yml

    spring:
    cloud:
    gateway:
    globalcors:
    cors-configurations:
    '[/**]':
    allowedOrigins: "*"
    allowedMethods:
    – GET
    – POST
    – PUT
    – DELETE
    allowedHeaders: "*"
    maxAge: 3600

    注意:如果网关开启鉴权 filter,跨域 OPTIONS 请求要直接放行,不要校验 token。

    5. 请求限流

    • RedisRateLimiter:令牌桶,Redis 共享,集群网关有效,按 IP/token/ 用户 id 限流。
    • Resilience4j:单机限流,信号量、时间窗口,不需要 Redis,多实例计数隔离。

    6. 熔断降级(Resilience4j)

    下游服务故障、超时,网关直接熔断,返回降级响应,防止雪崩。 当后端服务大量超时 / 报错,网关不再转发请求,直接返回预设降级 JSON。

    依赖:

    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
    </dependency>

    yaml 简单示例:

    filters:
    – name: CircuitBreaker
    args:
    name: demoBreaker
    fallbackUri: forward:/fallback

    /fallback 是网关内部降级接口,服务不可用时就转发到此返回降级提示。

    7. 请求超时控制

    可以设置路由级别的连接超时、响应超时,下游接口卡住不会无限等待。

    spring:
    cloud:
    gateway:
    httpclient:
    connect-timeout: 2000
    response-timeout: 5s

    8. 网关统一异常处理

    自定义 ErrorWebExceptionHandler:

    • 401 无 token、429 限流、500 熔断,统一返回标准 JSON 格式,而不是默认 html / 空响应。

    前面写过示例。

    9. 链路追踪透传 TraceId

    网关接收请求生成 traceId,放入 header 往下游所有微服务传递,整个链路日志使用同一个 traceId,方便排查问题。

    在 GlobalFilter 中 mutate 新增 header:X‑B3‑TraceId。配合 Sleuth / SkyWalking。

    ServerHttpRequest newReq = exchange.getRequest().mutate()
    .header("X‑Trace‑Id", UUID.randomUUID().toString())
    .build();
    return chain.filter(exchange.mutate().request(newReq).build());

    10. 动态路由

    (非常重要生产功能) 默认 yml 配置路由,修改配置要重启网关。

    动态路由:运行时不用重启网关,新增 / 修改 / 删除路由规则。

    实现方案:

  • 配置中心(Nacos‑Config / Apollo)存放路由 JSON 配置,网关监听配置变更自动刷新路由。
  • 数据库存储路由表,程序读取 DB,调用 RouteDefinitionWriter 增删改路由。
  • 核心 API:RouteDefinitionWriter

    // 新增、删除路由,运行时动态生效
    routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
    routeDefinitionWriter.delete(Mono.just("routeId")).subscribe();

    11. IP 黑白名单拦截

    GlobalFilter 获取客户端 IP,维护黑名单集合,黑名单 IP 直接拒绝访问。

    // GlobalFilter伪代码
    String ip = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
    Set<String> blackList = Set.of("127.0.0.2","192.168.1.100");
    if(blackList.contains(ip)){
    response.setStatusCode(HttpStatus.FORBIDDEN);
    return response.setComplete();
    }

    12. 网关健康检查

    Actuator 端点 开启 actuator,可以查看网关路由信息,管理网关。

    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

    management:
    endpoints:
    web:
    exposure:
    include: gateway,health

    访问:http://网关地址/actuator/gateway/routes,查看所有路由列表。

    ❌ Gateway 不做什么(边界,面试高频)

  • 不做业务逻辑,只做流量转发前置处理,业务逻辑交给下游微服务。
  • 不能用 Spring‑MVC (Tomcat),底层 WebFlux Netty 响应式。
  • 不做会话 session 共享;无状态,靠 token/jwt 鉴权。
  • 不做微服务之间内部调用中转:微服务之间 Feign 直连,网关只对外网流量。
  • Gateway 完整的整体工作流程汇总

  • 客户端请求到达网关 Netty
  • 全局过滤器 GlobalFilter:鉴权、ip 黑名单、生成 traceId、打印日志
  • 遍历路由,执行 Predicate 断言,匹配命中某一条 Route
  • 执行这条路由的局部 Filter 链:限流、重试、熔断、修改请求头、StripPrefix 路径修改
  • uri=lb://xxx:调用注册中心负载均衡,拿到下游服务实例地址
  • HttpClient 转发请求到下游微服务
  • 获取下游响应,执行后置 Filter(修改响应头)
  • 发生异常:进入自定义全局异常处理器,返回统一 JSON
  • 响应返回客户端
  • 面试高频提问清单

  • GlobalFilter 和 GatewayFilter(路由 Filter)区别? GlobalFilter:全部路由生效;GatewayFilter:只对当前这条 route 生效。执行顺序:GlobalFilter 先执行,再执行路由 Filter。
  • lb:// 是什么? lb 代表 loadbalance 负载均衡,从注册中心获取服务实例。
  • Gateway 过滤器链执行顺序? 数字 order 越小优先级越高,越先执行。
  • Nacos 完整讲解

    Nacos = Dynamic Naming and Configuration Service 两大核心能力:

  • 服务发现(Naming):微服务注册、发现、负载均衡(替代 Eureka)
  • 配置中心(Config):统一管理配置,动态刷新配置,不需要重启服务(替代 Spring‑Cloud‑Config)
  • 版本对齐:Spring‑Boot3.2.x + Spring‑Cloud 2023.0.x + Alibaba Cloud 2023.0.1.0 JDK17

    一、Nacos 下载启动

    1. 下载

    github:https://github.com/alibaba/nacos/releases 下载 nacos‑server‑2.3.2.zip

    2. 启动模式

    • 单机模式(开发用)

    # windows cmd
    startup.cmd -m standalone

    # linux/mac
    sh startup.sh -m standalone

    ⚠️不加 -m standalone 默认是集群模式,启动报错。

    访问控制台: http://127.0.0.1:8848/nacos 账号密码默认:nacos / nacos

    端口默认 8848;mysql 持久化可以修改 conf/application.properties 切换数据库,默认内置 derby 内存数据库,重启数据丢失。


    第一部分:Nacos 服务注册发现(Naming)

    作用:

    各个微服务启动时,把自己的 ip、端口、服务名注册到 Nacos;

    其他服务通过服务名找到实例,做负载均衡调用;不需要写死 IP 地址。

    1. 依赖

    Spring‑Boot3 父工程 dependencyManagement

    <dependencyManagement>
    <dependencies>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-dependencies</artifactId>
    <version>2023.0.1</version>
    <type>pom</type>
    <scope>import</scope>
    </dependency>
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-dependencies</artifactId>
    <version>2023.0.1.0</version>
    <type>pom</type>
    <scope>import</scope>
    </dependency>
    </dependencies>
    </dependencyManagement>

    业务服务 / Gateway 网关都引入 nacos‑discovery:

    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>

    2. application.yml 配置

    spring:
    application:
    name: demo-service # ✅服务名称,注册到nacos的服务名,全局唯一
    cloud:
    nacos:
    discovery:
    server-addr: 127.0.0.1:8848 # nacos服务地址
    # namespace: public #命名空间,环境隔离 dev/test/prod

    启动类加上注解 @EnableDiscoveryClient

    @SpringBootApplication
    @EnableDiscoveryClient
    public class DemoServiceApplication {
    public static void main(String[] args) {
    SpringApplication.run(DemoServiceApplication.class, args);
    }
    }

    启动服务后,打开 nacos 控制台 → 服务列表,可以看到 demo‑service,实例 ip 端口显示出来。

    Gateway 网关使用 Nacos(前面讲过)

    网关同样引入 nacos‑discovery,路由写 lb://demo-service

    spring:
    cloud:
    gateway:
    routes:
    – id: demo‑route
    uri: lb://demo-service # lb=loadbalance负载均衡,从nacos拿服务实例
    predicates:
    – Path=/demo/**
    filters:
    – StripPrefix=1

    lb://服务名:Gateway 底层调用 SpringCloud LoadBalancer,从 Nacos 获取实例列表,做负载均衡转发。

    3. 其他服务如何调用注册到 Nacos 的服务

    方式 1:OpenFeign(推荐,微服务内部调用)

    引入 feign 依赖

    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>

    启动类开启 Feign:@EnableFeignClients

    @SpringBootApplication
    @EnableDiscoveryClient
    @EnableFeignClients
    public class CallerApplication {}

    编写 Feign 接口:直接写 Nacos 服务名,不需要 ip 端口

    @FeignClient("demo-service") // nacos注册的服务名
    public interface DemoFeignClient {

    @GetMapping("/api/hello")
    String hello();

    @GetMapping("/api/user/{id}")
    String getUser(@PathVariable("id") Long id);
    }

    Controller 直接注入使用

    @RestController
    public class TestController {
    @Resource
    private DemoFeignClient demoFeignClient;

    @GetMapping("/testFeign")
    public String test(){
    return demoFeignClient.hello();
    }
    }

    Feign 底层:服务名交给 LoadBalancer,去 Nacos 拉取实例列表,负载均衡选出实例发起 http 调用。

    方式 2:LoadBalancerClient + RestTemplate

    @Bean
    @LoadBalanced // 开启负载均衡注解
    public RestTemplate restTemplate(){
    return new RestTemplate();
    }

    调用:直接写 http://demo‑service/api/hello

    String url = "http://demo-service/api/hello";
    String res = restTemplate.getForObject(url,String.class);

    ⚠️注意:RestTemplate 加了@LoadBalanced才会解析服务名;不加就会把 demo‑service 当成域名,报错。

    Nacos 服务发现核心概念

  • Service 服务:服务名称,例如 demo‑service,一个服务可以启动多个实例。
  • Instance 实例:同一个服务启动多份,就是多个实例(多节点部署)。
  • Namespace 命名空间:环境隔离,dev、test、prod;不同 namespace 之间服务互相不可见。默认 public。
  • Group 分组:同一个命名空间下再细分分组,例如 group‑a、group‑b;不同 group 默认不能互相发现。
  • 权重 weight:实例权重,Nacos 负载均衡可以按权重分配流量;权重 0 不接收流量,可以做灰度发布。
  • 健康检查:nacos 定时探测实例状态;实例宕机会自动剔除,不会接收流量。
  • 负载均衡策略:SpringCloud LoadBalancer 默认轮询;支持自定义随机、权重。

    服务发现完整流程

    demo‑service启动
    ↓
    @EnableDiscoveryClient,nacos‑discovery客户端启动
    ↓
    向Nacos Server注册:服务名demo‑service,本机ip,port
    ↓
    Nacos控制台保存服务实例列表
    ↓
    【调用方服务】Feign调用 @FeignClient("demo‑service")
    ↓
    LoadBalancer去Nacos Server拉取 demo‑service可用实例列表
    ↓
    负载均衡算法挑选一个实例ip:port
    ↓
    发起http请求访问实例
    ↓
    demo‑service返回结果

    客户端会本地缓存实例列表,定时更新;不会每次调用都请求 Nacos 服务端。


    第二部分:Nacos 配置中心 Config

    作用:把 yml 配置放到 Nacos 控制台,不需要打包到项目内;修改配置不用重启服务,自动刷新。 适合:数据库配置、业务参数、开关、限流参数。

    1. 依赖

    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>

    注意:配置中心读取的是 bootstrap.yml(SpringBoot3 需要额外引入 bootstrap 支持) Spring‑Boot3 默认不加载 bootstrap.yml,必须引入:

    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bootstrap</artifactId>
    </dependency>

    2. bootstrap.yml(优先级高于 application.yml)

    spring:
    application:
    name: demo-service
    cloud:
    nacos:
    config:
    server-addr: 127.0.0.1:8848
    file-extension: yaml #配置文件后缀 yaml/properties
    namespace: public

    DataId 拼接规则(重点)

    DataId = ${spring.application.name}.${file‑extension}
    demo-service.yaml

    打开 Nacos 控制台 → 配置管理 → 配置列表,新建配置:

    • Data ID:demo-service.yaml
    • 配置格式:YAML
    • 配置内容:

    app:
    name: nacos测试应用
    limit: 10

    3. 代码读取配置,实现自动刷新

    加上注解 @RefreshScope,配置变更自动刷新 bean 的值。

    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.cloud.context.config.annotation.RefreshScope;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;

    @RestController
    @RefreshScope // ✅关键:nacos配置变更自动刷新
    public class ConfigTestController {

    @Value("${app.name}")
    private String appName;

    @Value("${app.limit}")
    private Integer limit;

    @GetMapping("/config")
    public String getConfig(){
    return "appName="+appName + " limit="+limit;
    }
    }

    测试:Nacos 控制台修改配置,发布;不用重启服务,再次访问接口,值已经更新。

    Nacos‑Config 核心概念

  • DataId:配置 ID,一般和服务名对应;
  • Group:分组,默认 DEFAULT;
  • Namespace:命名空间,dev/test/prod 隔离配置;
  • 配置的优先级:bootstrap > nacos 远程配置 > application.yml 本地配置;远程配置覆盖本地配置。
  • 配置共享:多个服务共用一份配置,可以使用 shared‑configs。
  • 示例:共享配置,所有服务读取公共配置

    spring:
    cloud:
    nacos:
    config:
    shared-configs:
    – data-id: common.yaml
    group: DEFAULT
    refresh: true

    Nacos 配置中心完整流程

    服务启动
    ↓
    加载bootstrap.yml,知道nacos地址、dataId
    ↓
    nacos‑config客户端去Nacos Server拉取远程配置 demo‑service.yaml
    ↓
    远程配置合并到Spring环境变量,优先级高于本地application.yml
    ↓
    Bean装配,@Value注入配置
    ↓
    客户端长轮询监听Nacos配置变更
    ↓
    控制台修改配置并发布
    ↓
    客户端感知配置变更,刷新上下文;@RefreshScope修饰的Bean重新加载属性
    ↓
    程序无需重启,新配置生效


    第三部分:Gateway 网关读取 Nacos 配置中心

    网关也可以接入 nacos‑config,把路由配置放到 Nacos,实现动态路由,不用重启网关。 bootstrap.yml (gateway 服务)

    spring:
    application:
    name: gateway-server
    cloud:
    nacos:
    config:
    server-addr: 127.0.0.1:8848
    file-extension: yaml

    Nacos 新建 DataId:gateway-server.yaml,存放网关路由配置。 程序监听 nacos 配置变更事件,调用 RouteDefinitionWriter 更新路由。

    伪代码思路:监听 Nacos 配置变更,解析 yaml 路由 json,调用 routeDefinitionWriter 保存路由定义。


    Nacos 常见踩坑

  • 启动不加 -m standalone,集群模式启动失败。
  • SpringBoot3 不引入 spring‑cloud‑starter‑bootstrap,bootstrap.yml 不生效,读不到 nacos 配置。
  • 忘记加 @RefreshScope:修改 nacos 配置,程序不刷新。
  • Namespace 写错:服务注册在 dev 命名空间,消费方在 public,服务列表看不见。
  • Feign 调用服务名写错,大小写敏感。
  • 服务注册成功,但是 Feign 调用报错找不到实例:检查 namespace、group 两边必须一致。
  • derby 内存数据库,重启 nacos 配置丢失;生产切换 Mysql 持久化。
  • Nacos 在微服务整体位置(结合 Gateway)

    客户端浏览器APP
    ↓
    Spring Cloud Gateway(网关)
    ├─Nacos‑Discovery:注册网关自身;获取下游微服务实例 lb://xxx
    └─Nacos‑Config:读取网关路由、限流参数配置
    ↓
    下游微服务A、B、C
    ├─Nacos‑Discovery:服务注册;服务之间OpenFeign调用
    └─Nacos‑Config:读取业务配置,动态刷新

    面试高频问题

  • Nacos AP 还是 CP?
    • 服务发现:AP 模式(高可用);配置中心:CP 模式(强一致)。
  • Nacos 和 Eureka 区别? Eureka 只有服务注册;Nacos 同时有注册中心 + 配置中心;支持权重、命名空间;Eureka 已经停止维护。
  • Nacos 长轮询? 配置中心客户端使用长轮询监听配置变更;不会频繁轮询服务端。
  • @RefreshScope 原理? 动态代理 Bean;配置变更销毁旧 Bean,创建新 Bean 加载新属性。
  • SpringCloud Gateway + Nacos‑Config 实现动态路由

    目标:路由规则保存在 Nacos 配置中心,修改 Nacos 配置发布,网关不需要重启,路由实时生效。 版本:SpringBoot3.2.x、SpringCloud2023.0.x、Spring‑Cloud‑Alibaba 2023.0.1.0,JDK17

    实现思路:

  • 网关引入 nacos‑config、bootstrap 依赖;使用 bootstrap.yml 读取 nacos 配置。
  • Nacos 存放路由配置(JSON 格式,推荐 JSON 解析,yaml 格式解析容易缩进出错)。
  • 网关监听 Nacos 配置变更事件。
  • 收到变更事件:解析路由 JSON,调用 RouteDefinitionWriter,清空旧路由,写入新路由。
  • 新增 / 修改 / 删除路由只需要在 Nacos 控制台修改配置发布,网关无需重启。
  • ⚠️注意:代码方式管理动态路由之后,application.yml 里面写的 routes 静态路由会和动态路由共存;如果完全使用 nacos 管理路由,把 yml 中的 spring.cloud.gateway.routes 全部删掉。

    1.Gateway 网关完整 pom 新增依赖

    <!– gateway核心 –>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    <!– nacos服务发现 –>
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!– nacos配置中心 –>
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>
    <!– SpringBoot3必须,支持bootstrap.yml –>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bootstrap</artifactId>
    </dependency>
    <!– jackson解析JSON路由配置 –>
    <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    </dependency>

    2.bootstrap.yml(网关模块,重点!)

    bootstrap 优先级高于 application.yml;nacos‑config 需要读取 bootstrap。

    spring:
    application:
    name: gateway-server
    cloud:
    nacos:
    discovery:
    server-addr: 127.0.0.1:8848
    config:
    server-addr: 127.0.0.1:8848
    file-extension: json # 我们使用JSON格式存放路由配置
    namespace: public
    # 指定路由配置的data‑id;也可以使用默认 ${spring.application.name}.json
    data-id: gateway-router.json

    application.yml(网关,删除所有 routes 静态路由)

    server:
    port: 8080
    spring:
    cloud:
    gateway:
    # 不要写 routes!全部由nacos动态加载

    3.Nacos 控制台创建配置

    配置管理 → 配置列表 → 新建配置:

    • Data ID:gateway-router.json
    • 配置格式:JSON
    • Group:DEFAULT 配置内容:

    [
    {
    "id": "demo-service-route",
    "uri": "lb://demo-service",
    "predicates": [
    {
    "name": "Path",
    "args": {
    "_genkey_0": "/demo/**"
    }
    }
    ],
    "filters": [
    {
    "name": "StripPrefix",
    "args": {
    "_genkey_0": "1"
    }
    }
    ],
    "order": 0
    }
    ]

    字段解释:

    • id:路由唯一标识
    • uri:lb://demo-service 使用 nacos 负载均衡;也可以写死 http://127.0.0.1:9001
    • predicates:断言数组;_genkey_0 是 SpringGateway 内部参数 key 固定写法
    • filters:过滤器数组;StripPrefix=1
    • order:路由执行顺序,数字越小优先级越高。

    4. 动态路由核心代码

    package com.example.gatewayserver;

    import com.alibaba.cloud.nacos.NacosConfigManager;
    import com.alibaba.cloud.nacos.NacosConfigProperties;
    import com.fasterxml.jackson.core.type.TypeReference;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import jakarta.annotation.PostConstruct;
    import org.springframework.cloud.gateway.route.RouteDefinition;
    import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
    import org.springframework.stereotype.Component;
    import reactor.core.publisher.Mono;

    import java.util.List;

    /**
    * Nacos动态路由监听器
    * 监听nacos gateway-router.json配置变更,动态刷新网关路由
    */
    @Component
    public class NacosDynamicRouteService {

    private final RouteDefinitionWriter routeDefinitionWriter;
    private final NacosConfigManager nacosConfigManager;
    private final NacosConfigProperties nacosConfigProperties;
    private final ObjectMapper objectMapper;

    private static final String ROUTE_DATA_ID = "gateway-router.json";
    private static final String ROUTE_GROUP = "DEFAULT";

    public NacosDynamicRouteService(RouteDefinitionWriter routeDefinitionWriter,
    NacosConfigManager nacosConfigManager,
    NacosConfigProperties nacosConfigProperties,
    ObjectMapper objectMapper) {
    this.routeDefinitionWriter = routeDefinitionWriter;
    this.nacosConfigManager = nacosConfigManager;
    this.nacosConfigProperties = nacosConfigProperties;
    this.objectMapper = objectMapper;
    }

    /**
    * Bean初始化完成,加载路由 + 注册监听
    */
    @PostConstruct
    public void init() {
    // 1.首次启动加载路由
    loadRoutes();
    // 2.注册nacos配置变更监听器
    addListener();
    }

    /**
    * 从nacos读取配置,解析为RouteDefinition,更新路由
    */
    private void loadRoutes() {
    try {
    // nacos读取配置字符串
    String configInfo = nacosConfigManager.getConfigService()
    .getConfig(ROUTE_DATA_ID, ROUTE_GROUP, 3000);
    if (configInfo == null || configInfo.isBlank()) {
    System.err.println("nacos路由配置为空!");
    return;
    }
    // json反序列化为路由列表
    List<RouteDefinition> routeDefinitions = objectMapper.readValue(configInfo,
    new TypeReference<List<RouteDefinition>>() {
    });

    // =========关键:先删除内存中全部旧路由========
    routeDefinitionWriter.delete(Mono.just("*")).subscribe();

    // =========循环写入新路由========
    for (RouteDefinition definition : routeDefinitions) {
    routeDefinitionWriter.save(Mono.just(definition)).subscribe();
    }
    System.out.println("✅网关动态路由刷新成功,共加载:" + routeDefinitions.size() + "条路由");
    } catch (Exception e) {
    e.printStackTrace();
    }
    }

    /**
    * 添加nacos配置变更监听器;配置发布自动回调loadRoutes
    */
    private void addListener() {
    try {
    nacosConfigManager.getConfigService().addListener(
    ROUTE_DATA_ID,
    ROUTE_GROUP,
    event -> {
    System.out.println("🔔检测到Nacos路由配置变更,开始刷新路由");
    loadRoutes();
    }
    );
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    }

    启动类

    @SpringBootApplication
    @EnableDiscoveryClient
    public class GatewayServerApplication {
    public static void main(String[] args) {
    SpringApplication.run(GatewayServerApplication.class, args);
    }
    }

    5. 测试动态路由功能

  • 启动 Nacos;启动 demo‑service 业务服务;启动 gateway 网关。 控制台打印:✅网关动态路由刷新成功,共加载:1条路由
  • 访问:

    curl -H "token:abc123" http://127.0.0.1:8080/demo/api/hello

    可以转发到下游业务服务。

  • 修改 Nacos 中 gateway-router.json,新增第二条路由:
  • [
    {
    "id": "demo-service-route",
    "uri": "lb://demo-service",
    "predicates": [
    {
    "name": "Path",
    "args": {
    "_genkey_0": "/demo/**"
    }
    }
    ],
    "filters": [
    {
    "name": "StripPrefix",
    "args": {
    "_genkey_0": "1"
    }
    }
    ],
    "order": 0
    },
    {
    "id": "test-route",
    "uri": "http://httpbin.org/get",
    "predicates": [
    {
    "name": "Path",
    "args": {
    "_genkey_0": "/test/**"
    }
    }
    ],
    "order": 1
    }
    ]

    发布 Nacos 配置。网关控制台输出日志:🔔检测到Nacos路由配置变更,开始刷新路由。 访问 http://127.0.0.1:8080/test,新路由立即生效,网关无需重启。

    6. 开启 actuator 查看当前生效路由

    pom 增加 actuator

    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

    application.yml

    management:
    endpoints:
    web:
    exposure:
    include: gateway

    访问地址查看网关所有生效路由:

    http://127.0.0.1:8080/actuator/gateway/routes

    重要坑点说明

  • ❗SpringBoot3,必须引入 spring‑cloud‑starter‑bootstrap,否则 bootstrap.yml 不加载,读取不到 nacos 配置。
  • JSON 里面 predicates、filters 的 args key 固定使用_genkey_0,这是 Gateway 解析 RouteDefinition 约定参数名,写错路由不生效。
  • routeDefinitionWriter.delete(Mono.just("*")) 删除全部旧路由;生产环境如果你希望保留硬编码静态路由,不要写*,需要逐个删除旧 id。
  • 监听器是 Nacos 长轮询;修改 Nacos 配置点发布,网关立刻感知。
  • 如果 JSON 格式写错,jackson 解析异常,路由不会更新,控制台打印异常堆栈。
  • 不要混用 yml 静态 routes 和动态 routes;混用会两份路由同时存在。
  • Nacos 配置 namespace 要一致;如果使用 dev 命名空间,bootstrap.yml 必须指定 namespace。
  • 完整流程(Nacos 动态路由)

    网关启动
    ↓
    加载bootstrap.yml,连接Nacos‑Config
    ↓
    NacosDynamicRouteService @PostConstruct执行
    ↓
    从nacos读取gateway‑router.json字符串
    ↓
    Jackson反序列成List<RouteDefinition>
    ↓
    RouteDefinitionWriter删除内存旧路由,保存新路由到Gateway内存
    ↓
    注册Nacos配置监听器
    ↓
    Nacos控制台修改JSON路由配置,点击发布
    ↓
    Nacos触发监听器回调
    ↓
    再次loadRoutes(),重新拉取JSON,刷新内存路由;无需重启网关
    ↓
    客户端请求到达Gateway,使用内存中新路由规则转发请求

    生产拓展:

  • 可以把路由配置存数据库,写后台管理界面维护路由表;后台修改 DB,推送变更到 Nacos;网关监听 Nacos 自动刷新。
  • 增加异常捕获,当 Nacos 配置 JSON 解析失败,保留上一份可用路由,不要清空全部路由,避免网关全部路由失效。
  • Seata 完整详解

    Seata:分布式事务解决方案,解决微服务多服务操作数据库,要么全部成功、要么全部回滚。

    场景举例:

    下单业务:

  • 订单服务:插入订单(order 库)
  • 库存服务:扣减库存(storage 库)
  • 账户服务:扣减余额(account 库)
  • 三个属于不同微服务、不同数据库。

    如果订单插入成功,扣库存抛出异常 → 数据不一致,订单存在,库存没扣。

    本地事务只能管单个数据库,管不了跨服务。

    Seata 就是解决这个问题。

    Seata 四大模式:

  • AT 模式(最常用,默认,无侵入) ✅生产首选
  • TCC 模式(手动写 try‑confirm‑cancel,侵入业务代码)
  • SAGA 模式:长事务,适合流程很长,补偿模式
  • XA 模式:数据库原生 XA,性能差
  • 版本对齐:

    Spring‑Boot3.2.x,Spring‑Cloud2023.0.x,Spring‑Cloud‑Alibaba 2023.0.1.0;Seata 2.x。 JDK17

    Seata 核心角色

  • TC (Transaction Coordinator) 事务协调者:独立 Seata‑Server 服务。全局事务管理器。维护全局事务、分支事务;通知各个分支提交 / 回滚。
  • TM (Transaction Manager) 事务管理器:业务应用,开启全局事务 @GlobalTransactional。告诉 TC 开启全局事务;提交 / 回滚全局。
  • RM (Resource Manager) 资源管理器:每个微服务,管理本地事务资源;和 TC 交互,注册分支、上报分支状态,执行分支提交 / 回滚。
  • 通俗:

    TM:老板,开启一个大事务;

    TC:调度中心;

    RM:各个干活小弟,每个微服务都是 RM。

    Seata TC / TM / RM 关系(面试必背)

    • TC:Transaction Coordinator 事务协调者(Seata‑Server,独立服务)
    • TM:Transaction Manager 事务管理器(全局事务发起方,加 @GlobalTransactional 的应用)
    • RM:Resource Manager 资源管理器(各个微服务,管理本地数据库分支事务)

    ⚠️ TM、RM 是应用端的角色,不是独立进程;一个应用可以同时既是 TM 又是 RM(最常见,比如订单服务自己开事务,又操作自己库)。

    几对几关系

  • TC ↔ TM:一对多 1 个 TC 可以接收多个 TM发起的全局事务; 一个全局事务,只有 1 个 TM(谁开的@GlobalTransactional谁就是 TM)。
  • TC ↔ RM:一对多 1 个 TC 管理很多 RM; 一个全局事务 (XID),可以关联 N 个 RM(N 个分支事务)。

    也就是:1 个 XID(全局事务)对应多个分支事务 BID,TC 里全局事务表 ↔ 分支事务表是一对多。

  • TM ↔ RM:没有直接调用(事务层面) TM 和 RM 之间只有普通业务 RPC 调用,事务指令不直接互通;所有事务指令全部走 TC 中转。
    • TM 告诉 TC:开启 / 提交 / 回滚全局事务
    • TC 再通知各个 RM:分支提交 / 回滚
    • RM 只跟 TC 通信,不跟 TM 做事务交互。
  • 一句话记忆(面试口述)

    TC 是中心,一个 TC 对应多个 TM、多个 RM;一次全局事务只有一个 TM,可以有多个 RM;TM 和 RM 之间没有事务直接交互。

    典型例子

    订单服务加了@GlobalTransactional → TM; 订单服务操作自己数据库 → 同时也是RM; 库存服务、账户服务参与这个分布式事务 → RM; Seata‑Server → TC。

    • 1 个 TC;
    • 1 个 TM(订单);
    • 3 个 RM(订单、库存、账户)。

    数据库层面两张核心表(TC 存储)

  • global_table(全局事务):XID 为主键,一条记录代表一次全局事务
  • branch_table(分支事务):BID 为主键,每条代表一个 RM 的分支 👉 global_table : branch_table = 1 : N(一对多)。
  • 一、Seata‑Server 服务端安装启动(TC)

    1. 下载 seata‑server‑2.2.0.zip https://github.com/seata/seata/releases

    重要:Seata Server 有两种存储模式

  • file:内存存储,重启事务数据丢失,只用于 demo 测试。
  • db:数据库存储(生产必须),事务信息持久化 mysql。
  • 1)修改 conf/application.yml seata‑server 配置

    seata:
    registry:
    type: nacos # TC注册到Nacos,微服务RM/TM通过nacos发现TC
    nacos:
    application: seata-server
    server-addr: 127.0.0.1:8848
    group: DEFAULT
    config:
    type: nacos
    nacos:
    server-addr: 127.0.0.1:8848
    store:
    mode: db # 生产使用db存储
    db:
    datasource: druid
    db-type: mysql
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/seata_server?useUnicode=true
    username: root
    password: xxx

    执行 seata 官方数据库脚本,创建 seata_server 库三张表: global_table(全局事务)、branch_table(分支事务)、lock_table(AT 模式行锁)。

    启动 seata‑server:

    # windows
    bin/seata-server.bat
    # linux
    bin/seata-server.sh

    启动后,Nacos 控制台服务列表看到服务名:seata‑server。

    微服务不需要填 seata‑server 的 ip 端口,直接通过 Nacos 发现 TC。

    二、AT 模式(自动事务,业务几乎无侵入,重点)

    AT 模式原理:基于本地事务 + undo_log 回滚日志表。

    每个参与分布式事务的业务库,都必须建一张 undo_log 表!

    1. 每个业务数据库执行 undo_log 建表语句

    CREATE TABLE IF NOT EXISTS `undo_log`
    (
    `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'increment id',
    `branch_id` BIGINT(20) NOT NULL COMMENT 'branch transaction id',
    `xid` VARCHAR(128) NOT NULL COMMENT 'global transaction id',
    `context` VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
    `rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info',
    `log_status` INT(11) NOT NULL COMMENT '0:normal status,1:defense status',
    `log_created` DATETIME NOT NULL COMMENT 'create datetime',
    `log_modified` DATETIME NOT NULL COMMENT 'modify datetime',
    PRIMARY KEY (`id`),
    UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
    ) ENGINE = InnoDB
    AUTO_INCREMENT = 1
    DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table';

    order 库、storage 库、account 库,每个库都要创建 undo_log。

    2. 微服务客户端 pom 依赖(TM、RM)

    所有参与分布式事务的微服务都引入

    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    </dependency>

    3. 微服务 application.yml 配置

    spring:
    application:
    name: order-service
    cloud:
    alibaba:
    seata:
    # 事务分组,非常关键!
    tx-service-group: my_tx_group
    nacos:
    discovery:
    server-addr: 127.0.0.1:8848

    # seata配置:告诉事务分组去哪里找TC seata‑server
    seata:
    registry:
    type: nacos
    nacos:
    server-addr: 127.0.0.1:8848
    config:
    type: nacos
    nacos:
    server-addr: 127.0.0.1:8848
    service:
    # key:tx‑service‑group名称;value是nacos里面seata‑server服务名
    vgroup-mapping:
    my_tx_group: seata-server

    tx‑service‑group:事务分组。客户端通过 vgroup‑mapping 映射找到 TC 服务。 所有微服务 tx‑service‑group 保持一致。

    AT 模式业务代码示例

    业务场景:order‑service 作为 TM,发起全局事务;调用 storage‑service 扣库存,调用 account‑service 扣余额。服务之间 OpenFeign 调用。

    ① 订单服务(TM,发起全局事务)

    @RestController
    public class OrderController {

    @Resource
    private OrderService orderService;

    @PostMapping("/createOrder")
    public String createOrder(){
    orderService.createOrder();
    return "下单完成";
    }
    }

    @Service
    public class OrderService {

    @Resource
    private OrderMapper orderMapper;
    @Resource
    private StorageFeignClient storageFeignClient;
    @Resource
    private AccountFeignClient accountFeignClient;

    // ✅开启全局分布式事务,TM
    @GlobalTransactional(rollbackFor = Exception.class)
    public void createOrder(){
    // 1.本地数据库:插入订单,order库,RM本地事务
    orderMapper.insert();

    // 2.Feign远程调用库存服务,扣减库存
    storageFeignClient.deduct();

    // 3.Feign远程调用账户服务,扣余额
    accountFeignClient.deduct();

    // 如果这里抛出异常,全部回滚:订单插入撤销、库存回滚、余额回滚
    // int i = 1/0;
    }
    }

    ② Storage 库存服务 RM

    Feign 接口

    @FeignClient("storage-service")
    public interface StorageFeignClient {
    @PostMapping("/storage/deduct")
    void deduct();
    }

    库存服务业务代码

    @Service
    public class StorageService {
    @Resource
    private StorageMapper storageMapper;

    // RM,不需要加@GlobalTransactional;只需要本地@Transactional
    @Transactional
    public void deduct(){
    storageMapper.deductStock();
    }
    }

    ③ Account 账户服务 RM

    @Service
    public class AccountService {
    @Resource
    private AccountMapper accountMapper;

    @Transactional
    public void deduct(){
    accountMapper.deductMoney();
    }
    }

    关键点:

  • 只有发起方加 @GlobalTransactional(rollbackFor = Exception.class);远程被调用微服务只需要普通 @Transactional。
  • Feign 调用,XID 全局事务 ID 会自动透传 Header,Seata 拦截 Feign 请求自动传递 xid,不需要手动处理。
  • rollbackFor = Exception.class:一定要写;默认 RuntimeException 才回滚,普通 Exception 不会回滚。
  • AT 模式完整执行流程(重点面试)

    成功流程:

  • TM 执行 @GlobalTransactional,向 TC 申请,生成全局唯一 XID(全局事务 id)。XID 通过 Feign 调用传递到所有 RM 微服务。
  • Order‑Service RM:执行业务 SQL(insert order)。
    • Before Image:执行前记录数据快照写入 undo_log。
    • 执行业务 DML。
    • After Image:执行后数据快照写入 undo_log。
    • 本地事务提交(业务数据 + undo_log 一同提交数据库)。
    • RM 向 TC 报告分支事务完成。
  • Feign 调用 Storage‑Service,携带 XID;RM 同样生成 before/after image 写 undo_log,本地事务提交,上报 TC。
  • Feign 调用 Account‑Service,携带 XID;RM 同上。
  • TM 所有业务执行完毕,通知 TC:全局事务提交。
  • TC 通知所有 RM 分支提交。RM 收到提交指令,删除各自 undo_log 记录。全局完成。
  • 失败回滚流程(例如 account 服务抛异常):

  • Account 服务抛出异常,本地事务回滚,并上报 TC:分支失败。
  • TC 收到异常,向所有 RM 发送全局回滚指令。
  • Order RM:读取 undo_log 的 before_image,把数据还原,回滚业务数据;删除 undo_log。
  • Storage RM:读取 undo_log before_image,还原库存;删除 undo_log。
  • 全部数据恢复到执行之前状态。
  • AT 模式隔离级别:读未提交;中间未提交的数据其他事务可以读到;Seata 有行锁机制防止脏写。


    TCC 模式(手动补偿模式)

    AT 自动生成 undo_log;TCC 完全自己写业务补偿逻辑,没有 undo_log 表。 三个方法:

  • Try:资源检查、预留
  • Confirm:确认执行业务(全部 Try 成功才执行 Confirm)
  • Cancel:取消,释放预留资源(任意 Try 失败执行 Cancel 回滚)
  • @LocalTCC
    public interface StorageTCCService {
    @TwoPhaseBusinessAction(name = "storageTCC", commitMethod = "confirm", rollbackMethod = "cancel")
    boolean tryDeduct(@BusinessActionContextParameter(paramName = "count") Integer count);

    boolean confirm(BusinessActionContext ctx);

    boolean cancel(BusinessActionContext ctx);
    }

    TCC 适合:不支持 AT 的数据库;或者复杂业务。缺点:侵入业务代码,开发量大。

    Saga 模式:长事务

    适合长流程,比如订单、出库、物流,跨很久;每个业务提供正向接口 + 反向补偿接口;没有锁。

    缺点:没有隔离性,会出现脏读脏写。

    XA 模式:数据库原生两阶段提交

    数据库本身支持 XA 协议;性能差,生产很少用。


    Seata 常见坑点(高频面试)

  • 每个参与 AT 模式业务库必须建 undo_log 表!忘记建,直接报错。
  • @GlobalTransactional rollbackFor 不写,Exception 不会回滚。
  • tx‑service‑group 事务分组配置错误:vgroup‑mapping 映射不到 nacos 里面 seata‑server 服务名,客户端连不上 TC。
  • 内部调用:同一个类内部方法调用 @GlobalTransactional 不会生效(AOP 代理失效),和 Spring 事务坑一样。
  • Feign 调用丢失 XID:seata 自动拦截 feign 传递 XID;不要自己重写 Feign 拦截器覆盖 header。
  • 数据源代理:Seata AT 模式会代理 DataSource;不要自己手动 new 数据源,交给 Spring 容器管理。
  • seata‑server 使用 file 存储:重启丢失事务数据;生产必须 db 存储。
  • 异常被 try‑catch 吞掉:
  • @GlobalTransactional
    public void test(){
    try{
    feign.call();
    }catch (Exception e){
    // ❌捕获异常不抛出,TM认为业务正常,不会回滚!
    }
    }

    捕获异常后需要 throw 抛出,TM 才能感知异常。

    微服务整体架构图汇总(Gateway + Nacos + Seata)

    APP/浏览器
    ↓
    Spring Cloud Gateway(网关,鉴权、限流、路由;注册Nacos;读取Nacos‑Config动态路由)
    ↓
    order‑service(TM @GlobalTransactional,发起分布式事务)
    ↓Feign调用
    storage‑service (RM)
    ↓Feign调用
    account‑service (RM)

    Nacos:
    服务发现:Gateway、order、storage、account、seata‑server全部注册
    配置中心:各个微服务读取配置

    Seata‑Server(TC):独立服务注册Nacos
    协调全局事务;存储全局/分支事务记录(mysql db模式)

    order库、storage库、account库:每个库都有 undo_log(AT模式)

    面试高频题

  • AT 模式和 TCC 区别? AT:无侵入,自动 undo‑log 快照;TCC 手动写 Try Confirm Cancel,侵入业务。
  • @GlobalTransactional 和 @Transactional 区别? @Transactional:本地数据库事务;@GlobalTransactional:全局分布式事务,由 TC 协调多个 RM。
  • AT 模式什么时候回滚? TM 捕获异常,TC 通知所有 RM 回滚;利用 undo_log before_image 恢复数据。
  • XID 作用?全局唯一事务 ID,透传到各个微服务,标识属于同一个分布式事务。
  • Sentinel 完整详解

    Sentinel:阿里开源的流量控制、熔断降级、系统自适应保护组件,面向微服务,保护服务不被打垮。

    版本对齐:SpringBoot3.2.x,SpringCloud2023.0.x,Spring‑Cloud‑Alibaba 2023.0.1.0,JDK17。

    两大核心部分:

  • Sentinel‑Dashboard:可视化控制台,配置规则、监控流量(独立 web 服务)
  • Sentinel‑Client:嵌入到每个微服务,执行限流熔断规则。
  • 核心能力:

  • 流量控制(限流):QPS、线程数限流;控制请求的速度。
  • 熔断降级:调用慢、异常多,直接熔断,不调用下游,快速失败。
  • 系统自适应保护:根据 CPU、Load 保护整个服务。
  • 热点参数限流:针对热点参数做限流。
  • 授权规则:黑白名单来源访问控制。
  • Sentinel 底层核心:滑动窗口(时间窗口统计),统计 QPS、异常比例、慢调用比例。


    一、Sentinel‑Dashboard 控制台安装

    下载:sentinel‑dashboard‑2.0.0.jar

    2.x 版本适配 SpringBoot3。

    启动命令:

    java -jar sentinel-dashboard-2.0.0.jar

    默认端口:8080 访问:http://127.0.0.1:8080 账号密码:sentinel / sentinel

    ⚠️注意:

  • Dashboard 默认内存模式,重启所有规则丢失;生产配置 Nacos 持久化规则。
  • 微服务客户端需要主动上报心跳到 dashboard,控制台才能看到服务。
  • 微服务客户端 pom 依赖

    所有需要防护的微服务引入 sentinel starter

    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>

    application.yml 配置(微服务 client)

    spring:
    cloud:
    sentinel:
    transport:
    dashboard: 127.0.0.1:8080 # sentinel‑dashboard地址
    port: 8719 # client本地通信端口,默认8719,如果被占用自动+1
    # 关闭sentinel懒加载;服务启动立刻连接dashboard,否则第一次访问才注册
    eager: true

    启动微服务后,访问一次接口,Sentinel 控制台就能看到该服务。

    Sentinel 是懒加载:默认第一次访问资源,才会注册到控制台;配置eager:true启动就注册。

    核心概念

  • 资源 Resource:需要保护的对象,可以是接口、方法。
    • @SentinelResource 注解标记方法为资源。
    • SpringMVC 接口默认就是资源,资源名就是接口路径 /order/create。
  • 规则 Rule:流控规则、熔断规则、热点规则等。
  • BlockException:被限流 / 熔断触发抛出的异常。
  • SlotChain 责任链:Sentinel 核心;一系列 slot 依次执行:统计、限流、熔断、系统保护。

  • 二、流量控制(流控规则 FlowRule)

    流量控制:限制资源的访问速率,防止流量打满服务。

    流控模式

    1. 直接:对当前资源限流。

    2. 关联:关联其它资源;关联资源达到阈值,限流本资源。

    关联的场景:下单、查询订单;下单压力大,限制查询订单,保护下单。

    3. 链路:只记录指定入口链路上的流量;同一个方法多个调用入口,只对某一条调用链路限流。

    流控阈值类型

    • QPS(每秒查询数):每秒请求数量达到阈值,直接拦截。

    例如 QPS=5:每秒最多 5 个请求,超过直接拒绝。

    • 线程数:并行工作线程数阈值。

    例如线程数 = 10:最多 10 个线程同时执行业务方法;新来请求拒绝。适合 IO 密集型。

    流控效果

    当超过阈值之后怎么处理

  • 快速失败:直接抛出 BlockException;默认。
  • Warm‑Up(预热):冷启动;阈值慢慢抬升。
  • 场景:服务刚启动,缓存还没初始化,瞬间大流量会打垮;warm‑up 慢慢放开流量。

          3.排队等待:漏桶;匀速排队;请求排队,按固定速率放行。适合突发流量削峰。

    示例:接口 /order/create,流控模式直接,阈值 QPS=3,快速失败。每秒超过 3 次请求直接限流。

    @SentinelResource 注解自定义资源

    @Service
    public class OrderBizService {

    /**
    value:资源名称
    blockHandler:限流/熔断异常处理函数
    fallback:业务异常降级函数
    */
    @SentinelResource(value = "createOrderBiz",
    blockHandler = "blockHandlerFunc",
    fallback = "fallbackFunc")
    public String createOrder(Long userId){
    //业务逻辑
    return "下单成功";
    }

    // blockHandler:只处理sentinel拦截(限流、熔断)抛出BlockException
    public String blockHandlerFunc(Long userId, BlockException e){
    return "系统繁忙,请稍后重试";
    }

    // fallback:业务异常也会进入,异常Throwable
    public String fallbackFunc(Long userId,Throwable e){
    return "业务降级:下单失败";
    }
    }

    区分:

    • blockHandler:Sentinel 规则拦截(限流熔断)才走,入参必须带上 BlockException。
    • fallback:业务代码抛出异常也走降级。

    Controller 调用该 service 方法,资源名使用createOrderBiz,控制台针对这个资源配置流控规则。


    三、熔断降级规则 DegradeRule(重点)

    熔断降级:下游服务不稳定,慢调用多、异常多,触发熔断,短时间直接拒绝,不去调用下游,避免雪崩。 雪崩:A 调用 B,B 卡住;大量请求线程堆积在 A,A 服务线程耗尽整个服务卡死。

    Sentinel 三种熔断策略:

    • 慢调用比例

    统计时间窗口内,慢调用占比超过阈值 → 熔断。 慢调用:响应时间大于设置 RT 阈值。 例如 RT=200ms;超过 200ms 算慢调用;慢调用比例 > 50% 触发熔断。熔断时长内,所有请求直接降级。

    • 异常比例

    时间窗口内,抛出异常请求占比超过阈值,熔断。 例如异常比例阈值 0.4,40% 请求抛异常,打开熔断。

    • 异常数

    时间窗口内,异常请求数量达到阈值,触发熔断。

    参数:

    • 熔断时长:熔断打开之后,持续多久;时间到进入半开状态。
    • 最小请求数:窗口内请求总数达到这个数字才做统计,否则不触发熔断。

    例如最小请求数 = 5;窗口只有 2 次请求,就算全部异常,也不会熔断。

    熔断状态机:

  • 关闭 Closed:正常放行。
  • 打开 Open:熔断,所有请求直接降级。等待熔断时长。
  • 半开 Half‑Open:时间到,放行少量探测请求;探测请求成功 → 关闭熔断;探测失败 → 重新打开熔断。
  • 注意:Sentinel 的熔断是本地熔断,每个微服务自己独立统计,不是全局。

    OpenFeign 整合 Sentinel 熔断

    Feign 开启 sentinel 支持,application.yml

    feign:
    sentinel:
    enabled: true

    Feign 接口编写降级类

    @FeignClient(value = "storage-service", fallback = StorageFeignFallback.class)
    public interface StorageFeignClient {
    @PostMapping("/storage/deduct")
    void deduct(@RequestParam Long productId,@RequestParam Integer count);
    }

    @Component
    public class StorageFeignFallback implements StorageFeignClient{
    @Override
    public void deduct(Long productId, Integer count) {
    //熔断降级逻辑
    throw new RuntimeException("库存服务暂时不可用,降级");
    }
    }

    当 storage‑service 慢调用、异常、熔断触发,自动进入 fallback 降级类。


    四、热点参数限流

    针对请求参数做限流。 场景:商品详情接口,某个热点商品 id 访问量巨大;限制热点商品 ID 的 QPS。

    @SentinelResource(value = "getGoods", blockHandler = "hotBlock")
    public String getGoods(Long goodsId){
    return "商品详情";
    }

    控制台热点规则:资源getGoods,参数索引 0(第一个参数 goodsId);阈值 QPS=5。

    goodsId=100(热点)最多每秒 5 次;

    其它 goodsId 不受限制。还可以配置参数例外项。

    五、系统规则 SystemRule

    保护整个服务实例,不是某个资源。 可以设置阈值:CPU 使用率、LOAD、平均 RT、QPS、最大线程数。

    例如 CPU 超过 80%,拒绝新来请求。生产谨慎使用。

    六、授权规则 AuthorityRule

    黑白名单,根据调用来源 origin 控制是否允许访问。 配合RequestOriginParser解析请求来源。

    @Component
    public class CustomOriginParser implements RequestOriginParser {
    @Override
    public String parseOrigin(HttpServletRequest request) {
    //从header获取调用方标识origin
    return request.getHeader("origin");
    }
    }

    控制台授权规则:白名单 / 黑名单填写 origin 的值。

    七、Sentinel 规则持久化到 Nacos(生产必做)

    Sentinel‑dashboard 默认内存保存规则,重启全部丢失。生产把规则存 Nacos 配置中心;微服务从 Nacos 拉取规则,规则变更实时生效。

    客户端 application.yml

    spring:
    cloud:
    sentinel:
    transport:
    dashboard: 127.0.0.1:8080
    # nacos持久化流控规则
    datasource:
    flow:
    nacos:
    server-addr: 127.0.0.1:8848
    data-id: order-service-flow-rules
    group-id: DEFAULT
    rule-type: flow
    degrade:
    nacos:
    server-addr: 127.0.0.1:8848
    data-id: order-service-degrade-rules
    group-id: DEFAULT
    rule-type: degrade

    rule-type 可选:flow流控、degrade熔断、hot‑param热点、authority授权、system系统。

    Nacos 新建配置,order‑service‑flow‑rules格式 JSON 示例:

    [
    {
    "resource": "/order/create",
    "grade": 1,
    "count": 3,
    "strategy": 0,
    "controlBehavior": 0
    }
    ]

    字段说明:

    • grade:1=QPS,0 = 线程数
    • count:阈值
    • strategy:流控模式 0 直接,1 关联,2 链路
    • controlBehavior:流控效果 0 快速失败,1warmup,2 排队等待

    修改 Nacos 配置发布,Sentinel 客户端自动感知,规则实时生效,不需要重启微服务。 ⚠️注意:此时控制台 dashboard 只是监控展示;规则以 Nacos 为准,在控制台修改规则不会持久化回 Nacos;真正生产,在 Nacos 编辑规则。

    八、Sentinel 全局异常处理器

    SpringMVC 接口被限流熔断,默认返回报错页面。自定义全局捕获 BlockException 统一返回 JSON。

    @RestControllerAdvice
    public class SentinelGlobalExceptionHandler {

    @ExceptionHandler(BlockException.class)
    public ResponseEntity<?> blockHandler(BlockException ex){
    return ResponseEntity.status(429).body("请求被限流或熔断,访问过于频繁");
    }
    }

    注意:@SentinelResource标记的方法,blockHandler 会优先于全局异常处理器执行。

    九、Sentinel 与 Gateway 网关整合

    Gateway 网关也可以接入 Sentinel,对网关路由做限流。 网关依赖:

    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
    </dependency>

    可以针对网关路由 id、API 分组做限流。

    十、Sentinel 核心原理(面试重点)

            1. SlotChain 责任链 请求进入资源,执行 SlotChain:

    • StatisticSlot:统计 QPS、RT、异常数(滑动窗口)
    • FlowSlot:流控判断
    • DegradeSlot:熔断降级判断
    • SystemSlot:系统保护
    • AuthoritySlot:授权黑白名单

            2. 滑动时间窗口(LeapArray) Sentinel 统计底层滑动窗口;默认 1s 分割为 2 个窗口,每个窗口 500ms。 不使用固定窗口,解决临界突刺问题。 根据时间定位当前窗口,统计请求计数;窗口过期自动废弃。

    十一、Sentinel 常见坑

  • @SentinelResource 不生效:资源名不要写接口路径;不要和 controller 路径混淆;blockHandler 函数参数必须包含 BlockException,方法 public。
  • blockHandler 和 fallback 分不清:blockHandler 只管 sentinel 拦截;fallback 管业务异常。
  • 懒加载:不配置 eager:true,服务启动控制台看不到服务,必须访问一次接口。
  • Feign Sentinel 降级:必须开启feign.sentinel.enabled=true。
  • 规则内存模式重启丢失;生产务必接入 Nacos 持久化。
  • 熔断最小请求数:窗口请求数量不够,不会触发熔断。
  • Sentinel 熔断是客户端本地熔断,每个实例独立统计,不是集群全局。
  • 全局异常处理器,对@SentinelResource内部 blockHandler 不生效。
  • 十二、整套微服务技术栈汇总回顾

    前端浏览器APP
    ↓
    Spring Cloud Gateway
    ├─Nacos‑Discovery:服务注册,获取下游服务实例 lb://xxx
    ├─Nacos‑Config:动态网关路由配置
    └─Sentinel‑Gateway:网关层限流、防护
    ↓
    order‑service(TM) storage‑service(RM) account‑service(RM)
    ├─Nacos‑Discovery:注册发现,OpenFeign调用
    ├─Nacos‑Config:业务配置动态刷新
    ├─Seata‑AT:分布式事务 @GlobalTransactional,undo_log回滚
    └─Sentinel‑Client:限流、熔断降级,保护微服务,规则持久化Nacos

    Nacos:
    服务发现:所有微服务注册
    配置中心:网关路由、Sentinel规则、业务参数

    Seata‑Server(TC):分布式事务协调

    Sentinel‑Dashboard:流量监控,规则管理

    高频面试题

  • Sentinel 和 Hystrix 对比?
    • Hystrix:只做熔断降级,已经停止维护;线程池隔离;
    • Sentinel:阿里,功能更全,限流、熔断、热点、系统保护;滑动窗口;支持 Nacos 持久化;信号量隔离为主。
  • Sentinel 熔断三种策略区别?慢调用比例、异常比例、异常数。
  • blockHandler 和 fallback 区别。
  • 滑动窗口作用,解决什么问题?解决固定窗口临界流量突刺。
  • 熔断半开状态作用:少量探测请求判断下游是否恢复。
  • Spring Cloud LoadBalancer 详解

    Spring‑Cloud‑LoadBalancer:SpringCloud 官方客户端负载均衡组件,用来替代老旧的 Ribbon(已停止维护)。

    作用:从 Nacos/Eureka 服务注册中心拿到服务实例列表,在客户端本地做负载均衡,选择一个服务实例发起请求。

    区分两个概念:

  • 服务端负载均衡:Nginx、F5;请求先到 LB 服务器,再转发后端实例。
  • 客户端负载均衡 LoadBalancer:消费端本地拿到实例清单,自己算法选实例,直接调用目标服务。
  • 技术栈对齐:

    SpringBoot3.2.x,SpringCloud 2023.0.x,Spring‑Cloud‑Alibaba 2023.0.1.0。 OpenFeign 底层默认使用 LoadBalancer;RestTemplate 也可以集成 LoadBalancer。

    一、依赖说明

    SpringCloud 2020 之后,移除 Ribbon,默认使用 LoadBalancer。

    • 如果使用 OpenFeign:feign starter 已经传递依赖 LoadBalancer,不需要额外引入。
    • 如果自己用 RestTemplate 做负载均衡,手动引入:

    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
    </dependency>

    同时需要服务注册发现组件,例如 Nacos Discovery,从注册中心获取可用服务实例列表。

    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>

    二、基础使用方式

    方式 1:RestTemplate + LoadBalancer

    给 RestTemplate 加上 @LoadBalanced 注解,开启客户端负载均衡能力。

    @Configuration
    public class RestConfig {
    @Bean
    @LoadBalanced // 开启LoadBalancer增强
    public RestTemplate restTemplate(){
    return new RestTemplate();
    }
    }

    使用时,URL 写服务名(不是 ip:port)

    // storage-service:Nacos注册的服务名称
    String url = "http://storage-service/storage/deduct?productId=1&count=2";
    String resp = restTemplate.getForObject(url,String.class);

    @LoadBalanced原理:

    给 RestTemplate 增加拦截器 LoadBalancerInterceptor;

    拦截 http 请求,解析服务名,

    从注册中心拉实例列表,负载均衡算法选出实例,替换成真实 ip 端口发起 http 调用。

    方式 2:OpenFeign(底层自动使用 LoadBalancer)

    @FeignClient("storage-service")
    public interface StorageFeignClient {
    @PostMapping("/storage/deduct")
    void deduct(@RequestParam Long productId,@RequestParam Integer count);
    }

    Feign 内部会调用 LoadBalancerClient,完成实例选择。不需要手动写@LoadBalanced。

    方式 3:手动使用 LoadBalancerClient API

    可以手动调用 API 获取实例,适合自定义场景:

    @Resource
    private LoadBalancerClient loadBalancerClient;

    public void test(){
    // 根据服务名选择实例
    ServiceInstance instance = loadBalancerClient.choose("storage-service");
    String host = instance.getHost();
    int port = instance.getPort();
    System.out.println(host + ":" + port);
    }

    三、LoadBalancer 内置负载均衡策略

    Spring‑Cloud‑LoadBalancer 提供两种内置策略:

  • RoundRobinLoadBalancer —— 轮询(默认)
  • 按顺序依次循环选取实例。实例 A→B→C→A→B→C。 适合各实例性能差不多的场景。

  • RandomLoadBalancer —— 随机
  • 在可用实例列表随机挑选一个。

    ⚠️注意:和 Ribbon 不一样,LoadBalancer没有内置权重策略。 如果需要权重负载均衡,使用 Nacos Discovery 自带权重能力;或者自己自定义 LoadBalancer 策略。

    修改默认负载均衡策略

    语法:服务名 + .loadbalancer.rule,只针对指定服务生效。 application.yml

    spring:
    cloud:
    loadbalancer:
    clients:
    storage-service: # 针对storage-service服务,使用随机策略
    rule: random
    order-service: # order-service使用轮询(默认)
    rule: round_robin

    四、Nacos 权重负载均衡(重点面试)

    Nacos 服务实例可以配置权重,权重越大被选中概率越高。

    SpringCloud LoadBalancer 本身没有权重算法;Nacos‑Discovery 扩展了 LoadBalancer,支持权重。

    Nacos 控制台 → 服务管理 → 实例列表,可以修改实例权重。

    权重范围:0‑100;权重 = 0,不会接收流量。

    场景:灰度发布。新启动实例权重设置很小 (10),老实例权重 90;少量流量打到新版本,验证没问题再把权重拉满。

    开启权重负载均衡:

    spring:
    cloud:
    nacos:
    discovery:
    # 开启nacos权重负载均衡,底层替换LoadBalancer实例选择逻辑
    weight: true

    开启 weight:true 之后,会覆盖掉 LoadBalancer 自带的 round_robin/random 策略;优先使用 Nacos 权重算法。

    五、LoadBalancer 核心概念

  • ServiceInstance:服务实例对象,包含 host、port、serviceId、元数据 metadata。
  • LoadBalancerClient:顶层接口;核心方法 choose(String serviceId),选出实例。
  • ReactorLoadBalancer:响应式版本,SpringCloud Gateway 底层使用。
  • LoadBalancerInterceptor:RestTemplate 拦截器,解析服务名,替换真实地址。
  • 服务实例缓存:LoadBalancer 会缓存服务实例列表,不会每次都请求 Nacos;Nacos‑Discovery 定时更新实例缓存。
  • 六、自定义负载均衡策略

    1)自定义普通阻塞策略(RestTemplate / Feign)

    实现 ReactorLoadBalancer<ServiceInstance>,或者扩展 ServiceInstanceListSupplier。 示例:自定义基于 metadata 元数据的策略。

    public class CustomLoadBalancer implements ReactorLoadBalancer<ServiceInstance> {
    private final String serviceId;
    private final ObjectProvider<ServiceInstanceListSupplier> supplierProvider;

    public CustomLoadBalancer(String serviceId, ObjectProvider<ServiceInstanceListSupplier> supplierProvider) {
    this.serviceId = serviceId;
    this.supplierProvider = supplierProvider;
    }

    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
    ServiceInstanceListSupplier supplier = supplierProvider.getIfAvailable();
    return supplier.get().next().map(list->{
    // 自定义选择逻辑,从list中挑选实例
    ServiceInstance selected = list.get(0);
    return new DefaultResponse<>(selected);
    });
    }
    }

    配置类绑定给某个服务:

    @Configuration
    @LoadBalancerClient(value = "storage-service", configuration = CustomLoadBalancerConfig.class)
    public class CustomLoadBalancerConfig {
    @Bean
    public ReactorLoadBalancer<ServiceInstance> customLoadBalancer(Environment environment,
    ObjectProvider<ServiceInstanceListSupplier> supplier){
    String serviceId = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
    return new CustomLoadBalancer(serviceId, supplier);
    }
    }

    @LoadBalancerClient(value="服务名"),只对指定服务生效。不要加@Component全局生效。

    七、健康实例过滤

    ServiceInstanceListSupplier 负责拿到实例列表,可以做过滤:只保留健康实例。

    • Nacos Discovery 会自动过滤掉不健康实例;
    • LoadBalancer 自带过滤:支持过滤掉非 up 状态实例。

    application.yml

    spring:
    cloud:
    loadbalancer:
    health-check:
    enabled: true # 开启健康检查过滤实例

    八、LoadBalancer 重试机制

    调用服务实例失败,自动换一个实例重试。

    ⚠️注意:重试一定要注意幂等性!

    非幂等接口(新增订单)不要开启重试,会产生重复数据。

    <!– 重试需要额外引入spring‑retry依赖 –>
    <dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    </dependency>

    配置:

    spring:
    cloud:
    loadbalancer:
    retry:
    enabled: true # 开启重试
    max-retries-on-same-service-instance: 0 # 同一个实例重试次数,一般0,直接换实例
    max-retries-on-next-service-instance: 2 # 切换其他实例最大重试次数

    重试只会对 GET 等幂等请求生效;POST 默认不重试。

    如果需要 POST 重试,需要额外配置,业务接口必须保证幂等。

    ⚠️风险:如果接口不是幂等,重试会创建多条订单、重复扣库存。

    Seata 分布式事务场景尤其要小心。

    九、LoadBalancer 和 Ribbon 对比

    RibbonSpring‑Cloud‑LoadBalancer
    Netflix 开源,已停止维护 SpringCloud 官方实现,持续维护
    内置轮询、随机、权重、重试等多种策略 内置只有轮询、随机;权重靠注册中心扩展(Nacos weight)
    大量自定义 API API 简洁;响应式支持好,适配 Gateway
    Feign 默认底层 新版 SpringCloud 默认;Gateway 底层使用

    SpringCloud 2020.0 之后完全移除 Ribbon,只能用 LoadBalancer。

    十、常见坑点

  • RestTemplate 忘记加@LoadBalanced,不能解析服务名,报错 unknown host。
  • 想使用权重负载均衡,不要只改 LoadBalancer 策略;需要打开 spring.cloud.nacos.discovery.weight=true。
  • 开启重试,一定要注意接口幂等性;POST 接口谨慎开启。
  • @LoadBalancerClient配置类不要加@Component,否则变成全局策略,影响所有服务。
  • Feign 调用超时:LoadBalancer 只是选实例;超时时间由 Feign/http client 控制,不是 LoadBalancer 控制。
  • 实例列表缓存:不会实时拉 Nacos;Nacos 客户端定时更新实例缓存,服务上下线有短暂延迟。
  • 十一、完整调用链路回顾(OpenFeign + LoadBalancer + Nacos)

    storageFeignClient.deduct(1L,2);
    ↓ Feign拦截
    ↓ LoadBalancerClient.choose("storage-service")
    ↓ NacosDiscovery获取storage‑service全部可用实例列表
    ↓ 负载均衡算法(轮询 / Nacos权重)选出一个ServiceInstance
    ↓ 拿到真实host:port,组装http请求地址
    ↓ http调用真实服务实例

    如果调用失败 + 开启重试: 捕获异常,再次 choose,挑选另一个实例重试。

    十二、放到整套微服务架构视角

    前端
    ↓
    SpringCloud Gateway(底层使用ReactorLoadBalancer)
    ↓
    order‑service
    ├─OpenFeign
    │ └─LoadBalancerClient:客户端负载均衡,选择storage‑service实例
    │ └─Nacos‑Discovery:获取服务实例清单,支持权重
    ├─Sentinel:限流熔断保护
    └─Seata‑AT:分布式事务

    Nacos:服务注册中心,保存所有微服务实例、权重、元数据。

    面试高频问题

  • LoadBalancer 和 Ribbon 区别?
  • LoadBalancer 有哪些内置策略?怎么实现权重负载均衡?
  • @LoadBalanced注解原理是什么?
  • 客户端负载均衡和服务端负载均衡区别?
  • LoadBalancer 重试机制,有什么风险?
  • OpenFeign 底层负载均衡是怎么工作的?
  • Micrometer + Prometheus + Grafana 微服务监控完整详解

    整套监控链路: 微服务 (SpringBoot) → Micrometer 埋点输出 Metrics 指标 → Prometheus 拉取指标数据 → Grafana 做可视化大盘、告警

    组件角色分工

  • Micrometer:Java 指标门面,类似 SLF4J。提供统一 API 采集 JVM、Tomcat、SpringMVC、Feign、Sentinel、Seata 等指标;输出各种监控格式 (Prometheus、InfluxDB 等)。SpringBoot3 Actuator 底层默认集成 Micrometer。
  • Prometheus:时序数据库,定时拉取 (Pull 模式) 各个服务的 metrics 接口,存储时序指标数据;提供 PromQL 查询语言。
  • Grafana:可视化面板工具,对接 Prometheus 数据源;绘制图表、大盘、配置告警通知。
  • 技术栈对齐:

    SpringBoot3.2.x、SpringCloud2023.0.x、Spring‑Cloud‑Alibaba2023.0.1.0、JDK17。

    完整微服务栈:Nacos + Gateway + OpenFeign + LoadBalancer + Sentinel + Seata‑AT + Micrometer‑Prometheus + Grafana。

    一、SpringBoot 微服务客户端改造(Micrometer + Actuator + Prometheus)

    每个微服务(order‑service/storage‑service/account‑service/gateway)都需要引入依赖。

    pom.xml

    <!– actuator 提供端点 –>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!– micrometer prometheus registry,输出prometheus格式metrics –>
    <dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
    </dependency>

    micrometer-registry-prometheus 自动把 Micrometer 指标暴露到 /actuator/prometheus 端点。

    application.yml 配置

    management:
    endpoints:
    web:
    exposure:
    # 暴露端点:health健康检查、prometheus指标端点
    include: health,prometheus
    metrics:
    export:
    prometheus:
    enabled: true
    tags:
    # 给所有指标打上全局tag,区分服务名、环境,多实例非常重要
    application: ${spring.application.name}
    endpoint:
    health:
    show-details: always

    启动微服务,访问:

    http://127.0.0.1:8091/actuator/prometheus

    页面输出大量 prometheus 格式指标。

    内置自动采集指标(开箱即用)

    • JVM:堆内存、非堆内存、GC 次数、GC 耗时、线程数、类加载 jvm_*
    • Tomcat:tomcat 线程、请求数 tomcat_*
    • SpringMVC 接口:http 请求 QPS、响应时间 http_server_requests_seconds_*
    • Logback 日志:错误日志计数 logback_events_total
    • Feign 调用:feign 请求指标
    • Sentinel:Sentinel 也提供 micrometer 指标(需要额外依赖)
    • Seata:Seata 也支持输出 metrics

    指标格式示例:

    http_server_requests_seconds_bucket{application="order-service",exception="none",method="POST",outcome="SUCCESS",status="200",uri="/order/create",le="0.005",} 2.0

    标签 application="order-service" 用来区分不同微服务。

    Sentinel 对接 Micrometer(可选)

    Sentinel 输出限流熔断指标到 prometheus:

    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-micrometer</artifactId>
    </dependency>

    会生成sentinel_*系列指标,例如限流次数、熔断次数。

    Seata 对接 Micrometer(可选)

    Seata 客户端 metrics:

    <dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-micrometer</artifactId>
    <version>2.2.0</version>
    </dependency>

    输出 seata_global_tx、seata_branch_tx 事务成功失败指标。

    自定义业务指标(Counter、Timer)

    Micrometer 常用 4 种 Metric 类型

  • Counter 计数器:只增不减,请求数、错误数。
  • Timer 计时器:统计耗时,分布直方图。
  • Gauge[ɡeɪdʒ]仪表盘:瞬时值,内存、队列大小,可以涨可以跌。
  • Summary 摘要:记录大小分布。
  • @RestController
    @RequestMapping("/order")
    public class OrderController {

    // 计数器:下单总次数
    private final Counter orderCreateCounter;
    // Timer统计下单接口耗时
    private final Timer orderCreateTimer;

    public OrderController(MeterRegistry meterRegistry){
    this.orderCreateCounter = meterRegistry.counter("biz.order.create.total");
    this.orderCreateTimer = Timer.builder("biz.order.create.duration")
    .publishPercentiles(0.5,0.9,0.95,0.99) // P50 P90 P95 P99分位
    .register(meterRegistry);
    }

    @PostMapping("/create")
    public String create(){
    return orderCreateTimer.record(()->{
    orderCreateCounter.increment(); //计数器+1
    //执行业务下单
    return "下单成功";
    });
    }
    }

    自定义指标会自动出现在 /actuator/prometheus。


    二、安装 Prometheus

    Prometheus 采用 Pull 拉取模式:Prometheus 主动调用各个微服务的 /actuator/prometheus 获取指标。

    两种安装方式:二进制 /docker;推荐 docker 快速部署。

    Docker 启动 Prometheus

  • 创建配置文件 prometheus.yml
  • global:
    scrape_interval: 15s # 默认15秒拉取一次指标
    evaluation_interval: 15s # 告警规则评估周期

    scrape_configs:
    # job名称:order-service
    – job_name: 'order-service'
    metrics_path: '/actuator/prometheus'
    static_configs:
    – targets: ['127.0.0.1:8091']

    – job_name: 'storage-service'
    metrics_path: '/actuator/prometheus'
    static_configs:
    – targets: ['127.0.0.1:8092']

    – job_name: 'account-service'
    metrics_path: '/actuator/prometheus'
    static_configs:
    – targets: ['127.0.0.1:8093']

    – job_name: 'gateway'
    metrics_path: '/actuator/prometheus'
    static_configs:
    – targets: ['127.0.0.1:8080']

    static_configs 静态写死实例地址只适合本地测试。 生产不要写死 targets,使用 Nacos 服务发现自动发现微服务实例(prometheus nacos sd)。

  • docker run 启动,挂载配置文件
  • docker run -d \\
    –name prometheus \\
    -p 9090:9090 \\
    -v $(pwd)/prometheus.yml:/etc/prometheus/prometheus.yml \\
    prom/prometheus

    访问 Prometheus UI:http://127.0.0.1:9090

    • Status → Targets:查看各个 job 实例是否 UP。UP 代表成功拉取 metrics。
    • Graph 页面:写 PromQL 查询指标。

    简单 PromQL 示例

    # order‑service接口QPS
    rate(http_server_requests_seconds_count{application="order-service"}[1m])

    # JVM堆内存使用
    jvm_memory_used_bytes{application="order-service",area="heap"}

    # GC每秒次数
    rate(jvm_gc_pause_seconds_count{application="order-service"}[1m])

    # P99响应时间
    http_server_requests_seconds{application="order-service",quantile="0.99"}

    ⚠️生产重点:

    Prometheus Nacos‑SD 自动发现服务实例,不用手动维护 target;

    Prometheus 接入 Alertmanager 做告警。


    三、安装 Grafana

    Docker 快速部署:

    docker run -d \\
    –name grafana \\
    -p 3000:3000 \\
    grafana/grafana

    访问:http://127.0.0.1:3000 默认账号密码:admin / admin,首次登录强制修改密码。

    步骤 1:添加 Prometheus 数据源

  • 左侧菜单 Connections → Data sources → Add data source
  • 选择 Prometheus
  • URL 填写:http://prometheus:9090(docker 网络内部)或者 http://127.0.0.1:9090
  • 点击 Save & test,提示 Data source is working。
  • 步骤 2:导入 SpringBoot 通用监控大盘模板

    Grafana Dashboard 市场 ID:

    • Spring Boot Micrometer:12900(非常经典,JVM、HTTP 请求、GC、线程,开箱即用)

    操作:

    • 左侧 Dashboards → Import,输入 ID 12900 → Load;
    • 选择 Prometheus 数据源,导入。

    导入完成立刻看到:

    服务列表、JVM 内存、GC、线程、接口 QPS、P95/P99 耗时、错误率大盘。

    步骤 3:自己新建自定义业务面板

    示例:业务下单 QPS 面板

    • 新建 Panel,数据源选 Prometheus
    • PromQL:

    rate(biz_order_create_total[1m])

    图表类型选 Time series。

    步骤 4:配置告警规则

  • 在 Grafana 面板编辑页 → Alert,创建告警规则。 示例告警条件:order‑service 接口错误率 >5% 触发告警。 PromQL 条件:
  • sum(rate(http_server_requests_seconds_count{status=~"5.."}[1m])) / sum(rate(http_server_requests_seconds_count[1m])) > 0.05

    配置通知渠道:邮件、钉钉、企业微信。

    ⚠️生产最佳实践:告警规则建议写在 Prometheus 配置文件配合 Alertmanager,Grafana 告警适合简单场景。


    四、整套数据流完整流程

    微服务(SpringBoot)
    ↓ Micrometer埋点采集各类指标(JVM、http、自定义业务、Sentinel、Seata)
    ↓ Actuator暴露端点 /actuator/prometheus,输出prometheus格式文本
    ↓ Prometheus(Pull模式,15s拉取一次) http请求拉取metrics数据
    ↓ Prometheus存储时序数据库
    ↓ Grafana查询PromQL,渲染可视化图表、大盘、告警


    五、关键概念 Micrometer

    Meter 几种类型

  • Counter:计数器,只增不减,.increment();统计总请求数、错误数。
  • Timer:计时器,记录事件发生次数与耗时;生成 count、sum、bucket 直方图,用于 P50/P90/P99。
  • Gauge:瞬时值,获取当前数值;内存、活跃线程、队列长度。
  • DistributionSummary:大小分布,比如消息大小。
  • Tag 标签

    指标带上 tag,用来做维度过滤:application服务名、uri接口路径、status响应码、method请求方法。

    不要放高基数 tag(用户 ID、订单 ID),tag 爆炸会打爆 Prometheus 存储。

    常见 PromQL 函数

  • rate(xxx[1m]):计算区间每秒增长率,计数器必须用 rate。
  • sum by(application):按服务名聚合。
  • histogram_quantile(0.99, …):计算 P99 分位耗时。
  • ❗注意:Counter 类型指标绝对不要直接用,必须套 rate/increase。

    示例:

    # order‑service所有接口QPS,按uri分组
    sum by(uri) (rate(http_server_requests_seconds_count{application="order-service"}[1m]))


    六、生产环境注意事项 & 坑点

  • 高基数标签问题:不要把 userId、orderId 作为 tag;会造成指标数量爆炸,Prometheus 存储 CPU 暴涨。
  • Pull 模式:Prometheus 主动拉取;微服务只需要暴露 /actuator/prometheus,不需要推送。
  • actuator 端点安全:生产不要对外网暴露 /actuator;在内网网络访问;可以加 SpringSecurity 保护端点。
  • Prometheus 存储:单机 Prometheus 数据默认保留 15 天;长期监控需要远端存储(Thanos/Mimir)。
  • Nacos‑Service‑Discovery for Prometheus:生产不要 static 写死 targets;Prometheus 集成 Nacos SD 自动发现所有微服务实例。
  • 时间序列 cardinality(基数)监控,防止指标爆炸。
  • 告警区分级别:P0 服务宕机、P1 错误率高、P2 JVM 内存偏高。
  • SpringCloud Gateway 同样接入 actuator+micrometer‑prometheus,网关层指标监控。
  • 常见问题

  • prometheus target 显示 DOWN:检查防火墙;确认/actuator/prometheus可以访问;检查 management.endpoints.web.exposure.include 是否暴露 prometheus 端点。
  • Grafana 图表没有数据:PromQL 是否正确;rate 范围是否匹配 scrape_interval;确认指标 tag application 是否和服务匹配。
  • Timer 看不到 P99:构建 Timer 的时候必须配置 publishPercentiles。

  • 七、整合进整套微服务完整架构总图

    前端APP
    ↓
    Spring Cloud Gateway
    ├─Nacos‑Discovery 服务注册发现
    ├─LoadBalancer 客户端负载均衡
    ├─Sentinel‑Gateway网关限流熔断
    └─Micrometer+Actuator:输出gateway metrics(/actuator/prometheus)
    ↓Feign调用
    order‑service / storage‑service / account‑service
    ├─Nacos‑Config配置中心
    ├─OpenFeign + LoadBalancer
    ├─Sentinel客户端限流熔断,输出sentinel metrics
    ├─Seata‑AT分布式事务,输出seata metrics
    └─Micrometer+Actuator:JVM、http、自定义业务指标 prometheus端点

    外部组件:
    Nacos:服务注册、配置中心
    Seata‑Server:事务协调
    Sentinel‑Dashboard:流量监控

    监控链路:
    Prometheus:pull拉取所有微服务metrics,时序存储
    Grafana:可视化大盘、告警

    面试高频题

  • Micrometer 作用是什么?为什么用它,不直接写 Prometheus 格式?
  • Micrometer 是指标门面 API,屏蔽不同监控系统差异;业务代码只写一套 API,可以对接 Prometheus、InfluxDB 等多种监控。

  • Prometheus Pull 模式和 Push 模式区别?
  • Pull:Prometheus 主动拉;简单;目标实例暴露 metrics 接口。Push:客户端推到 pushgateway;适合短生命周期任务。微服务一般 Pull。

  • Counter 为什么要用 rate () 函数?
  • Counter 只增不减,rate 计算一段时间内每秒增量;直接取值没有意义。

  • 什么是高基数 Tag,有什么危害?
  • tag 取值数量巨大,时间序列暴涨,Prometheus 内存 CPU 压力巨大。

  • Timer 直方图,P95/P99 怎么计算?histogram_quantile。
  • PromQL 速查表 + Prometheus Nacos‑SD 完整配置

    环境:SpringBoot3 + Micrometer + Prometheus + Grafana 所有指标自带 tag:application="xxx‑service",用来区分微服务。

    一、常用 PromQL 速查表

    🔹 HTTP 请求指标(SpringMVC / Gateway)

    # 1、按服务、接口统计QPS(每秒请求数)
    sum by(application,uri) (rate(http_server_requests_seconds_count[1m]))

    # 2、接口错误QPS(5xx)
    sum by(application,uri) (rate(http_server_requests_seconds_count{status=~"5.."}[1m]))

    # 3、接口错误率(按服务)
    sum(rate(http_server_requests_seconds_count{status=~"5.."}[1m]))
    /
    sum(rate(http_server_requests_seconds_count[1m]))

    # 4、P99响应耗时
    histogram_quantile(0.99, sum by(le,application,uri) (rate(http_server_requests_seconds_bucket[1m])))

    # 5、P95响应耗时
    histogram_quantile(0.95, sum by(le,application,uri) (rate(http_server_requests_seconds_bucket[1m])))

    # 6、平均响应时间
    sum by(application,uri)(rate(http_server_requests_seconds_sum[1m]))
    /
    sum by(application,uri)(rate(http_server_requests_seconds_count[1m]))

    🔹 JVM 指标

    # JVM堆内存使用字节
    jvm_memory_used_bytes{area="heap"}

    # JVM堆内存最大
    jvm_memory_max_bytes{area="heap"}

    # 堆内存使用率
    jvm_memory_used_bytes{area="heap"} / jvm_memory_max_bytes{area="heap"}

    # GC每秒次数
    sum by(application) (rate(jvm_gc_pause_seconds_count[1m]))

    # GC停顿耗时(sum,1m区间)
    sum by(application) (rate(jvm_gc_pause_seconds_sum[1m]))

    # 当前活跃线程数
    jvm_threads_live_threads

    # 守护线程
    jvm_threads_daemon_threads

    # 已加载类数量
    jvm_classes_loaded_classes

    🔹 Tomcat 容器指标

    # tomcat繁忙线程数
    tomcat_threads_busy_threads

    # tomcat最大线程
    tomcat_threads_config_max_threads

    # tomcat线程使用率
    tomcat_threads_busy_threads / tomcat_threads_config_max_threads

    🔹 Sentinel 指标(引入 spring‑cloud‑alibaba‑sentinel‑micrometer)

    # sentinel总通过QPS
    sum by(application,resource) (rate(sentinel_total_pass[1m]))

    # sentinel限流拒绝QPS
    sum by(application,resource) (rate(sentinel_total_block[1m]))

    # sentinel异常QPS
    sum by(application,resource) (rate(sentinel_total_exception[1m]))

    # sentinel熔断降级QPS
    sum by(application,resource) (rate(sentinel_total_degrade[1m]))

    🔹 Seata 分布式事务指标(seata‑micrometer)

    # 全局事务成功QPS
    sum by(application) (rate(seata_global_tx_total{status="committed"}[1m]))

    # 全局事务回滚QPS
    sum by(application) (rate(seata_global_tx_total{status="rollbacked"}[1m]))

    # 分支事务QPS
    sum by(application) (rate(seata_branch_tx_total[1m]))

    🔹 自定义业务指标示例

    业务埋点:biz_order_create_total Counter;biz_order_create_duration Timer

    # 下单QPS
    rate(biz_order_create_total[1m])

    # 下单P99耗时
    histogram_quantile(0.99, sum by(le,application) (rate(biz_order_create_duration_bucket[1m])))

    🔹 常用函数说明

  • rate(metric[1m]):Counter 专用,计算 1 分钟内每秒增量;只适合计数器。
  • increase(metric[1m]):时间段内总增量。
  • sum by(tag):按标签聚合。
  • histogram_quantile(0.99, …):直方图计算分位 P99/P95。
  • ~="5..":正则匹配,500‑599 状态码。
  • ❗重要:Counter 类型禁止直接裸用,必须包 rate/increase。


    二、Prometheus 对接 Nacos‑SD 自动发现(生产必备)

    之前 static_configs 写死 targets 只适合本地测试;生产 Prometheus 直接从 Nacos 拉取全部微服务实例,不用手动维护地址。 Prometheus 本身没有内置 Nacos 服务发现,需要第三方 sd 插件:prometheus‑nacos‑sd。

    方案:prometheus‑nacos‑sd(sidecar 模式)

    项目地址:https://github.com/nacos-group/prometheus-nacos-sd 工作模式:

  • prometheus‑nacos‑sd 连接 Nacos,监听服务实例变化。
  • 动态生成 prometheus file‑sd 格式 json 文件。
  • Prometheus 读取该 json 文件,自动加载 targets。
  • 步骤 1:docker 启动 prometheus‑nacos‑sd

    创建配置文件 nacos‑sd‑config.yml

    nacos:
    address: "127.0.0.1:8848"
    namespace: "public"
    username: "nacos"
    password: "nacos"
    group: "DEFAULT"
    # 输出文件路径,prometheus读取
    output:
    file: "/etc/prometheus/nacos_sd_targets.json"

    docker run:

    docker run -d \\
    –name prometheus-nacos-sd \\
    -p 8088:8088 \\
    -v $(pwd)/nacos-sd-config.yml:/app/config.yml \\
    -v $(pwd)/nacos_sd_targets.json:/etc/prometheus/nacos_sd_targets.json \\
    nacos/prometheus-nacos-sd:latest

    启动后,nacos_sd_targets.json 文件会自动生成所有 nacos 服务实例。

    步骤 2:修改 prometheus.yml,使用 file_sd_configs 读取文件

    global:
    scrape_interval: 15s
    evaluation_interval: 15s

    scrape_configs:
    – job_name: 'nacos‑services'
    metrics_path: '/actuator/prometheus'
    file_sd_configs:
    – files:
    – '/etc/prometheus/nacos_sd_targets.json'
    refresh_interval: 10s

    docker prometheus 容器需要挂载这个 json 文件。重启 prometheus。 打开 Prometheus UI → Status → Targets,即可看到 Nacos 上所有微服务实例自动出现在 target 列表。 nacos‑sd 会自动带上标签:service_name、group、namespace,可以在 PromQL 使用。


    三、Prometheus 告警规则示例(alert.rules.yml)

    生产建议使用 Prometheus 原生告警规则 + Alertmanager,比 Grafana 告警更稳定。 新建 alert.rules.yml

    groups:
    – name: service‑alert
    rules:
    # 规则1:实例宕机,target变为down
    – alert: ServiceInstanceDown
    expr: up == 0
    for: 30s
    labels:
    severity: P1
    annotations:
    summary: "服务实例 {{ $labels.instance }} 不可用"
    description: "job={{ $labels.job }}, instance={{ $labels.instance }} 已经down超过30秒"

    # 规则2:5xx错误率大于5%
    – alert: HighErrorRate
    expr: sum(rate(http_server_requests_seconds_count{status=~"5.."}[1m])) / sum(rate(http_server_requests_seconds_count[1m])) > 0.05
    for: 1m
    labels:
    severity: P2
    annotations:
    summary: "服务错误率过高"
    description: "服务 {{ $labels.application }} 错误率 {{ $value }}"

    # 规则3:JVM堆内存使用率超过85%
    – alert: JvmHeapHighUsage
    expr: (jvm_memory_used_bytes{area="heap"} / jvm_memory_max_bytes{area="heap"}) > 0.85
    for: 2m
    labels:
    severity: P2
    annotations:
    summary: "JVM堆内存使用率过高"
    description: "服务 {{ $labels.application }} 堆内存使用率 {{ $value }}"

    # 规则4:GC频繁
    – alert: GcBusy
    expr: rate(jvm_gc_pause_seconds_count[1m]) > 5
    for: 1m
    labels:
    severity: P2
    annotations:
    summary: "GC频繁"

    在 prometheus.yml 引入告警规则文件:

    rule_files:
    – "alert.rules.yml"

    alerting:
    alertmanagers:
    – static_configs:
    – targets:
    – "alertmanager:9093"

    Alertmanager(接收告警,转发钉钉 / 企业微信 / 邮件)

    简单 docker 启动 alertmanager,配置 webhook 对接钉钉机器人。

    docker run -d –name alertmanager -p 9093:9093 -v $(pwd)/alertmanager.yml:/etc/alertmanager/alertmanager.yml prom/alertmanager

    alertmanager.yml 钉钉 webhook 模板示例:

    global:
    resolve_timeout: 5m
    route:
    receiver: 'dingtalk'
    receivers:
    – name: 'dingtalk'
    webhook_configs:
    – url: 'https://dingtalk‑webhook‑url'


    四、Grafana 推荐 Dashboard 模板 ID

    Dashboard ID用途
    12900 Spring Boot Micrometer (JVM、HTTP、GC,最常用)
    14700 Sentinel 监控大盘
    1860 Prometheus 自身监控大盘

    导入方式:Grafana → Dashboards → Import,填入 ID 加载。

    五、生产避坑清单

  • 禁止高基数 tag:不要把 userId、orderId 放到 meter tag;会爆炸生成大量时序。
  • actuator 端点不要暴露公网;放在内网访问,可加 SpringSecurity basic 认证。
  • Prometheus 单机默认保留数据 15 天;长期存储用 Thanos / Mimir。
  • Pull 模式:微服务实例重启,prometheus 自动感知(配合 nacos‑sd)。
  • Counter 指标必须使用 rate/increase;不要直接取值。
  • Timer 想要 P95/P99,代码中必须配置publishPercentiles。
  • for告警持续时间,避免瞬时抖动触发误告警。
  • 开启 Prometheus 监控自身指标 http://prometheus:9090/metrics。
  • 六、完整架构回顾

    微服务SpringBoot
    └─Micrometer采集指标 → Actuator /actuator/prometheus

    prometheus‑nacos‑sd sidecar
    └─监听Nacos实例变更 → file‑sd json文件

    Prometheus
    └─file‑sd读取nacos实例列表,pull拉metrics
    └─rule_files告警规则 → Alertmanager转发钉钉告警

    Grafana
    └─查询PromQL,可视化大盘


    Micrometer Tracing 详解

    Micrometer 分为两大块:

  • micrometer‑core / micrometer‑registry‑prometheus → Metrics 指标(计数器、Gauge、Timer,给 Prometheus)
  • micrometer‑tracing → Tracing 分布式链路追踪(TraceId、Span,调用链)
  • Spring Boot3 已经废弃了旧的 Sleuth,Spring Cloud Sleuth 不再维护,替代方案就是 Micrometer Tracing。

    完整可观测性三大支柱:

    ✅ Metrics(指标‑Prometheus)

    ✅ Tracing(链路‑Zipkin / Jaeger)

    ✅ Logging(日志,日志聚合 ELK/Loki)

    当前整套栈补齐之后完整:

    Nacos + Gateway + OpenFeign + LoadBalancer + Sentinel + Seata‑AT + Micrometer‑Metrics(Prometheus+Grafana) + Micrometer‑Tracing(Zipkin/Jaeger)

    组件分工

  • Micrometer Tracing:统一链路 API 门面,类似 Metrics。生成 Trace、Span;自动埋点:MVC、Feign、RestTemplate、Gateway、Reactor 线程。
  • Tracer 实现:
    • brave:Brave(Zipkin 底层实现,默认)
    • opentelemetry:OpenTelemetry
  • 上报后端:
    • Zipkin(轻量,简单,常用)
    • Jaeger
  • Micrometer‑Tracing 本身不存储链路数据,只是生成 span,上报给 Zipkin/Jaeger。

    Trace:一次完整请求链,全局唯一 TraceId。

    Span:调用链里每一段(接口、Feign 调用、数据库),一个 Trace 包含多个 Span。

    每个日志会自动带上 traceId、spanId,方便日志系统按 traceId 检索日志。

    SpringBoot3 依赖

    每个微服务(gateway、order‑service、storage‑service)都引入:

    <!– micrometer tracing 核心 –>
    <dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-tracing-bridge-brave</artifactId>
    </dependency>
    <!– zipkin exporter:把span上报zipkin –>
    <dependency>
    <groupId>io.zipkin.reporter2</groupId>
    <artifactId>zipkin-reporter-brave</artifactId>
    </dependency>

    如果用 Jaeger,替换成 jaeger exporter。

    application.yml 配置

    management:
    tracing:
    sampling:
    probability: 1.0 # 采样率:1.0全部采样;生产环境0.1 ~ 0.2,不然数据量爆炸
    zipkin:
    tracing:
    endpoint: http://127.0.0.1:9411/api/v2/spans # zipkin上报地址

    • sampling.probability:采样率,开发环境 1.0;生产不要 100% 采样,链路数据量巨大。0.1 代表 10% 请求采样。

    自动埋点开箱即用:

    • SpringMVC Controller
    • OpenFeign 调用
    • RestTemplate
    • Spring Cloud Gateway
    • JDBC 数据库调用
    • Reactor/Webflux

    请求链路示例:

    网关Gateway → order‑service(controller) → Feign调用storage‑service → storage‑service controller TraceId贯穿整条调用链,每个服务日志输出traceId。

    Zipkin 部署(docker)

    docker run -d -p 9411:9411 openzipkin/zipkin

    访问:http://127.0.0.1:9411 可以按 TraceId 查询整条调用链;看每个 span 耗时,定位慢调用。

    日志打印 TraceId / SpanId

    logback/log4j2 日志 pattern 增加 %X{traceId} %X{spanId} logback‑spring.xml

    <pattern>%d{yyyy‑MM‑dd HH:mm:ss.SSS} [%thread] %‑5level %logger{50} traceId=%X{traceId} spanId=%X{spanId} – %msg%n</pattern>

    输出日志示例:

    2026‑09‑06 22:10:00 [http‑nio‑8091‑exec‑1] INFO o.s.w.s.DispatcherServlet traceId=abc123 spanId=def456 – 收到请求

    拿到 traceId,就可以去 Zipkin 查完整调用链,也可以在 Loki/ELK 按 traceId 检索日志。

    手动创建自定义 Span(业务埋点)

    业务代码手动埋一段 span,统计某个业务逻辑块耗时:

    @Service
    public class OrderService {
    private final Tracer tracer;

    public OrderService(Tracer tracer) {
    this.tracer = tracer;
    }

    public void createOrder() {
    // 手动创建span
    Span span = tracer.nextSpan().name("biz‑create‑order‑db").start();
    try (Tracer.SpanInScope ws = tracer.withSpan(span)) {
    // 业务逻辑:数据库插入订单
    } finally {
    span.end();
    }
    }
    }

    链路传播

    Feign/RestTemplate 会自动把 traceparent http header 向下游传递,TraceId 全链路透传。不需要自己处理 header。

    Micrometer Tracing + Metrics 联动

    Micrometer 可以把 tracing 的异常、慢调用生成 metrics,输出给 Prometheus。

    micrometer_tracing_span_* 指标,统计 span 数量、异常 span 数量。

    PromQL 示例:

    # 链路异常span统计
    rate(micrometer_tracing_span_seconds_count{error="true"}[1m])


    完整可观测三大支柱对比

    模块组件用途数据输出
    Metrics 指标 Micrometer‑core + Prometheus+Grafana QPS、错误率、JVM 内存、线程、GC,大盘告警 时序指标
    Tracing 链路追踪 Micrometer‑Tracing + Zipkin/Jaeger 一次请求完整调用链,定位慢调用、哪个环节耗时 TraceId / Span
    Logging 日志 Logback + ELK/Grafana Loki 完整业务日志,错误详情 日志文本,携带 traceId

    三者通过 traceId 关联:

    Grafana 大盘看到接口错误 → 复制 traceId → Zipkin 看调用链 → Loki/ELK 搜 traceId 看完整报错日志。


    补全整套微服务完整架构图(最终完整版)

    前端APP
    ↓
    Spring Cloud Gateway
    ├─Nacos‑Discovery
    ├─LoadBalancer 客户端负载均衡
    ├─Sentinel‑Gateway 限流熔断
    ├─Micrometer‑Metrics:输出actuator/prometheus
    └─Micrometer‑Tracing:生成span上报Zipkin
    ↓ OpenFeign调用
    order‑service / storage‑service / account‑service
    ├─Nacos‑Config
    ├─OpenFeign + LoadBalancer
    ├─Sentinel:限流熔断,sentinel metrics
    ├─Seata‑AT:分布式事务,seata metrics
    ├─Micrometer‑Metrics(JVM/http/biz指标) → Prometheus拉取
    └─Micrometer‑Tracing:自动埋点Feign/MVC/JDBC,span上报Zipkin

    基础中间件:
    Nacos:注册+配置中心
    Seata‑Server:事务协调
    Sentinel‑Dashboard:流量管控

    可观测体系:
    Prometheus:拉取Metrics时序数据
    Grafana:指标大盘、告警
    Zipkin:接收Micrometer‑Tracing span,分布式调用链查询
    Loki/ELK:日志聚合,日志携带traceId
    Alertmanager:告警推送钉钉/企业微信

    面试高频(Micrometer Tracing)

    TraceId、SpanId 概念;采样率作用,生产为什么不能 100% 采样?

    100% 采样会产生海量 span 数据,存储压力巨大。

    traceparent header 作用?

    链路透传?

    Metrics 和 Tracing 区别:

    • Metrics:聚合统计,看整体健康度(QPS、错误率)
    • Tracing:单次请求细节,定位慢请求链路。

    TraceId 怎么打通指标、链路、日志三者?

    常见坑

  • 微服务之间 traceId 断了:确认 Feign/RestTemplate 自动埋点生效;不要手动清除 http header。
  • Zipkin 看不到链路:检查 endpoint 地址;检查采样率;确认依赖 brave+zipkin‑reporter‑brave。
  • 生产采样率不要写 1.0;开发环境可以全开。
  • Gateway(WebFlux)要注意,micrometer‑tracing 对 webflux 是支持的,依赖齐全就自动埋点。
  • 日志看不到 traceId:logback pattern 必须带上%X{traceId} %X{spanId}。
  • SpringCloud 微服务全套面试背诵版

    技术栈:Nacos + Gateway + OpenFeign + LoadBalancer + Sentinel + Seata‑AT + Micrometer (Metrics+Tracing)+Prometheus+Grafana+Zipkin

    适合 5‑8 年 Java 后端,微服务架构面试,全部高频问题,精简可直接背诵。

    一、Nacos(注册中心 + 配置中心)

    Q1 Nacos 作为注册中心原理

  • 服务启动向 Nacos 注册实例,发送心跳;客户端定时拉取服务实例列表。
  • 临时实例:心跳维持,心跳超时自动剔除;用于普通微服务。
  • 持久实例:不会因为心跳失败删除,用于固定中间件。
  • 服务发现:客户端缓存实例列表,本地做负载均衡;服务上下线有延迟。
  • 支持权重,灰度发布;支持 namespace、group 做环境隔离。
  • Q2 Nacos 配置中心原理

  • 微服务启动拉取配置;长轮询监听配置变更。
  • namespace 环境隔离,group 分组,dataId 配置文件。
  • 配置优先级:本地配置 < nacos 配置;支持 @RefreshScope 动态刷新 bean。
  • Q3 Nacos AP 还是 CP

    • 临时实例(服务注册):AP,优先可用性,最终一致性。
    • 持久实例:切换为 CP 模式,保证强一致。

    Q4 Nacos 和 Eureka 对比

  • Eureka 只做注册中心,已经停止维护;Nacos 同时支持注册 + 配置。
  • Nacos 支持权重、namespace、group;Eureka 无。
  • Eureka 自我保护;Nacos 心跳检测更完善。
  • 常见坑

    • 长轮询超时;namespace 写错;@RefreshScope 不生效;权重负载均衡需要开启spring.cloud.nacos.discovery.weight=true。

    二、Spring Cloud Gateway(网关)

    Q1 Gateway 作用

    请求入口;路由转发、鉴权、限流、熔断、跨域、日志、链路埋点;基于 WebFlux 响应式,非阻塞。

    Q2 核心组件

  • Route:路由,id、uri、predicate 断言、filter 过滤器。
  • Predicate 断言:匹配请求路径、Header、参数,匹配成功走该路由。
  • Filter 过滤器:pre 前置、post 后置,修改请求响应。
  • Q3 Gateway 和 Zuul 区别

  • Zuul1 是 Servlet 阻塞 IO,性能差,已停止维护;Zuul2 没集成进 SpringCloud。
  • Gateway 基于 WebFlux 非阻塞,性能高;支持异步;SpringCloud 官方网关。
  • Q4 Gateway 底层负载均衡

    底层使用 ReactorLoadBalancer(Spring‑Cloud‑LoadBalancer),客户端负载均衡,解析服务名从 Nacos 拿实例。

    Q5 Gateway 整合 Sentinel

    Sentinel‑Gateway 实现网关限流,支持路由维度、API 分组限流。

    Q6 可观测

    Gateway 开启 actuator+micrometer‑metrics 输出 prometheus 指标;micrometer‑tracing 自动生成链路 span 上报 Zipkin。


    三、OpenFeign + LoadBalancer

    Q1 OpenFeign 是什么

    声明式 HTTP 客户端;基于注解写接口,自动生成 http 请求;底层封装 http 客户端;默认集成 LoadBalancer 客户端负载均衡。

    Q2 @FeignClient 工作流程

  • 启动扫描 @FeignClient 接口,生成代理对象。
  • 调用接口方法,组装 http 请求。
  • LoadBalancer 根据服务名从 Nacos 选择实例。
  • http 调用下游;解析响应。
  • Q3 LoadBalancer(替代 Ribbon)

    SpringCloud2020 之后移除 Ribbon,官方 LoadBalancer。

  • 客户端负载均衡:消费端本地选实例,直连目标服务,不是 Nginx 这种服务端 LB。
  • 内置策略:轮询 round_robin、随机 random。
  • 权重负载均衡靠 Nacos‑Discovery 扩展,LoadBalancer 本身没有权重策略。
  • @LoadBalanced给 RestTemplate 增加拦截器,解析服务名替换真实 ip 端口。
  • 支持重试;开启重试必须接口幂等,POST 慎用重试,防止重复数据。
  • Q4 客户端负载均衡 vs 服务端负载均衡

    • 客户端 LB(LoadBalancer):客户端拿到实例清单本地算法选实例;直连后端;每个客户端都做负载。
    • 服务端 LB(Nginx):请求先到 LB 服务器,再转发后端;集中式。

    Q5 Feign 超时配置

    Feign 超时不是 LoadBalancer 控制;由 feign http client 参数控制:connectTimeout、readTimeout。

    Q6 Feign 调用 traceId 传递

    Micrometer‑Tracing 自动往 header 写入traceparent,自动透传 traceId,不需要手动处理。


    四、Sentinel(流量控制、熔断降级)

    Q1 Sentinel 核心能力

    限流、熔断降级、热点参数限流、系统保护、流量控制;支持 web、feign、gateway。

    • 限流:QPS / 线程数限制,保护系统不被打垮。
    • 熔断降级:下游异常比例、慢调用比例达到阈值,短时间拒绝调用,快速失败,防止雪崩。
    • 流量整形:WarmUp 预热,匀速排队。

    Q2 Sentinel 工作机制

  • 资源埋点;统计 QPS、异常数、耗时;滑动窗口做统计。
  • 规则:流控规则、降级规则、热点规则、系统规则。
  • Sentinel‑Dashboard 控制台;规则可以持久化到 Nacos 配置中心。
  • Q3 熔断和限流区别

    • 限流:限制流入流量,保护自身。
    • 熔断:下游故障,断开调用,保护调用方,防止雪崩。

    Q4 Sentinel 和 Hystrix 对比

    Hystrix 停止维护;Sentinel 功能更强,滑动窗口,支持网关限流,规则持久化 Nacos;轻量。

    Q5 可观测

    引入sentinel‑micrometer输出sentinel_*指标到 Prometheus;可以看到 block、degrade、pass 计数。


    五、Seata‑AT 分布式事务

    Q1 Seata AT 模式工作原理(重点)

    AT 模式:无侵入,基于本地事务 + undo_log 回滚日志表。 角色:

  • TC 事务协调器(Seata‑Server):全局事务管理者,记录全局事务状态。
  • TM 事务管理器:业务服务,开启 / 提交 / 回滚全局事务。
  • RM 资源管理器:每个数据库分支,注册分支事务。
  • 执行流程:

  • TM 向 TC 开启全局事务,生成 XID。XID 通过请求链路透传到所有微服务。
  • RM 执行业务 SQL,解析 SQL 生成 undo_log 快照,执行业务 commit,本地事务提交;向 TC 注册分支。
  • 所有分支执行成功:TM 通知 TC 全局提交;TC 通知所有 RM 删除 undo_log。
  • 任意分支失败:TC 通知所有 RM 回滚;RM 读取 undo_log 快照反向补偿回滚数据。
  • undo_log 表:每一个业务库都需要创建。

    Q2 AT 模式优缺点

    ✅业务代码无侵入; ❌只支持关系型数据库;不支持 DDL;大事务要避免长锁;undo_log 会有性能损耗。

    Q3 Seata XID 怎么跨服务传递

    Feign 拦截器自动把 XID 放到 http header 下游传递;RestTemplate 同样自动传递。

    Q4 Seata 可观测

    seata‑micrometer 输出全局事务、分支事务成功失败指标给 Prometheus。

    Q5 Seata 常见问题

  • XID 丢失:不要丢弃 feign header;
  • undo_log 没有生成:数据库代理没生效;
  • 大事务:事务占用时间长,锁占用,尽量拆分。

  • 六、可观测三大支柱:Metrics + Tracing + Logging

    完整可观测三支柱:指标、链路追踪、日志,通过 traceId 关联三者。

    6.1 Micrometer‑Metrics(Prometheus+Grafana)

    Q1 Micrometer 是什么

    指标门面框架,类似 SLF4J;统一 API 采集指标,可以对接 Prometheus、InfluxDB 等多种监控系统。SpringBoot3 Actuator 底层基于 Micrometer。

    Q2 四种 Meter 类型
  • Counter:计数器,只增不减;请求数、错误数;必须 rate () 函数计算每秒速率。
  • Timer:计时器;统计调用次数、耗时,直方图,计算 P50/P95/P99。
  • Gauge:瞬时值;内存、队列大小,可涨可跌。
  • DistributionSummary:大小分布统计。
  • Q3 Prometheus(时序数据库)
  • Pull 模式:Prometheus 主动拉取各个服务 /actuator/prometheus。
  • PromQL 查询语言;rate、sum by、histogram_quantile。
  • 不要高基数 tag(userId、orderId),会造成时序爆炸。
  • 生产使用 prometheus‑nacos‑sd 自动发现微服务 targets,不要静态写死地址。
  • Alertmanager 处理告警,推送钉钉 / 企业微信。
  • Q4 Grafana

    可视化面板;导入现成 dashboard 模板;展示大盘,配置告警。

    6.2 Micrometer‑Tracing 分布式链路追踪

    Q1 Sleuth 去哪了?

    SpringBoot3 废弃 Sleuth;替代方案 Micrometer‑Tracing;底层 Brave 实现,上报 Zipkin/Jaeger。

    Q2 TraceId / SpanId 概念
    • TraceId:一次完整请求全局唯一 ID,整条调用链透传。
    • Span:调用链中每一段;一个 Trace 包含多个 Span。
    • traceparent http header 完成跨服务链路透传。
    Q3 采样率 sampling.probability

    开发环境:1.0 全采样;生产环境 0.1‑0.2;100% 采样会产生海量 span,存储压力巨大。

    Q4 自动埋点

    MVC、Feign、RestTemplate、Gateway、JDBC 数据库;自动生成 span。业务可以手动自定义 Span。

    Q5 Zipkin

    接收上报 span,存储调用链;按 TraceId 查询调用链路,定位慢调用、超时。

    6.3 Logging 日志

    日志 pattern 打印 traceId、spanId;日志系统(Loki/ELK)按 traceId 检索日志。

    Q6 三者如何联动

  • Grafana 看到大盘错误率上涨 → 拿一条异常请求的 traceId。
  • Zipkin 根据 traceId 看完整调用链,定位慢节点、哪个服务报错。
  • Loki/ELK 搜 traceId 拿到完整堆栈日志,定位根因。

  • 高频综合面试题

    Q1 微服务完整请求链路(背熟)

    前端请求 → SpringCloud Gateway
    1. Gateway断言匹配路由,过滤器执行
    2. Gateway Micrometer‑Tracing生成第一个span,traceId创建
    3. Gateway通过LoadBalancer从Nacos选order‑service实例
    → HTTP调用 order‑service
    order‑service:
    1. MVC接收请求;micrometer埋点;traceId透传进来
    2. 业务逻辑;@GlobalTransactional开启Seata全局事务(XID生成)
    3. OpenFeign调用storage‑service
    4. Feign拦截器:LoadBalancer选择storage‑service实例;透传traceId和Seata XID
    → storage‑service执行业务,RM完成undo_log记录
    全部成功:Seata TC通知所有RM提交删除undo_log。

    监控侧:
    各个服务暴露 /actuator/prometheus;Prometheus拉取metrics;Grafana看QPS、JVM、错误率。
    Span上报Zipkin,可以查看完整调用链。
    日志输出traceId、XID,日志系统检索。

    Q2 微服务雪崩怎么防护

  • Sentinel 熔断降级,下游故障快速失败。
  • 限流保护入口流量。
  • 超时设置,避免线程阻塞。
  • 线程池隔离。
  • 重试要控制次数,接口幂等。
  • Q3 分布式事务方案选型

  • Seata‑AT:无侵入,关系数据库优先;
  • TCC:性能好,业务侵入高;
  • SAGA:长事务,补偿模式;
  • 本地消息表、可靠消息最终一致性(RocketMQ)。
  • Q4 客户端负载均衡流程

  • Feign 调用写服务名;
  • LoadBalancerClient 去 Nacos 获取服务实例列表;
  • 根据策略(轮询 / Nacos 权重)选出实例;
  • 替换服务名为真实 host:port 发起 http 调用;
  • 失败可开启重试切换实例。
  • Q5 Pull 模式 vs Push 模式 (Prometheus)

    • Pull:Prometheus 主动拉取 metrics 接口;微服务只暴露端点;适合微服务;缺点实例防火墙要打通。
    • Push:业务推数据到 PushGateway;适合短生命周期任务(批处理 job)。

    Q6 Counter 为什么必须 rate ()

    Counter 是只增不减;rate 计算一段时间窗口每秒增量;直接拿原始值没有意义。

    Q7 什么是高基数 Tag,危害

    tag 取值数量巨大(userId orderId),产生海量时间序列;Prometheus 内存 CPU 暴涨。

    Q8 为什么 Tracing 生产不能 100% 采样

    每一条请求生成多条 span;流量大时存储、网络压力爆炸,所以按比例采样。

    Q9 你这套微服务监控告警怎么做

  • Micrometer 采集 JVM、http、业务、Sentinel、Seata 指标;Prometheus pull 拉取。
  • Grafana 做可视化大盘;Prometheus Alertmanager 配置告警规则,钉钉推送告警。
  • Micrometer‑Tracing 上报 Zipkin,定位慢调用。
  • Loki 收集日志,traceId 打通指标‑链路‑日志。
  • 微服务线上踩坑清单 + 故障排查思路

    技术栈:Nacos + Gateway + OpenFeign + LoadBalancer + Sentinel + Seata‑AT + Micrometer‑Metrics‑Tracing + Prometheus‑Grafana‑Zipkin‑Loki 面向 5‑8 年 Java 后端,线上问题定位流程、典型故障、根因、排查步骤、规避方案。

    通用故障排查总流程(标准思路,面试也可以直接讲)

    发生线上故障,按顺序排查,不要上来就看代码。

  • 看告警:Alertmanager/Grafana 告警,确认影响范围:哪些服务、哪些接口、错误率、延迟、是否大面积报错。
  • 看 Grafana 指标大盘
    • HTTP:QPS、错误率、P95/P99 耗时、吞吐量。
    • JVM:堆内存、GC 频率、GC 停顿、线程数、Tomcat 繁忙线程。
    • Sentinel:限流、熔断次数,是否触发流控降级。
    • Seata:全局事务成功 / 回滚 QPS。
  • 拿异常请求 TraceId,去 Zipkin 看调用链:定位慢节点、哪个服务 / 哪个阶段耗时高、哪里抛异常。
  • Loki/ELK 根据 traceId 检索完整日志,看异常堆栈、业务报错。
  • 看中间件状态:Nacos 实例是否正常、Seata‑Server 状态、数据库连接池、慢 SQL。
  • 本地复现 / 线程 dump、heap dump,定位根因。
  • 止损优先:降级、限流、切流量、重启实例,再定位根因。
  • 核心线索:traceId 打通指标、链路、日志三者。


    一、JVM 相关故障

    1. 频繁 FullGC / OOM 内存溢出

    现象

    • Grafana:堆内存持续上涨,频繁 FullGC,GC 停顿时间变长;服务响应变慢,偶发超时,最终 OOM 宕机。
    • PromQL:

    jvm_memory_used_bytes{area="heap"} / jvm_memory_max_bytes{area="heap"}
    rate(jvm_gc_pause_seconds_count[1m])

    排查步骤

  • 看 Grafana 堆内存曲线,是缓慢泄漏还是瞬间打满。
  • 发生 OOM 时开启 ‑XX:+HeapDumpOnOutOfMemoryError 生成堆 dump 文件,MAT 分析。
  • 检查:集合未释放、线程池线程持有大对象、第三方库内存泄漏、大对象请求。
  • 查看线程 dump,看是否有大量阻塞线程。
  • 规避

    • 设置合理堆大小;生产开启 dump;监控堆使用率告警阈值 85% 告警。
    • 避免内存泄漏;大业务对象及时释放。

    2. GC 停顿过长,请求超时

    现象:JVM GC STW 停顿大,接口随机超时,业务无报错,链路 Zipkin 看到大量 spans 被卡住。 根因:堆太大、老年代碎片、大对象分配。 规避:合理堆大小;优先 G1/ZGC;监控 GC 停顿时间告警。

    3. 线程数暴涨,Tomcat 线程耗尽

    现象:tomcat 繁忙线程打满,新请求排队,接口大量超时。 PromQL:

    tomcat_threads_busy_threads / tomcat_threads_config_max_threads

    根因:Feign 调用没有设置超时,下游慢,线程卡住;锁等待;数据库慢 SQL 连接占住线程。 排查:jstack 线程 dump,看大量 http 线程卡在什么堆栈。 规避:Feign、数据库必须设置超时;Sentinel 线程数限流;线程池监控告警。


    二、Nacos 相关踩坑

    1. 服务实例上下线延迟

    现象:服务已经关闭,但 Nacos 短时间还返回旧实例;LoadBalancer 继续调用已下线实例,出现连接异常。 根因:Nacos 客户端实例缓存;nacos‑sd 刷新间隔。 排查:Prometheus Targets 查看实例状态。 规避:

    • 服务优雅停机,主动注销实例。
    • 配合 LoadBalancer 重试机制;做好异常容错。

    2. Nacos 配置不生效,@RefreshScope 失效

    现象:Nacos 修改配置,程序没有拿到新值。 根因:

  • 忘记加@RefreshScope;
  • Bean 被@Conditional提前初始化;
  • bootstrap 配置没有加载;SpringBoot3 需要引入spring‑cloud‑starter‑bootstrap。
  • 3. 权重灰度不生效

    现象:Nacos 控制台修改实例权重,流量没有按权重分配。 根因:没有开启 spring.cloud.nacos.discovery.weight=true;LoadBalancer 默认轮询不会读取权重。

    4. Nacos 长轮询超时警告

    现象:日志大量长轮询 timeout 警告,不代表功能异常,网络问题会加剧。 规避:内网部署 Nacos;调大长轮询超时时间。


    三、OpenFeign + LoadBalancer 故障

    1. Feign 随机超时

    现象:部分请求超时,不是全部超时。 排查:

  • Zipkin 看 span 耗时,确认是网络还是下游服务慢。
  • Feign connectTimeout/readTimeout 配置是否足够;LoadBalancer 不负责超时。
  • 下游 JVM GC、慢 SQL 导致响应慢。
  • 2. LoadBalancer 重试造成重复数据(高频坑!)

    现象:开启重试后,数据库出现重复订单、重复扣库存。 根因:POST 非幂等接口失败触发重试。

    重试只适合 GET 幂等接口;POST 接口尽量关闭重试;如果一定要重试,业务必须保证幂等。

    3. @LoadBalanced 漏加,RestTemplate 报错 unknown‑host

    RestTemplate 调用服务名,解析不了。 解决:加上@LoadBalanced。

    4. Feign 丢失 Header(traceId / XID 丢失)

    现象:链路 traceId 断链,Seata XID 丢失,分布式事务失效。 根因:自定义拦截器清空 / 覆盖 http header;部分过滤器丢弃 header。 排查:Zipkin 查看 traceId 是否传递到下游;日志看 traceId 是否为空。 规避:不要粗暴清空 header;micrometer‑tracing、seata 靠 header 透传上下文。

    5. Feign 调用报错 No instance available

    Nacos 没有该服务实例;实例全部 DOWN;服务名写错;namespace 不一致。


    四、Sentinel 线上踩坑

    1. 规则不生效

  • Dashboard 配置规则,没有持久化 Nacos;服务重启规则丢失。生产必须把规则持久化 Nacos 配置中心。
  • 资源名不匹配;网关维度和业务服务维度规则混淆。
  • 2. 熔断不触发

    现象:下游大量报错,Sentinel 没有熔断。 根因:统计窗口时间设置过大;异常统计只统计业务异常,不统计超时;慢调用比例阈值配置错误。

    3. Sentinel 误限流

    现象:正常流量被限流。 根因:QPS 阈值设置过小;机器单机阈值,多实例要算总流量;注意是单机 QPS。

    Sentinel 流控规则是单机实例 QPS,不是全集群总 QPS。

    4. sentinel‑micrometer 指标没有数据

    确认依赖引入;资源有实际触发限流 / 熔断才会产生 metrics。


    五、Seata‑AT 分布式事务高频坑(非常容易出线上问题)

    1. XID 丢失,分布式事务失效

    现象:@GlobalTransactional,但是分支事务没有回滚。 根因:Feign 请求 header 丢失 XID;异步线程丢失上下文;新线程不会自动继承 XID。

    注意:新起线程池线程,Seata XID 不会自动传递,需要手动传递上下文。

    2. undo_log 没有生成,回滚失效

  • 数据库没有创建 undo_log 表。
  • seata 代理数据源配置错误;没有代理真实 DataSource。
  • DDL 语句不会生成 undo_log,AT 不支持 DDL 回滚。
  • 3. 大事务问题

    现象:全局事务执行时间很长;数据库锁占用;undo_log 堆积。 根因:@GlobalTransactional 包裹过多业务逻辑,包含远程调用,事务时间拉长。 规避:尽量缩小全局事务范围;远程调用放到事务外面。

    4. 事务悬挂、空回滚

    AT 模式经典问题;新版本 Seata 已经修复;注意使用稳定版本。

    5. Seata‑Server 宕机

    TM/RM 会缓存事务状态;短时间 Seata‑Server 不可用,已开启事务还能继续执行;新事务无法创建。


    六、可观测体系(Micrometer / Prometheus / Zipkin)踩坑

    1. Prometheus target 显示 DOWN

    排查清单:

  • 防火墙 / 网络不通;prometheus 不能访问微服务端口。
  • management.endpoints.web.exposure.include 没有暴露prometheus端点。
  • 微服务实例挂掉。
  • 2. Grafana 图表无数据

  • PromQL 写错:Counter 忘记加rate()。
  • tag 标签不匹配:application标签不对。
  • 采样率:Tracing 采样率过低,Zipkin 看不到链路。
  • Timer 没有配置publishPercentiles,P95/P99 无数据。
  • 3. 高基数 Tag,Prometheus CPU / 内存暴涨

    禁止把 userId、orderId 作为 micrometer tag。 现象:prometheus 内存持续上涨,查询变慢。 根因:tag 取值无限多,时间序列爆炸。

    4. Zipkin 看不到完整链路,traceId 断链

  • 缺少 micrometer‑tracing‑brave 依赖。
  • 采样率设置为 0;开发环境设置 1.0。
  • WebFilter / 自定义拦截器把traceparent header 丢弃。
  • Gateway WebFlux 环境依赖不全。
  • 5. 日志看不到 traceId

    logback pattern 没有配置%X{traceId} %X{spanId}。

    6. Prometheus 存储爆满

    默认保留 15 天;长期监控用 Thanos/Mimir 做远端存储;配置数据保留时间。

    7. actuator 端点对外暴露

    生产 actuator 不要暴露公网;内网访问;增加 basic auth 安全认证。


    七、Gateway 网关踩坑

  • Gateway WebFlux 环境,部分同步拦截器上下文丢失;traceId 透传异常。
  • Gateway Sentinel 网关限流,区分路由维度和 API 分组。
  • Gateway OOM:大请求体,未限制请求大小。

  • 八、典型线上故障场景模拟(面试经常问)

    场景 1:接口大量 504 超时

    排查步骤:

  • 看 Grafana 大盘:P99 耗时飙升;Tomcat 繁忙线程打满?GC 频繁?
  • 拿 traceId 查 Zipkin,看耗时消耗在哪个 span:网关?order‑service?storage‑service?数据库?Feign 调用下游?
  • 如果是数据库:查慢 SQL;连接池是否耗尽。
  • 如果下游服务慢:看下游 JVM 状态、GC、线程。
  • 止损:Sentinel 降级;扩容实例;优化慢 SQL。
  • 场景 2:分布式事务部分回滚,数据不一致

  • 看 Seata metrics:全局事务回滚 QPS。
  • Zipkin 看整条链路是否完整走完。
  • 日志看 XID 是否全链路传递;是否异步线程丢失 XID。
  • 检查 undo_log 表是否存在;数据源代理是否正确。
  • 查看 TC 事务状态表,手工补偿数据。
  • 场景 3:流量突增系统雪崩

  • 告警:错误率飙升,线程打满。
  • 看 Sentinel 指标:是否触发限流熔断。
  • 止损:开启限流降级;扩容;非核心业务降级。
  • 事后优化:优化慢接口;扩容;压测评估容量。
  • 场景 4:偶发随机报错,很难复现

    大概率:网络抖动;实例上下线;重试;GC 停顿;Nacos 缓存延迟。 优先依赖 traceId 看 Zipkin 和日志,复现很难,链路日志是关键。


    九、线上故障排查工具清单(实际工作使用)

  • JVM:jstack(线程 dump)、jmap、MAT 分析 heap dump;jstat 看 GC。
  • Prometheus+Grafana:指标大盘、告警。
  • Zipkin:traceId 查调用链、慢调用。
  • Loki/ELK:traceId 检索日志。
  • Nacos 控制台:查看服务实例、配置。
  • Seata‑Server 控制台:查看全局事务状态。
  • Sentinel‑Dashboard:查看限流熔断情况。

  • 十、生产环境检查清单(上线前核对)

    ✅ Nacos 实例配置权重、namespace、group 正确;配置持久化。 ✅ Feign/RestTemplate 全部设置合理超时;POST 接口关闭 LoadBalancer 重试。 ✅ Sentinel 规则持久化 Nacos;配置限流熔断策略。 ✅ Seata AT undo_log 表创建;数据源代理配置正确;异步线程 XID 传递处理。 ✅ Micrometer 暴露 prometheus 端点;全局 application tag;禁止高基数 tag。 ✅ Prometheus‑Nacos‑SD 自动发现实例,不写死 target。 ✅ 告警规则配置:实例宕机、错误率、JVM 内存、GC 繁忙。 ✅ Micrometer‑Tracing 采样率生产 0.1‑0.2;开发 1.0;日志输出 traceId spanId。 ✅ actuator 端点内网访问,不对外暴露。 ✅ JVM OOM dump 开启。 ✅ 服务优雅停机配置。

    基础架构类

    Q1:介绍下你项目用的微服务技术栈,整体架构是怎样的?

    答: 我们采用 Spring Cloud 微服务体系。

    • 注册 & 配置中心:Nacos,服务注册发现、配置动态管理,支持 namespace/group 做环境隔离,支持权重实现灰度发布。
    • 网关:Spring Cloud Gateway,基于 WebFlux 响应式,作为统一入口,负责路由转发、鉴权、限流、跨域。
    • 远程调用:OpenFeign 做声明式 HTTP 调用,负载均衡使用 Spring‑Cloud‑LoadBalancer,替代旧的 Ribbon。
    • 容错防护:Sentinel 做流量防护,限流、熔断降级、热点参数限流,规则持久化到 Nacos,防止服务雪崩。
    • 分布式事务:Seata AT 模式,业务无侵入解决微服务之间数据一致性问题。
    • 可观测体系三支柱
    • Metrics 指标:Micrometer 采集指标,Prometheus Pull 模式拉取 /actuator/prometheus,Grafana 做大盘可视化,Alertmanager 实现告警推送。
    • Tracing 链路追踪:SpringBoot3 废弃 Sleuth,使用 Micrometer‑Tracing,底层 Brave 实现,Span 上报 Zipkin,全链路 TraceId 透传定位慢调用。
    • Logging 日志:日志打印 traceId、spanId,Loki 做日志聚合,三者依靠 traceId 关联排查问题。

    请求流程:前端请求打到 Gateway 网关 → Gateway 路由转发,LoadBalancer 从 Nacos 选取实例 → Feign 调用业务微服务;服务之间 Seata XID、Tracing traceId 自动通过 Http Header 透传。


    Q2:Spring‑Cloud‑LoadBalancer 和 Ribbon 区别,为什么不用 Ribbon?

    答: Ribbon 已经停止维护,SpringCloud2020 版本之后移除 Ribbon,官方替换为 Spring‑Cloud‑LoadBalancer。

  • LoadBalancer 是客户端负载均衡,内置轮询、随机策略;本身不支持权重,权重能力由 Nacos‑Discovery 扩展实现,需要手动开启配置。
  • RestTemplate 加上@LoadBalanced注解,增加拦截器,把服务名解析为真实实例地址。
  • 支持重试机制,POST 非幂等接口一定要关闭重试,否则会产生重复数据,重试更适合 GET 幂等接口。
  • Feign 底层默认集成 LoadBalancer 做实例选择。
  • 客户端负载均衡:消费端本地拿到实例列表,本地算法选实例直连下游;对比 Nginx 服务端负载均衡。

    Q3:OpenFeign 的工作流程,会遇到哪些坑?

    答:

  • 启动时扫描@FeignClient注解,生成动态代理对象。
  • 调用接口方法,组装 HTTP 请求。
  • LoadBalancer 根据服务名从 Nacos 获取实例列表,选择可用实例。
  • 构造 HTTP 请求调用下游服务,解析响应返回。
  • 常见坑:

  • 超时不是 LoadBalancer 控制,是 Feign http client 配置 connectTimeout、readTimeout。
  • 自定义拦截器不要随意清空 Header,会丢失 traceId 和 Seata XID,造成链路断链、分布式事务失效。
  • 开启重试要注意幂等性。
  • No instance available:服务名错误、namespace 不一致、实例全部 down。
  • Q4:Gateway 和 Zuul 对比,Gateway 核心组件?

    答: Zuul1 基于 Servlet 阻塞 IO,性能差,已经停止维护;Zuul2 没有整合进 SpringCloud。 Gateway 基于 WebFlux 非阻塞响应式,性能更高,Spring 官方网关。 三大核心组件:

  • Route:路由,包含 id、目标 uri、断言、过滤器。
  • Predicate 断言:匹配请求路径、header、参数,匹配成功执行对应路由。
  • Filter 过滤器:pre 前置、post 后置,可以修改请求响应。 Gateway 也可以接入 Sentinel 实现网关层面限流;同时支持 micrometer metrics 和 tracing 埋点。

  • 容错、雪崩、限流熔断

    Q5:什么是服务雪崩?怎么防护?

    答: 雪崩:一个下游服务故障,调用方大量请求堆积,线程耗尽,拖垮调用方,故障层层扩散,整个系统瘫痪。 防护手段:

  • 熔断降级(Sentinel):下游异常比例、慢调用比例达到阈值,熔断,快速失败,不再调用故障下游。
  • 限流:入口网关、业务接口限流,保护系统不被大流量打垮。
  • 设置合理超时时间,避免线程无限阻塞。
  • 线程隔离,故障不扩散。
  • 谨慎使用重试,接口必须保证幂等;
  • 扩容、流量灰度。
  • Q6:Sentinel 限流和熔断降级区别?Sentinel 对比 Hystrix?

    答:

    • 限流:限制流入本资源的 QPS / 线程数,保护自己,多余请求直接拒绝。Sentinel 是单机实例 QPS,不是集群总 QPS。
    • 熔断降级:发现下游服务故障,主动断开调用,快速返回,保护调用方,防止雪崩。

    Hystrix 已经停止维护;Sentinel 功能更强:滑动窗口统计、支持网关限流、热点参数限流、规则可以持久化 Nacos,轻量易用。

    注意:Sentinel 控制台配置规则重启丢失,生产必须持久化到 Nacos 配置中心。

    Q7:Sentinel 滑动窗口原理简单说下

    答: Sentinel 用滑动时间窗口做统计,把时间切分成多个小桶,每个桶统计 QPS、异常数、慢调用数量。时间滑动,淘汰过期桶,实时统计指标,用来判断是否触发限流或者熔断。


    分布式事务 Seata‑AT

    Q8:讲下 Seata AT 模式原理,三个角色,执行流程

    答: 三个角色:

  • TC:事务协调器,Seata‑Server;全局事务状态管理者。
  • TM:事务管理器,业务服务,负责开启、提交、回滚全局事务。
  • RM:资源管理器,每个数据库分支,负责分支事务注册、undo_log 生成、补偿回滚。
  • 执行流程:

  • TM 向 TC 开启全局事务,生成全局唯一 XID;XID 通过 Feign 请求 Header 透传给所有微服务。
  • RM 执行业务 SQL,解析 SQL 生成 undo_log 回滚快照,本地事务提交,业务数据落库,向 TC 注册分支事务。
  • 如果全部分支执行成功:TM 通知 TC 全局提交;TC 通知 RM 删除 undo_log,不需要回滚。
  • 如果任意分支失败:TC 通知各个 RM 回滚;RM 读取 undo_log 快照反向补偿,回滚业务数据。
  • AT 优点:业务代码无侵入;缺点:只支持关系型数据库,不支持 DDL;大事务会带来锁与性能损耗。

    每个业务库都需要手动创建 undo_log 表。

    Q9:Seata‑AT 线上常见问题?

    答:

  • XID 丢失,事务不回滚:Feign 拦截器丢失 header;异步新建线程,线程池不会自动传递 XID,需要手动传递上下文。
  • undo_log 不生成:没有创建 undo_log 表、数据源没有被 Seata 代理;DDL 语句不会生成 undo_log,AT 不支持 DDL 回滚。
  • 大事务:@GlobalTransactional包裹远程调用,事务时间拉长,占用数据库锁,尽量缩小全局事务范围。
  • Seata‑Server 宕机:已经开启的事务还能继续执行;新的全局事务无法创建。
  • Q10:分布式事务有哪些方案,怎么选型?

    答:

  • Seata‑AT:无侵入,关系数据库优先,大多数业务首选;
  • TCC:性能高,但是业务侵入大,需要手写 try‑confirm‑cancel;
  • SAGA:长事务场景,基于补偿;
  • 可靠消息最终一致性(RocketMQ 本地消息表):适合异步场景。

  • 可观测体系 Metrics & Tracing

    Q11:Micrometer 是什么?Metrics 四种 Meter 类型?

    答: Micrometer 相当于监控领域的 SLF4J,统一指标 API 门面,可以对接 Prometheus、InfluxDB 多种监控系统;SpringBoot3 Actuator 底层基于 Micrometer。 四种 Meter:

  • Counter 计数器:只增不减,请求数、异常数;Prometheus 使用必须搭配rate()。
  • Timer 计时器:统计调用次数、耗时,生成直方图,计算 P95/P99 百分位耗时。
  • Gauge 仪表盘:瞬时值,可涨可跌,比如堆内存、线程数、队列长度。
  • DistributionSummary:数值大小分布统计。
  • Q12:Prometheus Pull 模式和 Push 模式区别?

    答:

    • Pull 模式:Prometheus 主动拉取服务暴露的/actuator/prometheus端点;微服务不需要感知 Prometheus;适合常驻微服务;需要网络打通。
    • Push 模式:业务主动推送指标到 PushGateway;适合短时批处理 Job 任务;不适合常驻微服务。

    生产不要手写静态 target,使用 prometheus‑nacos‑sd 从 Nacos 自动发现服务实例。

    Q13:什么是高基数 Tag,有什么危害?

    答: tag 取值无限,比如 userId、orderId 当做 tag;每一个不同值生成一条时间序列。 会造成 Prometheus 时序爆炸,内存 CPU 暴涨,查询缓慢。业务严禁把业务唯一 ID 设置为 micrometer tag。

    Q14:SpringBoot3 链路追踪,Sleuth 去哪里了?TraceId、SpanId 概念?

    答: SpringBoot3 已经废弃 Spring Cloud Sleuth,替代方案是 Micrometer‑Tracing,底层 Brave 实现,上报 Zipkin/Jaeger。

    • TraceId:一次完整请求全局唯一 ID,整条调用链透传。
    • SpanId:调用链中每一段单元,一个 Trace 包含多个 Span。 通过 http header traceparent完成跨服务透传。

    开发采样率 1.0 全采样;生产设置 0.1‑0.2;100% 采样会产生海量 span,存储压力巨大。

    Q15:指标、链路、日志三者怎么联动排查问题?

    答: 三者依靠traceId串联:

  • Grafana 大盘看到接口错误率上涨、P99 耗时升高;
  • 拿到异常请求的 traceId,去 Zipkin 查看完整调用链,定位慢节点、报错服务;
  • 使用 Loki/ELK 检索 traceId,拿到完整业务堆栈日志,定位根因。
  • Q16:Zipkin 看不到链路,可能的原因?

    答:

  • micrometer‑tracing 相关依赖缺失;
  • 采样率设置为 0;
  • 自定义 Filter 拦截器丢弃了traceparent请求头;
  • Zipkin 上报 endpoint 配置错误,网络不通;
  • Gateway WebFlux 场景依赖不全。

  • 故障排查类(高频)

    Q17:线上接口大量 504 超时,你的排查思路?

    答:

  • 先看告警 Grafana 大盘:查看 QPS、错误率、P99 耗时;看 tomcat 繁忙线程是否打满;JVM 是否频繁 GC。
  • 取异常请求 traceId,去 Zipkin 看调用链,定位耗时发生在哪一段:网关?业务服务?Feign 调用下游?数据库?
  • 如果是数据库慢,查看慢 SQL、连接池状态;如果下游慢,检查下游 JVM GC、线程、CPU。
  • 优先止损:降级、限流、扩容;再定位根因。
  • Q18:偶发数据不一致,Seata 分布式事务部分回滚,排查思路?

    答:

  • 查看 Seata metrics,看全局事务回滚 QPS。
  • Zipkin 看整条调用链路是否完整;日志检查 XID 是否全链路传递。
  • 排查是否异步线程丢失 XID 上下文。
  • 确认 undo_log 表是否存在,数据源是否被 Seata 代理。
  • 查询 Seata‑Server 事务状态表,必要时手工补偿数据。
  • Q19:线上故障通用排查流程

    答:

  • 确认告警,评估故障影响范围。
  • Grafana 看指标大盘:HTTP、JVM、Sentinel、Seata 指标。
  • 获取 traceId,Zipkin 看调用链定位慢节点。
  • Loki/ELK 根据 traceId 检索完整日志看异常堆栈。
  • 检查中间件状态:Nacos 实例、数据库、连接池。
  • 必要时 jstack 线程 dump、heap dump 分析。
  • 先止损,后定位根因。
  • Q20:线上生产上线前检查哪些关键点?

    答:

  • Nacos namespace、group、权重配置;配置持久化。
  • Feign 设置超时,POST 接口关闭 LoadBalancer 重试,注意幂等。
  • Sentinel 规则持久化 Nacos,配置限流熔断。
  • Seata undo_log 表创建,数据源代理;异步线程 XID 传递处理。
  • Micrometer 禁止高基数 tag;prometheus 端点暴露,内网访问。
  • Prometheus‑nacos‑sd 自动发现实例,不写死 target。
  • 配置告警:实例宕机、错误率、JVM 内存、GC 告警。
  • Tracing 采样率生产 0.1‑0.2;日志输出 traceId、spanId。
  • JVM 开启 OOM dump;服务优雅停机配置。
  • 如何保证微服务架构高可用

    高可用核心目标:减少故障时间,单个组件故障不扩散,故障可快速止损,尽量保证业务可用。 分为:基础设施层、网关层、服务治理层、业务代码层、数据层、可观测与运维层,结合你这套栈 (Nacos/Gateway/Sentinel/Seata/Prometheus‑Zipkin)。

    1. 基础设施 & 部署层面

  • 多实例部署,避免单点 每个微服务部署≥2 个实例;不要单实例运行。实例分散到不同机器 / 可用区,避免单机故障整个服务不可用。
  • 注册中心高可用 Nacos 集群 Nacos 不能单机,部署 Nacos 集群;数据库使用 MySQL 主从。防止注册中心单点故障导致服务发现瘫痪。
  • 中间件集群化 Seata‑Server 集群、数据库主从、MQ 集群;核心中间件杜绝单机。
  • 优雅停机 + 健康检查 服务关闭前主动向 Nacos 注销实例;K8s 健康检查,异常实例自动剔除;避免流量还打到已经关闭的实例。
  • 灰度 / 滚动发布 滚动更新,不要一次性全量重启所有实例;Nacos 权重灰度,小流量验证新版本,出现问题快速切回旧版本。
  • 2. 网关层高可用(Spring Cloud Gateway)

  • 网关本身集群部署,前面架设 Nginx 做负载均衡,网关不能单点。
  • 网关层限流、鉴权,把恶意流量挡在入口,保护后端微服务。
  • 网关配置降级兜底响应,后端服务故障返回友好提示。
  • 网关开启可观测:指标 + 链路追踪,及时发现网关侧异常。
  • 3. 服务调用与容错治理(Sentinel + LoadBalancer)

    解决服务雪崩,这是微服务高可用最核心部分。

  • 超时控制 Feign/RestTemplate 必须设置合理 connectTimeout、readTimeout;不允许无限等待,防止线程被慢下游占满。
  • 熔断降级 Sentinel
    • 下游服务异常比例、慢调用比例到达阈值,触发熔断,快速失败,不再调用故障服务;
    • 非核心业务降级返回兜底数据,牺牲非核心保证核心链路可用。
  • 限流 网关限流、服务接口限流;单机 QPS 阈值,防止流量突增打垮服务。支持预热、排队。
  • 重试机制谨慎使用 LoadBalancer 重试只用于GET 幂等接口;POST 业务接口关闭重试,避免重复数据;重试次数不能过大。
  • 负载均衡 多实例流量分摊;Nacos 权重实现流量分配;自动过滤掉不健康实例。
  • 隔离 Sentinel 线程池隔离,不同业务资源互相隔离;一个慢接口不会耗尽整个服务线程池。
  • 4. 业务代码层面保障

  • 接口幂等设计 新增、扣款等接口,使用唯一业务流水号做幂等;应对重试、重复请求带来脏数据。
  • 异步解耦 非强实时逻辑使用 MQ 异步处理,不要同步阻塞主链路;主流程尽量精简。
  • 避免大事务 Seata‑AT 不要把远程 Feign 调用包裹在全局事务中;大事务会持有数据库锁,引发数据库阻塞,降低可用性。缩小事务范围。
  • 异步线程上下文传递 线程池新开线程手动传递 XID、traceId;否则分布式事务、链路追踪失效,引发隐性数据故障。
  • 资源保护 线程池、数据库连接池设置合理上限,防止资源耗尽。
  • 5. 数据层高可用

  • 数据库主从架构,读写分离;主库故障切换从库。
  • 分库分表,避免单库压力过大。
  • 分布式事务合理选型:
    • 强一致性:Seata‑AT/TCC;
    • 允许最终一致性:MQ 可靠消息,降低锁开销,提升可用性。
  • 缓存保护数据库:Redis 缓存热点数据,减轻 DB 压力;做好缓存穿透、击穿、雪崩防护。
  • 数据备份,定时备份,故障可以恢复。
  • 6. 可观测:监控、告警、故障快速发现(Micrometer+Prometheus+Grafana+Zipkin)

    高可用前提:故障要尽早发现。

  • 指标 Metrics:监控实例存活、QPS、错误率、P95/P99 耗时、JVM 内存、GC、线程池、Sentinel 限流熔断计数;配置告警,异常钉钉推送。
  • 链路 Tracing:Micrometer‑Tracing+Zipkin,定位慢调用、跨服务故障点。
  • 日志 Logging:Loki/ELK 收集日志,携带 traceId,快速检索异常堆栈。
  • 告警覆盖:实例宕机、错误率飙升、JVM 内存过高、GC 频繁、数据库慢查询。
  • 故障流程:先止损,再排查根因。降级、限流、切流量优先恢复业务。
  • 7. 运维保障

  • 压力测试:压测评估系统容量,提前发现性能瓶颈。
  • 故障演练:模拟实例宕机、中间件故障,验证降级熔断是否生效。
  • 回滚方案:版本发布要有快速回滚手段。
  • 容灾:多机房 / 多可用区部署,应对机房级故障。

  • 面试精简背诵版(直接口述)

    保证微服务高可用,我会从基础设施、网关、服务容错、业务代码、数据层、可观测运维六个维度做保障:

  • 基础设施:消除单点 所有微服务、Nacos、Seata‑Server 等中间件都集群多实例部署,跨节点部署;滚动 / 灰度发布;服务优雅停机,不健康实例自动摘除。
  • 网关层:流量入口防护 Gateway 集群部署,网关做统一限流,恶意流量拦截,后端故障支持降级兜底。
  • 服务调用容错,防止雪崩 使用 Sentinel 实现限流、熔断降级、线程隔离;Feign 设置合理超时;谨慎使用重试,非幂等接口关闭重试;LoadBalancer 多实例负载均衡。
  • 业务代码层面 接口做好幂等;非核心逻辑 MQ 异步解耦;避免大事务;线程池注意上下文传递;线程池、连接池资源做好限制。
  • 数据层高可用 数据库主从读写分离;缓存防穿透击穿雪崩;分布式事务合理选型;定时数据备份。
  • 可观测与运维 Micrometer 采集指标,Prometheus+Grafana 配置告警;Micrometer‑Tracing 做链路追踪;日志聚合;故障快速发现,先止损再定位根因;压测验证容量,版本支持快速回滚。
  • 核心思想:故障不可避免,目标是故障影响范围缩小,业务尽可能可用,故障快速感知快速恢复。

    微服务降级与熔断设计(面试背诵版)

    技术栈:Sentinel;区分熔断、降级概念;什么时候触发、策略、落地、坑点。

    一、概念区分(面试必问)

    熔断(Circuit‑Breaker)

    保护调用方,针对下游依赖故障。 当调用下游服务,异常比例 / 慢调用比例达到阈值,熔断器打开,暂时切断对下游的调用,直接走快速失败逻辑,避免大量请求堆积、线程耗尽,防止雪崩。 熔断器有状态:关闭 → 打开 → 半开探测。

    • 关闭:正常调用下游。
    • 打开:不调用下游,快速失败。
    • 半开:释放少量请求探测下游是否恢复;探测成功,熔断器关闭恢复正常调用。

    熔断是被动触发:下游出问题才熔断。

    降级(Fallback)

    牺牲非核心业务,保障核心链路可用。 主动关闭 / 弱化某些非核心功能,返回兜底数据、缓存数据或者提示,不调用真实逻辑。

    • 可以手动开关;也可以系统压力大、熔断触发时自动降级。
    • 例子:商品详情页,推荐、评论是非核心,降级后不展示;订单创建,短信通知异步降级。

    熔断是 “下游坏了我不调用它”;降级是 “我主动放弃一部分功能保核心”。

    简单记: 熔断防下游故障扩散;降级保核心业务存活。熔断是手段,降级往往是熔断之后的兜底行为。


    二、Sentinel 中熔断降级实现(实际项目怎么做)

    Sentinel 的降级规则就是熔断能力,支持三种熔断策略:

  • 慢调用比例:统计窗口内,耗时大于阈值的请求占比达到设定比例,触发熔断。适合下游响应变慢但不报异常的场景。
  • 异常比例:统计窗口内抛出异常的请求占比达到阈值,触发熔断。
  • 异常数:统计窗口内异常数量达到阈值,触发熔断。
  • 配置要点:

    • 滑动窗口时长;熔断时长(熔断打开多久);半开阶段会放行少量请求探测恢复。
    • 熔断触发后,执行自定义 fallback 兜底逻辑。

    生产规则不要写在内存,持久化到 Nacos 配置中心,服务重启规则不丢失;Sentinel‑Dashboard 只用来调试查看。

    降级策略分类

  • 返回兜底默认值:比如个人信息接口,头像昵称兜底返回空 / 默认值。
  • 返回缓存旧数据:读接口优先返回缓存,不调用下游。
  • 抛出友好业务异常:提示 “系统繁忙,请稍后重试”。
  • 直接丢弃非核心请求:如消息通知、埋点日志直接丢弃,不阻塞主流程。
  • 异步化:同步调用改为 MQ 异步,主流程快速返回。
  • 核心原则:降级只降非核心链路;核心链路不能降级。


    三、网关层面降级熔断(Spring Cloud Gateway + Sentinel)

    网关层可以做:

  • 路由维度熔断降级:某个后端服务整体故障,网关直接返回兜底,流量不转发到后端。
  • API 分组限流降级。
  • 防止故障流量全部打入后端服务。

  • 四、业务落地注意点(面试高频坑)

  • 区分读接口、写接口降级
    • 读接口:容易降级,可以返回缓存、默认值。
    • 写接口(下单、扣库存)不能随便降级,不能伪造成功,会造成数据错乱;写接口降级一般只能返回 “系统繁忙,请稍后”。
  • fallback 降级方法不要写重业务逻辑 fallback 本身要轻量,不能再发起复杂远程调用、数据库操作,防止 fallback 本身也故障。
  • 熔断≠永久断开 经过熔断时间之后进入半开状态,少量请求探测下游是否恢复;不要配置过长熔断时间。
  • 和超时配合 熔断要配合 Feign 超时配置;慢调用要先超时,才会被 Sentinel 统计为慢调用。如果没有超时设置,线程无限卡住,熔断不会生效。
  • 不要滥用重试 熔断打开之后不要再重试;重试会加重故障下游压力。
  • 可观测 开启 sentinel‑micrometer,Prometheus 统计熔断、降级触发次数;Grafana 配置告警:熔断次数突增要告警,说明下游服务出问题。

  • 五、面试标准简答(可以直接口述)

    Q:微服务中如何设计熔断和降级?

    答: 熔断主要用来保护调用方,防止下游服务故障引发雪崩;降级用来牺牲非核心业务,保障核心链路可用。

    我们项目使用 Sentinel 实现熔断降级。 熔断:配置降级规则,支持慢调用比例、异常比例、异常数三种策略。当下游调用达到阈值,熔断器打开,短时间停止调用下游服务;过了熔断时间进入半开状态,少量请求探测下游是否恢复。

    降级:熔断触发后执行 fallback 兜底逻辑。读接口可以返回缓存或者默认值;写接口一般返回系统繁忙提示,不能伪造业务成功。非核心功能可以主动降级,保障核心链路。

    落地注意:

  • 熔断规则持久化到 Nacos,避免重启丢失;
  • Feign 必须配置超时时间,否则慢调用无法被统计;
  • fallback 逻辑保持轻量,不要做复杂业务;写接口谨慎降级,防止数据错乱;
  • 网关层也可以做路由级别的熔断降级;
  • 通过监控观测熔断降级触发次数,发生大量熔断要告警,及时发现下游故障。
  • 熔断是防御手段,降级是兜底方案。


    六、延伸面试题

    Q:熔断和限流的区别?

    • 限流:限制流入系统的流量,保护自己,抵御外部大流量。
    • 熔断:下游依赖故障,保护自己不被拖垮,限制对外调用。
    • 降级:主动舍弃部分功能,保障核心业务。

    一句话:限流防流量;熔断防依赖故障;降级保核心。

    微服务架构中的服务治理(面试背诵版)

    结合你的技术栈:Nacos + Gateway + OpenFeign + LoadBalancer + Sentinel + Seata + Micrometer 可观测 服务治理本质:对微服务整个生命周期进行管控:服务注册发现、流量管控、容错、配置管理、可观测、灰度发布、安全、故障处理,解决微服务拆分之后带来的复杂度。

    服务治理分为七大模块:服务注册发现治理、配置治理、流量治理、容错治理、分布式事务治理、可观测治理、运维 & 发布治理。

    1. 服务注册与发现治理(Nacos)

  • 服务注册、心跳检测、实例健康管理 服务启动向 Nacos 注册;定时上报心跳;心跳超时剔除不健康临时实例;避免流量打到故障实例。
    • 区分临时实例(普通微服务)、持久实例(中间件)。
    • namespace、group 做环境、业务分组隔离,避免开发 / 测试 / 生产环境互相串调。
  • 客户端缓存机制 微服务本地缓存实例列表,减少对注册中心压力;存在实例上下线延迟,要配合容错机制处理旧实例调用报错。
  • 负载均衡与灰度流量 使用 Nacos 设置实例权重实现灰度发布;配合 Spring‑Cloud‑LoadBalancer 完成客户端负载均衡。
  • 注意:LoadBalancer 本身没有权重策略,需要 Nacos‑Discovery 扩展开启权重支持。

  • 实例元数据管理 给实例打元数据标签,区分版本、机房,用于路由筛选。
  • 2. 配置治理(Nacos Config)

  • 统一配置中心管理配置 DataId、Group、Namespace 隔离配置;配置集中管理,不用每个服务本地维护配置文件。
  • 动态配置刷新 @RefreshScope实现配置不重启动态生效;适用于开关、限流阈值、业务参数。
  • 配置版本、回滚 Nacos 保存配置历史版本,配置改错可以快速回滚。
  • 配置加密 敏感配置(数据库密码)加密存储,防止明文泄露。
  • 配置持久化 Sentinel 限流熔断规则持久化到 Nacos,而不是内存,服务重启规则不丢失。
  • 3. 流量治理(Gateway + Sentinel + LoadBalancer)

    流量治理是服务治理核心,管控服务之间调用流量。

  • 网关作为流量入口 Spring Cloud Gateway 统一入口,路由转发、请求过滤、鉴权、跨域;网关层面实现路由限流、API 分组限流。
  • 负载均衡 LoadBalancer 客户端负载均衡;轮询 / 随机;配合 Nacos 权重实现灰度。 合理控制重试:仅幂等接口开启重试;POST 写接口关闭重试,防止重复数据。
  • 限流
    • 网关层限流:拦截入口大流量。
    • 服务维度限流:Sentinel 单机 QPS、线程数限流;预热、匀速排队,防止突增流量打垮服务。

    Sentinel 是单机实例 QPS,不是集群总 QPS,多实例要计算整体容量。

  • 熔断降级 Sentinel 降级规则实现熔断:慢调用比例、异常比例、异常数触发熔断;熔断器关闭‑打开‑半开状态流转;触发熔断执行 fallback 兜底逻辑。 读接口返回缓存 / 默认值;写接口返回繁忙提示,不能伪造成功。牺牲非核心业务保障核心链路。
  • 线程隔离 Sentinel 线程池隔离,不同业务资源互相隔离;一个慢接口不会耗尽整个服务线程池。
  • 4. 容错治理

  • 超时控制 Feign/RestTemplate 必须设置 connectTimeout、readTimeout,禁止无限等待,避免线程被下游拖死。
  • 重试控制 重试次数不能过大;只用于 GET 幂等接口;非幂等写接口禁用重试,防止脏数据。
  • 接口幂等性 业务流水号保证幂等,应对重试、重复请求。
  • 异常处理 统一异常处理器,规范返回;区分业务异常、系统异常。
  • 5. 分布式事务治理(Seata‑AT)

    微服务跨服务数据一致性属于服务治理一部分。

  • 合理选型分布式事务方案 优先业务最终一致性;强一致性场景使用 Seata‑AT。
  • XID 上下文透传 Feign 自动透传 XID;线程池异步线程手动传递 XID 上下文,避免事务失效。
  • 禁止大事务 不要把远程 Feign 调用放进@GlobalTransactional,缩小事务范围,减少锁占用。
  • 监控 Seata 事务指标 通过 seata‑micrometer 上报事务成功、回滚指标到 Prometheus;异常事务配置告警。
  • 6. 可观测治理(三大支柱 Metrics + Tracing + Logging)

    没有可观测就谈不上治理,必须看得见服务运行状态。

  • 指标 Metrics:Micrometer 采集 JVM、HTTP、Sentinel、Seata 业务指标;Prometheus 拉取,Grafana 大盘;配置告警(实例宕机、错误率、GC、熔断次数)。
  • 链路 Tracing:Micrometer‑Tracing + Zipkin;TraceId 全链路透传,定位慢调用、跨服务故障点。
  • 日志 Logging:日志输出 traceId、spanId;Loki/ELK 日志聚合;依靠 traceId 打通指标‑链路‑日志。
  • 治理目标:故障及时告警、快速定位根因。
  • 7. 发布、运维与安全治理

  • 灰度 / 滚动发布 滚动发布,不全量停机;Nacos 权重灰度,小流量验证新版本,有问题快速切回旧版本。
  • 优雅停机 服务关闭前主动向 Nacos 注销实例,拒绝新请求,处理完存量请求再退出,避免请求报错。
  • 安全治理
    • actuator 监控端点内网访问,禁止暴露公网;增加认证。
    • 网关做鉴权认证,接口权限校验。
  • 容量评估、压测 定期压测评估系统容量,提前发现瓶颈。
  • 故障演练 模拟实例宕机、下游故障,验证限流熔断降级是否生效。

  • 面试精简口述版(直接背)

    Q:在微服务架构中怎么做服务治理?

    答: 微服务拆分之后服务数量变多,调用关系复杂,服务治理就是管控微服务全生命周期,我从注册发现、配置、流量、容错、分布式事务、可观测、运维发布这几个方面落地。

  • 注册发现治理:使用 Nacos 做注册中心,服务心跳检测,剔除不健康实例;通过 namespace、group 做环境隔离;配合 LoadBalancer 做客户端负载均衡,利用 Nacos 权重实现灰度流量。
  • 配置治理:Nacos 配置中心统一管理配置;支持@RefreshScope动态刷新;配置支持版本回滚;Sentinel 限流熔断规则持久化 Nacos,避免重启丢失。
  • 流量治理:Gateway 作为统一入口;网关和业务服务使用 Sentinel 做限流、熔断降级、线程隔离;保护系统防止雪崩;LoadBalancer 负责负载均衡,谨慎使用重试,非幂等写接口关闭重试。
  • 容错治理:Feign 设置合理超时;业务接口实现幂等;fallback 降级逻辑保持轻量,读接口返回缓存兜底,写接口不能伪造成功。
  • 分布式事务治理:跨服务一致性使用 Seata‑AT;注意 XID 上下文传递,异步线程手动传递上下文;避免大事务;监控事务执行指标。
  • 可观测治理:基于 Micrometer 三大支柱,Metrics 指标采集,Prometheus+Grafana 大盘告警;Micrometer‑Tracing+Zipkin 做链路追踪;日志聚合,traceId 串联指标、链路、日志,快速定位故障。
  • 运维发布治理:滚动、灰度发布;服务优雅停机;监控端点内网隔离;压测评估容量;故障演练验证容错能力。
  • 服务治理核心思想:看得见、管得住、故障隔离、快速恢复。


    延伸面试题

    Q:服务治理和服务注册发现的区别?

    服务注册发现只是服务治理其中一块;服务治理是更大的概念,还包含配置、流量、容错、可观测、发布运维整套能力。

    Q:微服务治理会带来哪些开销?

  • 注册中心心跳、拉取实例列表带来网络开销。
  • Sentinel 滑动窗口统计、链路追踪采样带来 CPU 网络开销。
  • Seata AT 模式 undo_log 生成带来数据库开销。
  • 生产环境调采样率、调阈值来平衡功能和性能。

    微服务接口设计原则(API 设计、版本兼容、契约)面试背诵版

    微服务之间通过 HTTP/Feign 通信,接口设计不合理会引发耦合、升级报错、兼容性问题。分为:接口规范、版本管理、契约设计、入参出参、异常处理、兼容性、最佳实践。

    一、接口设计基本原则

  • 职责单一 一个接口只做一件事,不要大而全。不要把多种业务逻辑塞到同一个接口;避免接口不断膨胀,参数越来越多。
  • 粗粒度原则 微服务对外接口尽量粗粒度,减少跨服务调用次数。
  • 不要在循环里 Feign 远程调用,会产生大量网络 IO,性能差。内部可以细粒度,对外提供粗粒度聚合接口。

  • 面向资源设计(REST 风格可选)
    • GET 查询;POST 新增;PUT 全量更新;PATCH 部分更新;DELETE 删除。
    • 实际项目 Feign 不一定严格 REST,但语义要清晰。
  • 解耦,最小依赖 服务之间只依赖接口,不要依赖对方内部实体类;不要把内部数据库实体直接对外返回。
  • 提供 DTO 对外,DO 数据库实体仅限服务内部使用。

  • 幂等性优先 写接口(新增、扣款、状态变更)必须支持幂等。依靠业务唯一流水号,防止重试、重复请求造成脏数据。
  • 超时可控 所有对外接口都要考虑超时;调用方配置 Feign 超时;接口本身不能无限阻塞。
  • 接口可观测 所有对外接口都被 micrometer 埋点统计 QPS、错误率、耗时;关键业务打印日志;链路 traceId 透传。

  • 二、入参 & 出参设计

    入参

  • 使用 DTO 接收参数,不要用零散参数;方便后续扩展字段。
  • 参数校验:服务端一定要校验,不能信任调用方传来的数据;非空、范围、格式校验。
  • 禁止传递大对象、大集合;分页查询,不要一次性返回全量数据,防止 OOM、网络压力。
  • 不要传递内部技术字段,只传递业务字段。
  • 出参

    统一返回体结构,示例:

    {
    "code": 200,
    "msg": "success",
    "data": { … }
    }

    • code:业务状态码,区分成功、业务异常、系统异常。
    • msg:提示信息。
    • data:业务数据;成功放数据,失败 data 可以为 null。

    ❌ 不要用 HTTP status 承载业务逻辑;业务异常统一返回 200,在 code 字段区分业务错误。HTTP 状态码只代表网络 /http 层状态。

    分页

    统一分页结构体:pageNum、pageSize、total、list;服务端强制限制最大 pageSize,防止全表导出攻击。


    三、异常设计(非常容易踩坑)

  • 区分业务异常 vs 系统异常
    • 业务异常:参数错误、业务规则不满足,可预期,返回对应业务 code。
    • 系统异常:数据库异常、网络异常,不可预期。
  • 不要把内部堆栈直接返回给调用方,泄露实现细节。
  • Feign 调用异常:调用方拿到返回码,区分是下游业务异常,还是下游服务宕机 / 超时。
  • 全局统一异常处理器,统一封装返回格式。
  • 注意:Sentinel 熔断降级会抛出特殊异常,业务要捕获处理。


    四、接口版本管理(兼容升级高频考点)

    微服务不能同时把所有服务一次性全部升级;必须支持滚动升级,新旧版本共存。

    核心原则:向后兼容,旧调用方不修改代码也可以调用新版本服务。

    兼容修改(安全,可以直接发布)

    ✅ 新增可选字段;DTO 增加新字段,设置为非必填。 ✅ 新增接口。 ✅ 枚举新增值。 ✅ 接口内部逻辑优化,不改变原有输出。

    不兼容修改(禁止直接发布,会导致旧调用方报错)

    ❌ 删除原有字段 ❌ 修改原有字段类型(String 改成 Integer) ❌ 修改字段含义,同一个字段表达不一样业务 ❌ 修改原有接口入参必填规则,把原来非必填改成必填 ❌ 修改返回数据结构

    版本号三种方案

  • URL 路径版本(/api/v1/user) 简单直观;不同版本不同 url;适合接口改动大、不兼容场景。 缺点:url 膨胀。
  • Header 版本(请求头携带 version:v1) 同一个 url,通过 header 区分版本;url 干净。网关根据 header 路由到不同版本服务实例。
  • 参数版本(参数?version=v1) 较少用。
  • 优先尽量做向后兼容,尽量不升级版本号;只有无法兼容的时候才升级版本。

    生产实践:优先兼容扩展;万不得已才升级 v2;新旧版本并行一段时间,旧调用方全部迁移完成后再下线 v1。


    五、服务契约(契约设计)

    契约就是调用方与提供方之间的约定:接口地址、入参、出参、错误码、超时时间、业务语义。

  • 契约优先:先定接口契约,再开发;前后端、微服务之间对齐契约。
  • 文档维护:Swagger / OpenAPI 生成接口文档;保证文档和代码同步。
  • 契约测试:保证接口契约不被无意破坏。
  • 不建议直接把对方的 DTO jar 包引入;会造成强耦合,一方 DTO 改动所有调用方都要升级。 最佳实践:接口文档作为契约,两边各自维护自己的 DTO。

    反模式:公共 DTO jar 包

    把服务对外 DTO 打成 jar,所有调用方依赖这个 jar。 问题:提供方修改 DTO,所有调用方必须同步升级 jar 包,微服务丧失独立发布能力,耦合严重。


    六、接口安全设计

  • 网关层统一鉴权;内部服务之间也可以做简单鉴权。
  • 参数防篡改,敏感参数校验。
  • 内部接口不要暴露到公网;内部服务接口只允许内网访问。
  • 防重放、防批量恶意请求。

  • 七、面试口述精简版

    Q:微服务之间接口怎么设计,如何保证版本兼容?

    答: 微服务接口设计,我主要从接口原则、入参出参、异常、版本兼容、服务契约几个方面考虑。

  • 接口原则 遵循职责单一,对外接口尽量粗粒度,减少远程调用次数;禁止循环 Feign 调用;对外使用 DTO,不暴露内部 DO 实体;写接口保证幂等;接口要做监控埋点。
  • 入参出参与异常 统一返回体,业务错误用自定义 code,不要用 HTTP 状态码表达业务逻辑;服务端必须做参数校验;分页要限制最大分页大小。区分业务异常和系统异常,不对外暴露内部堆栈。
  • 版本兼容是重点,优先向后兼容 允许新增字段、新增接口;禁止直接删除字段、修改字段类型、修改字段含义。这类不兼容改动不能直接发布。 如果无法兼容,需要做版本管理,可以采用 URL 版本或者 Header 版本,新旧版本并行,等调用方全部迁移完成再下线旧版本。
  • 服务契约 接口文档作为调用双方的契约;不建议共用 DTO jar 包,避免强耦合,两边各自维护 DTO;使用 Swagger 维护接口文档。
  • 配套容错:调用方配置超时;做好熔断降级保护。
  • 核心思想:微服务支持独立发布,修改服务不能强制所有依赖服务一起升级。


    延伸面试题

    Q:为什么不要直接返回数据库 DO 给外部调用?

    DO 里面包含数据库字段,内部字段,一旦表结构改动,接口就破坏兼容性;还会泄露内部信息;DO 包含很多不需要对外的字段。必须转换为对外 DTO。

    Q:什么是向后兼容?

    新版本服务可以接收旧版本调用方的请求,旧调用方不用改代码、不用升级,依然可以正常调用新版本服务。滚动发布的基础。

    Q:如果必须做不兼容接口变更怎么办?

    不能直接改老接口;发布新版本接口 v2;新旧版本同时运行;推动调用方逐步迁移到 v2;全部迁移完成之后下线 v1。

    微服务常见设计陷阱与反模式(面试背诵版)

    结合项目栈:Nacos/Gateway/OpenFeign‑LoadBalancer/Sentinel/Seata‑AT/Micrometer 可观测,面试经常问:微服务踩过哪些坑、哪些错误设计要避免。

    一、调用反模式

    1. 循环远程调用(循环 Feign)

    现象:for 循环里面调用 Feign 接口,批量查询,循环发起远程请求。

    // 反面示例
    for(Long id : idList){
    UserDTO user = userFeign.getUserById(id);
    }

    问题:大量网络 IO,性能极差,高并发下网络开销大,超时概率暴涨。 正确做法:提供批量查询接口,一次传入 ID 列表批量获取数据。

    2. 同步链路过长

    A 调用 B,B 调用 C,C 调用 D,链路层级很深。 问题:链路越长整体耗时越高,任意一个服务抖动,整条链路超时;故障概率放大;排错困难。 解决:

    • 尽量压缩调用链;
    • 部分场景改为 MQ 异步解耦;
    • 必要时做数据缓存,减少跨服务查询。

    3. 滥用重试

    对 POST 写接口开启 LoadBalancer 重试。 问题:产生重复下单、重复扣库存等脏数据。

    重试只适合 GET 幂等接口;写接口关闭重试;如果业务需要重试,业务层必须实现幂等。

    4. 不设置超时

    Feign 不配置 connectTimeout、readTimeout。 问题:下游服务慢,业务线程会一直阻塞,线程池耗尽,服务雪崩;Sentinel 慢调用熔断也无法触发。 解决:所有远程调用强制配置超时。

    二、服务拆分反模式

    1. 过度拆分(拆得过细)

    把很小的业务拆成多个微服务。 问题:大量跨服务调用,网络开销上升,分布式事务变多,复杂度陡增,运维成本暴涨。

    拆分原则:按业务域拆分,高内聚低耦合;不要为了拆而拆。优先考虑领域边界,团队维护边界。

    2. 共享数据库反模式

    多个微服务直接操作同一个数据库,多个服务读写同一张表。 问题:服务之间强耦合;表结构修改会影响多个服务;失去微服务独立发布能力。 解决:每个微服务拥有自己独立库;跨服务拿数据走接口调用,不要直接访问别人库。

    允许:只读场景做数据同步,不允许直接写对方库。

    3. 上帝服务(单体没拆干净)

    一个服务包揽大量不相关业务,代码量巨大,模块混杂,发布风险高。 问题:改一点就要整体发布;扩容困难;故障影响范围大。 解决:按领域逐步拆分。

    三、分布式事务反模式(Seata‑AT 高频坑)

    1. 大事务:@GlobalTransactional 包裹远程 Feign 调用

    @GlobalTransactional
    public void createOrder(){
    orderMapper.insert();
    // 远程调用放到全局事务内部,错误!
    storageFeign.deduct();
    }

    问题:远程调用网络耗时不可控,数据库事务长时间不提交,占用数据库锁,引发锁等待、数据库阻塞。 正确:全局事务尽量只包裹本地 DB 操作;远程调用放到事务外面。

    2. 异步线程丢失 XID 上下文

    线程池新开线程去执行逻辑,Seata XID 不会自动传递。 现象:明明加了@GlobalTransactional,但是分支事务不会回滚,数据不一致。 解决:手动传递上下文。

    3. AT 模式使用 DDL 语句

    DDL 不会生成 undo_log,无法回滚。业务避免在 AT 事务内执行 DDL。

    四、接口设计反模式

    1. 直接返回数据库 DO 实体对外

    把数据库 DO 直接作为 Feign 接口返回对象。 问题:表结构改动直接破坏接口兼容性;暴露内部数据库字段。 解决:对外统一使用 DTO,DO 只允许服务内部使用。

    2. 公共 DTO Jar 包强耦合

    把对外 DTO 打成公共 jar 包,所有调用方依赖该 jar。 问题:DTO 改动,所有调用服务必须同步升级 jar,丧失微服务独立发布能力。 解决:以接口文档作为契约,调用方和提供方各自维护 DTO。

    3. 接口不做向后兼容,随意修改老接口字段

    直接删除字段、修改字段类型。滚动发布环境,旧版本调用方会报错。 解决:优先新增字段;不兼容变更走 v2 新版本接口,新旧共存。

    4. 超大入参、全量查询不分页

    一次性传入上万条数据;查询接口不做分页,返回全量列表。 问题:网络传输大,内存暴涨,OOM 风险。 解决:限制单次批量大小;强制分页,服务端限制最大 pageSize。

    五、流量治理反模式(Sentinel)

    1. 把 Sentinel 内存规则当生产配置

    Sentinel 控制台配置规则只保存在内存,服务重启全部丢失。

    生产必须持久化规则到 Nacos 配置中心;控制台只做调试查看。

    2. 混淆单机 QPS 与集群 QPS

    Sentinel 限流阈值是单机实例 QPS。 比如阈值设置 100,部署 3 实例,集群总 QPS 上限约 300;很多人误以为是集群总 QPS,导致限流配置错误。

    3. fallback 里面写复杂业务逻辑

    fallback 又做数据库操作、又发起 Feign 调用。 问题:fallback 自身发生异常,兜底失效。 要求:fallback 逻辑尽量轻。

    六、可观测反模式

    1. 高基数 Tag

    把 userId、orderId 等业务唯一 ID 作为 micrometer tag。 问题:Prometheus 时序爆炸,内存 CPU 飙升,查询缓慢。 禁止把无限取值的业务 ID 设置为 tag。

    2. Tracing 采样率生产设置 1.0 全采样

    生产环境全部采样。 问题:Zipkin 存储压力巨大,CPU、网络开销上涨。 生产建议 0.1‑0.2 采样率;开发环境 1.0。

    3. actuator 端点暴露公网

    /actuator/prometheus等监控端点对外网开放。 风险:泄露服务内部运行信息。 解决:仅内网访问,增加认证。

    七、注册中心 & 部署反模式

    1. 不做优雅停机

    直接 kill 进程,实例没有主动注销 Nacos。 现象:Nacos 短时间保留不健康实例,流量打到已经关闭的服务,出现偶发报错。 解决:开启优雅停机;配合容错重试、异常处理。

    2. Nacos 单机部署

    注册中心单点故障,整个微服务体系服务发现瘫痪。 生产 Nacos 集群部署。

    八、面试口述版

    Q:微服务开发中有哪些常见反模式,怎么规避?

    答:

  • 调用反模式:禁止循环 Feign 远程调用,改用批量接口;避免调用链路过长;写接口禁用重试;远程调用必须配置超时。
  • 拆分反模式:不要过度拆分,也不要上帝服务;禁止多个微服务共享写同一个数据库;每个微服务拥有独立数据库。
  • 分布式事务反模式:Seata‑AT 不要把 Feign 远程调用放在全局事务中,避免大事务;异步线程手动传递 XID 上下文;事务内部避免 DDL。
  • 接口反模式:不要直接返回 DO 数据库实体;避免公共 DTO jar 包强耦合;接口优先向后兼容;大结果集强制分页。
  • 流量治理反模式:Sentinel 规则不能放内存,持久化 Nacos;分清单机 QPS;fallback 逻辑轻量化。
  • 可观测反模式:避免高基数 tag;生产链路不要全采样;actuator 监控端点禁止暴露公网。
  • 部署反模式:注册中心集群部署;服务配置优雅停机。
  • 微服务很多坑,本质来源于网络不可靠、分布式复杂度,设计时就要提前考虑容错、兼容、可观测。

    延伸面试题

    Q:微服务最大的痛点是什么?

    微服务把单体复杂度转移到分布式复杂度:网络不可靠、服务调用链变长、分布式数据一致性、服务治理、运维复杂度提升。需要注册中心、容错、限流熔断、分布式事务、完整可观测体系去解决。

    赞(0)
    未经允许不得转载:171主机测评 » 10w字详解springcloud全家桶(详细流程+配置+代码)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址