目录
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(响应式,非阻塞),用来做请求路由、鉴权、限流、重写路径、负载均衡。
核心三大概念:
⚠️⚠️⚠️ Gateway 不能用 Spring‑MVC (tomcat),必须用 webflux;引入依赖不要引入 spring‑boot‑starter‑web!
环境版本
- Spring Boot:3.2.x
- Spring Cloud:2023.0.x
- JDK 17
项目准备
一共两个服务:
第一步:业务服务 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
测试访问
访问网关地址:
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 打印全部请求 + 响应报文
⚠️注意:
✅【正确示例 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 负载均衡)
前置
上面例子 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返回给客户端
测试
请求完整流程梳理
访问 http://127.0.0.1:xxxx/callGateway
流程:
调用方服务 → http 请求 → Gateway 网关(校验 token 全局过滤器)→ 路由转发 → demo‑service 业务服务 → 返回结果给调用方。
常见踩坑:
❗️❗️❗️
微服务最佳实践:业务服务之间尽量不要跨网关调用。
业务服务之间直接 OpenFeign 调用;
网关只负责接收外部客户端请求(浏览器、APP)。
业务服务内部互相调用不走网关,可以避免额外 token 校验、多一层网络开销。
Spring Cloud Gateway 请求限流
Gateway 官方内置 Redis‑RateLimiter 限流过滤器,基于 Redis + Lua 脚本实现令牌桶限流,是生产最常用方案。
底层原理:令牌桶算法,Lua 脚本保证 Redis 操作原子性;需要 Redis 环境。
两种粒度:
限流维度:
可以按 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 脚本)
key 就是 KeyResolver 返回值,例如客户端 IP。
常见坑点
请求流程(带限流完整链路,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 配置路由,修改配置要重启网关。
动态路由:运行时不用重启网关,新增 / 修改 / 删除路由规则。
实现方案:
核心 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 不做什么(边界,面试高频)
Gateway 完整的整体工作流程汇总
面试高频提问清单
Nacos 完整讲解
Nacos = Dynamic Naming and Configuration Service 两大核心能力:
版本对齐: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 服务发现核心概念
负载均衡策略: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 核心概念
示例:共享配置,所有服务读取公共配置
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 常见踩坑
Nacos 在微服务整体位置(结合 Gateway)
客户端浏览器APP
↓
Spring Cloud Gateway(网关)
├─Nacos‑Discovery:注册网关自身;获取下游微服务实例 lb://xxx
└─Nacos‑Config:读取网关路由、限流参数配置
↓
下游微服务A、B、C
├─Nacos‑Discovery:服务注册;服务之间OpenFeign调用
└─Nacos‑Config:读取业务配置,动态刷新
面试高频问题
- 服务发现:AP 模式(高可用);配置中心:CP 模式(强一致)。
SpringCloud Gateway + Nacos‑Config 实现动态路由
目标:路由规则保存在 Nacos 配置中心,修改 Nacos 配置发布,网关不需要重启,路由实时生效。 版本:SpringBoot3.2.x、SpringCloud2023.0.x、Spring‑Cloud‑Alibaba 2023.0.1.0,JDK17
实现思路:
⚠️注意:代码方式管理动态路由之后,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. 测试动态路由功能
访问:
curl -H "token:abc123" http://127.0.0.1:8080/demo/api/hello
可以转发到下游业务服务。
[
{
"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
重要坑点说明
完整流程(Nacos 动态路由)
网关启动
↓
加载bootstrap.yml,连接Nacos‑Config
↓
NacosDynamicRouteService @PostConstruct执行
↓
从nacos读取gateway‑router.json字符串
↓
Jackson反序列成List<RouteDefinition>
↓
RouteDefinitionWriter删除内存旧路由,保存新路由到Gateway内存
↓
注册Nacos配置监听器
↓
Nacos控制台修改JSON路由配置,点击发布
↓
Nacos触发监听器回调
↓
再次loadRoutes(),重新拉取JSON,刷新内存路由;无需重启网关
↓
客户端请求到达Gateway,使用内存中新路由规则转发请求
生产拓展:
Seata 完整详解
Seata:分布式事务解决方案,解决微服务多服务操作数据库,要么全部成功、要么全部回滚。
场景举例:
下单业务:
三个属于不同微服务、不同数据库。
如果订单插入成功,扣库存抛出异常 → 数据不一致,订单存在,库存没扣。
本地事务只能管单个数据库,管不了跨服务。
Seata 就是解决这个问题。
Seata 四大模式:
版本对齐:
Spring‑Boot3.2.x,Spring‑Cloud2023.0.x,Spring‑Cloud‑Alibaba 2023.0.1.0;Seata 2.x。 JDK17
Seata 核心角色
通俗:
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 ↔ RM:一对多 1 个 TC 管理很多 RM; 一个全局事务 (XID),可以关联 N 个 RM(N 个分支事务)。
也就是:1 个 XID(全局事务)对应多个分支事务 BID,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 存储)
一、Seata‑Server 服务端安装启动(TC)
1. 下载 seata‑server‑2.2.0.zip https://github.com/seata/seata/releases
重要:Seata Server 有两种存储模式
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();
}
}
关键点:
AT 模式完整执行流程(重点面试)
成功流程:
- Before Image:执行前记录数据快照写入 undo_log。
- 执行业务 DML。
- After Image:执行后数据快照写入 undo_log。
- 本地事务提交(业务数据 + undo_log 一同提交数据库)。
- RM 向 TC 报告分支事务完成。
失败回滚流程(例如 account 服务抛异常):
AT 模式隔离级别:读未提交;中间未提交的数据其他事务可以读到;Seata 有行锁机制防止脏写。
TCC 模式(手动补偿模式)
AT 自动生成 undo_log;TCC 完全自己写业务补偿逻辑,没有 undo_log 表。 三个方法:
@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 常见坑点(高频面试)
@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模式)
面试高频题
Sentinel 完整详解
Sentinel:阿里开源的流量控制、熔断降级、系统自适应保护组件,面向微服务,保护服务不被打垮。
版本对齐:SpringBoot3.2.x,SpringCloud2023.0.x,Spring‑Cloud‑Alibaba 2023.0.1.0,JDK17。
两大核心部分:
核心能力:
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
⚠️注意:
微服务客户端 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启动就注册。
核心概念
- @SentinelResource 注解标记方法为资源。
- SpringMVC 接口默认就是资源,资源名就是接口路径 /order/create。
二、流量控制(流控规则 FlowRule)
流量控制:限制资源的访问速率,防止流量打满服务。
流控模式
1. 直接:对当前资源限流。
2. 关联:关联其它资源;关联资源达到阈值,限流本资源。
关联的场景:下单、查询订单;下单压力大,限制查询订单,保护下单。
3. 链路:只记录指定入口链路上的流量;同一个方法多个调用入口,只对某一条调用链路限流。
流控阈值类型
-
QPS(每秒查询数):每秒请求数量达到阈值,直接拦截。
例如 QPS=5:每秒最多 5 个请求,超过直接拒绝。
-
线程数:并行工作线程数阈值。
例如线程数 = 10:最多 10 个线程同时执行业务方法;新来请求拒绝。适合 IO 密集型。
流控效果
当超过阈值之后怎么处理
场景:服务刚启动,缓存还没初始化,瞬间大流量会打垮;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 次请求,就算全部异常,也不会熔断。
熔断状态机:
注意: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 常见坑
十二、整套微服务技术栈汇总回顾
前端浏览器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:流量监控,规则管理
高频面试题
- Hystrix:只做熔断降级,已经停止维护;线程池隔离;
- Sentinel:阿里,功能更全,限流、熔断、热点、系统保护;滑动窗口;支持 Nacos 持久化;信号量隔离为主。
Spring Cloud LoadBalancer 详解
Spring‑Cloud‑LoadBalancer:SpringCloud 官方客户端负载均衡组件,用来替代老旧的 Ribbon(已停止维护)。
作用:从 Nacos/Eureka 服务注册中心拿到服务实例列表,在客户端本地做负载均衡,选择一个服务实例发起请求。
区分两个概念:
技术栈对齐:
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 提供两种内置策略:
按顺序依次循环选取实例。实例 A→B→C→A→B→C。 适合各实例性能差不多的场景。
在可用实例列表随机挑选一个。
⚠️注意:和 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 核心概念
六、自定义负载均衡策略
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 对比
| Netflix 开源,已停止维护 | SpringCloud 官方实现,持续维护 |
| 内置轮询、随机、权重、重试等多种策略 | 内置只有轮询、随机;权重靠注册中心扩展(Nacos weight) |
| 大量自定义 API | API 简洁;响应式支持好,适配 Gateway |
| Feign 默认底层 | 新版 SpringCloud 默认;Gateway 底层使用 |
SpringCloud 2020.0 之后完全移除 Ribbon,只能用 LoadBalancer。
十、常见坑点
十一、完整调用链路回顾(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:服务注册中心,保存所有微服务实例、权重、元数据。
面试高频问题
Micrometer + Prometheus + Grafana 微服务监控完整详解
整套监控链路: 微服务 (SpringBoot) → Micrometer 埋点输出 Metrics 指标 → Prometheus 拉取指标数据 → Grafana 做可视化大盘、告警
组件角色分工
技术栈对齐:
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 类型
@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
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 -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 数据源
步骤 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:配置告警规则
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 几种类型
Tag 标签
指标带上 tag,用来做维度过滤:application服务名、uri接口路径、status响应码、method请求方法。
不要放高基数 tag(用户 ID、订单 ID),tag 爆炸会打爆 Prometheus 存储。
常见 PromQL 函数
❗注意:Counter 类型指标绝对不要直接用,必须套 rate/increase。
示例:
# order‑service所有接口QPS,按uri分组
sum by(uri) (rate(http_server_requests_seconds_count{application="order-service"}[1m]))
六、生产环境注意事项 & 坑点
常见问题
七、整合进整套微服务完整架构总图
前端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 是指标门面 API,屏蔽不同监控系统差异;业务代码只写一套 API,可以对接 Prometheus、InfluxDB 等多种监控。
Pull:Prometheus 主动拉;简单;目标实例暴露 metrics 接口。Push:客户端推到 pushgateway;适合短生命周期任务。微服务一般 Pull。
Counter 只增不减,rate 计算一段时间内每秒增量;直接取值没有意义。
tag 取值数量巨大,时间序列暴涨,Prometheus 内存 CPU 压力巨大。
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])))
🔹 常用函数说明
❗重要: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 工作模式:
步骤 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
| 12900 | Spring Boot Micrometer (JVM、HTTP、GC,最常用) |
| 14700 | Sentinel 监控大盘 |
| 1860 | Prometheus 自身监控大盘 |
导入方式:Grafana → Dashboards → Import,填入 ID 加载。
五、生产避坑清单
六、完整架构回顾
微服务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 分为两大块:
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)
组件分工
- 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 怎么打通指标、链路、日志三者?
常见坑
SpringCloud 微服务全套面试背诵版
技术栈:Nacos + Gateway + OpenFeign + LoadBalancer + Sentinel + Seata‑AT + Micrometer (Metrics+Tracing)+Prometheus+Grafana+Zipkin
适合 5‑8 年 Java 后端,微服务架构面试,全部高频问题,精简可直接背诵。
一、Nacos(注册中心 + 配置中心)
Q1 Nacos 作为注册中心原理
Q2 Nacos 配置中心原理
Q3 Nacos AP 还是 CP
- 临时实例(服务注册):AP,优先可用性,最终一致性。
- 持久实例:切换为 CP 模式,保证强一致。
Q4 Nacos 和 Eureka 对比
常见坑
- 长轮询超时;namespace 写错;@RefreshScope 不生效;权重负载均衡需要开启spring.cloud.nacos.discovery.weight=true。
二、Spring Cloud Gateway(网关)
Q1 Gateway 作用
请求入口;路由转发、鉴权、限流、熔断、跨域、日志、链路埋点;基于 WebFlux 响应式,非阻塞。
Q2 核心组件
Q3 Gateway 和 Zuul 区别
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 工作流程
Q3 LoadBalancer(替代 Ribbon)
SpringCloud2020 之后移除 Ribbon,官方 LoadBalancer。
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 工作机制
Q3 熔断和限流区别
- 限流:限制流入流量,保护自身。
- 熔断:下游故障,断开调用,保护调用方,防止雪崩。
Q4 Sentinel 和 Hystrix 对比
Hystrix 停止维护;Sentinel 功能更强,滑动窗口,支持网关限流,规则持久化 Nacos;轻量。
Q5 可观测
引入sentinel‑micrometer输出sentinel_*指标到 Prometheus;可以看到 block、degrade、pass 计数。
五、Seata‑AT 分布式事务
Q1 Seata AT 模式工作原理(重点)
AT 模式:无侵入,基于本地事务 + undo_log 回滚日志表。 角色:
执行流程:
undo_log 表:每一个业务库都需要创建。
Q2 AT 模式优缺点
✅业务代码无侵入; ❌只支持关系型数据库;不支持 DDL;大事务要避免长锁;undo_log 会有性能损耗。
Q3 Seata XID 怎么跨服务传递
Feign 拦截器自动把 XID 放到 http header 下游传递;RestTemplate 同样自动传递。
Q4 Seata 可观测
seata‑micrometer 输出全局事务、分支事务成功失败指标给 Prometheus。
Q5 Seata 常见问题
六、可观测三大支柱:Metrics + Tracing + Logging
完整可观测三支柱:指标、链路追踪、日志,通过 traceId 关联三者。
6.1 Micrometer‑Metrics(Prometheus+Grafana)
Q1 Micrometer 是什么
指标门面框架,类似 SLF4J;统一 API 采集指标,可以对接 Prometheus、InfluxDB 等多种监控系统。SpringBoot3 Actuator 底层基于 Micrometer。
Q2 四种 Meter 类型
Q3 Prometheus(时序数据库)
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 三者如何联动
高频综合面试题
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 微服务雪崩怎么防护
Q3 分布式事务方案选型
Q4 客户端负载均衡流程
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 你这套微服务监控告警怎么做
微服务线上踩坑清单 + 故障排查思路
技术栈:Nacos + Gateway + OpenFeign + LoadBalancer + Sentinel + Seata‑AT + Micrometer‑Metrics‑Tracing + Prometheus‑Grafana‑Zipkin‑Loki 面向 5‑8 年 Java 后端,线上问题定位流程、典型故障、根因、排查步骤、规避方案。
通用故障排查总流程(标准思路,面试也可以直接讲)
发生线上故障,按顺序排查,不要上来就看代码。
- HTTP:QPS、错误率、P95/P99 耗时、吞吐量。
- JVM:堆内存、GC 频率、GC 停顿、线程数、Tomcat 繁忙线程。
- Sentinel:限流、熔断次数,是否触发流控降级。
- Seata:全局事务成功 / 回滚 QPS。
核心线索: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])
排查步骤
规避
- 设置合理堆大小;生产开启 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 修改配置,程序没有拿到新值。 根因:
3. 权重灰度不生效
现象:Nacos 控制台修改实例权重,流量没有按权重分配。 根因:没有开启 spring.cloud.nacos.discovery.weight=true;LoadBalancer 默认轮询不会读取权重。
4. Nacos 长轮询超时警告
现象:日志大量长轮询 timeout 警告,不代表功能异常,网络问题会加剧。 规避:内网部署 Nacos;调大长轮询超时时间。
三、OpenFeign + LoadBalancer 故障
1. Feign 随机超时
现象:部分请求超时,不是全部超时。 排查:
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. 规则不生效
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 没有生成,回滚失效
3. 大事务问题
现象:全局事务执行时间很长;数据库锁占用;undo_log 堆积。 根因:@GlobalTransactional 包裹过多业务逻辑,包含远程调用,事务时间拉长。 规避:尽量缩小全局事务范围;远程调用放到事务外面。
4. 事务悬挂、空回滚
AT 模式经典问题;新版本 Seata 已经修复;注意使用稳定版本。
5. Seata‑Server 宕机
TM/RM 会缓存事务状态;短时间 Seata‑Server 不可用,已开启事务还能继续执行;新事务无法创建。
六、可观测体系(Micrometer / Prometheus / Zipkin)踩坑
1. Prometheus target 显示 DOWN
排查清单:
2. Grafana 图表无数据
3. 高基数 Tag,Prometheus CPU / 内存暴涨
禁止把 userId、orderId 作为 micrometer tag。 现象:prometheus 内存持续上涨,查询变慢。 根因:tag 取值无限多,时间序列爆炸。
4. Zipkin 看不到完整链路,traceId 断链
5. 日志看不到 traceId
logback pattern 没有配置%X{traceId} %X{spanId}。
6. Prometheus 存储爆满
默认保留 15 天;长期监控用 Thanos/Mimir 做远端存储;配置数据保留时间。
7. actuator 端点对外暴露
生产 actuator 不要暴露公网;内网访问;增加 basic auth 安全认证。
七、Gateway 网关踩坑
八、典型线上故障场景模拟(面试经常问)
场景 1:接口大量 504 超时
排查步骤:
场景 2:分布式事务部分回滚,数据不一致
场景 3:流量突增系统雪崩
场景 4:偶发随机报错,很难复现
大概率:网络抖动;实例上下线;重试;GC 停顿;Nacos 缓存延迟。 优先依赖 traceId 看 Zipkin 和日志,复现很难,链路日志是关键。
九、线上故障排查工具清单(实际工作使用)
十、生产环境检查清单(上线前核对)
✅ 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。
客户端负载均衡:消费端本地拿到实例列表,本地算法选实例直连下游;对比 Nginx 服务端负载均衡。
Q3:OpenFeign 的工作流程,会遇到哪些坑?
答:
常见坑:
Q4:Gateway 和 Zuul 对比,Gateway 核心组件?
答: Zuul1 基于 Servlet 阻塞 IO,性能差,已经停止维护;Zuul2 没有整合进 SpringCloud。 Gateway 基于 WebFlux 非阻塞响应式,性能更高,Spring 官方网关。 三大核心组件:
容错、雪崩、限流熔断
Q5:什么是服务雪崩?怎么防护?
答: 雪崩:一个下游服务故障,调用方大量请求堆积,线程耗尽,拖垮调用方,故障层层扩散,整个系统瘫痪。 防护手段:
Q6:Sentinel 限流和熔断降级区别?Sentinel 对比 Hystrix?
答:
- 限流:限制流入本资源的 QPS / 线程数,保护自己,多余请求直接拒绝。Sentinel 是单机实例 QPS,不是集群总 QPS。
- 熔断降级:发现下游服务故障,主动断开调用,快速返回,保护调用方,防止雪崩。
Hystrix 已经停止维护;Sentinel 功能更强:滑动窗口统计、支持网关限流、热点参数限流、规则可以持久化 Nacos,轻量易用。
注意:Sentinel 控制台配置规则重启丢失,生产必须持久化到 Nacos 配置中心。
Q7:Sentinel 滑动窗口原理简单说下
答: Sentinel 用滑动时间窗口做统计,把时间切分成多个小桶,每个桶统计 QPS、异常数、慢调用数量。时间滑动,淘汰过期桶,实时统计指标,用来判断是否触发限流或者熔断。
分布式事务 Seata‑AT
Q8:讲下 Seata AT 模式原理,三个角色,执行流程
答: 三个角色:
执行流程:
AT 优点:业务代码无侵入;缺点:只支持关系型数据库,不支持 DDL;大事务会带来锁与性能损耗。
每个业务库都需要手动创建 undo_log 表。
Q9:Seata‑AT 线上常见问题?
答:
Q10:分布式事务有哪些方案,怎么选型?
答:
可观测体系 Metrics & Tracing
Q11:Micrometer 是什么?Metrics 四种 Meter 类型?
答: Micrometer 相当于监控领域的 SLF4J,统一指标 API 门面,可以对接 Prometheus、InfluxDB 多种监控系统;SpringBoot3 Actuator 底层基于 Micrometer。 四种 Meter:
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串联:
Q16:Zipkin 看不到链路,可能的原因?
答:
故障排查类(高频)
Q17:线上接口大量 504 超时,你的排查思路?
答:
Q18:偶发数据不一致,Seata 分布式事务部分回滚,排查思路?
答:
Q19:线上故障通用排查流程
答:
Q20:线上生产上线前检查哪些关键点?
答:
如何保证微服务架构高可用
高可用核心目标:减少故障时间,单个组件故障不扩散,故障可快速止损,尽量保证业务可用。 分为:基础设施层、网关层、服务治理层、业务代码层、数据层、可观测与运维层,结合你这套栈 (Nacos/Gateway/Sentinel/Seata/Prometheus‑Zipkin)。
1. 基础设施 & 部署层面
2. 网关层高可用(Spring Cloud Gateway)
3. 服务调用与容错治理(Sentinel + LoadBalancer)
解决服务雪崩,这是微服务高可用最核心部分。
- 下游服务异常比例、慢调用比例到达阈值,触发熔断,快速失败,不再调用故障服务;
- 非核心业务降级返回兜底数据,牺牲非核心保证核心链路可用。
4. 业务代码层面保障
5. 数据层高可用
- 强一致性:Seata‑AT/TCC;
- 允许最终一致性:MQ 可靠消息,降低锁开销,提升可用性。
6. 可观测:监控、告警、故障快速发现(Micrometer+Prometheus+Grafana+Zipkin)
高可用前提:故障要尽早发现。
7. 运维保障
面试精简背诵版(直接口述)
保证微服务高可用,我会从基础设施、网关、服务容错、业务代码、数据层、可观测运维六个维度做保障:
核心思想:故障不可避免,目标是故障影响范围缩小,业务尽可能可用,故障快速感知快速恢复。
微服务降级与熔断设计(面试背诵版)
技术栈:Sentinel;区分熔断、降级概念;什么时候触发、策略、落地、坑点。
一、概念区分(面试必问)
熔断(Circuit‑Breaker)
保护调用方,针对下游依赖故障。 当调用下游服务,异常比例 / 慢调用比例达到阈值,熔断器打开,暂时切断对下游的调用,直接走快速失败逻辑,避免大量请求堆积、线程耗尽,防止雪崩。 熔断器有状态:关闭 → 打开 → 半开探测。
- 关闭:正常调用下游。
- 打开:不调用下游,快速失败。
- 半开:释放少量请求探测下游是否恢复;探测成功,熔断器关闭恢复正常调用。
熔断是被动触发:下游出问题才熔断。
降级(Fallback)
牺牲非核心业务,保障核心链路可用。 主动关闭 / 弱化某些非核心功能,返回兜底数据、缓存数据或者提示,不调用真实逻辑。
- 可以手动开关;也可以系统压力大、熔断触发时自动降级。
- 例子:商品详情页,推荐、评论是非核心,降级后不展示;订单创建,短信通知异步降级。
熔断是 “下游坏了我不调用它”;降级是 “我主动放弃一部分功能保核心”。
简单记: 熔断防下游故障扩散;降级保核心业务存活。熔断是手段,降级往往是熔断之后的兜底行为。
二、Sentinel 中熔断降级实现(实际项目怎么做)
Sentinel 的降级规则就是熔断能力,支持三种熔断策略:
配置要点:
- 滑动窗口时长;熔断时长(熔断打开多久);半开阶段会放行少量请求探测恢复。
- 熔断触发后,执行自定义 fallback 兜底逻辑。
生产规则不要写在内存,持久化到 Nacos 配置中心,服务重启规则不丢失;Sentinel‑Dashboard 只用来调试查看。
降级策略分类
核心原则:降级只降非核心链路;核心链路不能降级。
三、网关层面降级熔断(Spring Cloud Gateway + Sentinel)
网关层可以做:
四、业务落地注意点(面试高频坑)
- 读接口:容易降级,可以返回缓存、默认值。
- 写接口(下单、扣库存)不能随便降级,不能伪造成功,会造成数据错乱;写接口降级一般只能返回 “系统繁忙,请稍后”。
五、面试标准简答(可以直接口述)
Q:微服务中如何设计熔断和降级?
答: 熔断主要用来保护调用方,防止下游服务故障引发雪崩;降级用来牺牲非核心业务,保障核心链路可用。
我们项目使用 Sentinel 实现熔断降级。 熔断:配置降级规则,支持慢调用比例、异常比例、异常数三种策略。当下游调用达到阈值,熔断器打开,短时间停止调用下游服务;过了熔断时间进入半开状态,少量请求探测下游是否恢复。
降级:熔断触发后执行 fallback 兜底逻辑。读接口可以返回缓存或者默认值;写接口一般返回系统繁忙提示,不能伪造业务成功。非核心功能可以主动降级,保障核心链路。
落地注意:
熔断是防御手段,降级是兜底方案。
六、延伸面试题
Q:熔断和限流的区别?
- 限流:限制流入系统的流量,保护自己,抵御外部大流量。
- 熔断:下游依赖故障,保护自己不被拖垮,限制对外调用。
- 降级:主动舍弃部分功能,保障核心业务。
一句话:限流防流量;熔断防依赖故障;降级保核心。
微服务架构中的服务治理(面试背诵版)
结合你的技术栈:Nacos + Gateway + OpenFeign + LoadBalancer + Sentinel + Seata + Micrometer 可观测 服务治理本质:对微服务整个生命周期进行管控:服务注册发现、流量管控、容错、配置管理、可观测、灰度发布、安全、故障处理,解决微服务拆分之后带来的复杂度。
服务治理分为七大模块:服务注册发现治理、配置治理、流量治理、容错治理、分布式事务治理、可观测治理、运维 & 发布治理。
1. 服务注册与发现治理(Nacos)
- 区分临时实例(普通微服务)、持久实例(中间件)。
- namespace、group 做环境、业务分组隔离,避免开发 / 测试 / 生产环境互相串调。
注意:LoadBalancer 本身没有权重策略,需要 Nacos‑Discovery 扩展开启权重支持。
2. 配置治理(Nacos Config)
3. 流量治理(Gateway + Sentinel + LoadBalancer)
流量治理是服务治理核心,管控服务之间调用流量。
- 网关层限流:拦截入口大流量。
- 服务维度限流:Sentinel 单机 QPS、线程数限流;预热、匀速排队,防止突增流量打垮服务。
Sentinel 是单机实例 QPS,不是集群总 QPS,多实例要计算整体容量。
4. 容错治理
5. 分布式事务治理(Seata‑AT)
微服务跨服务数据一致性属于服务治理一部分。
6. 可观测治理(三大支柱 Metrics + Tracing + Logging)
没有可观测就谈不上治理,必须看得见服务运行状态。
7. 发布、运维与安全治理
- actuator 监控端点内网访问,禁止暴露公网;增加认证。
- 网关做鉴权认证,接口权限校验。
面试精简口述版(直接背)
Q:在微服务架构中怎么做服务治理?
答: 微服务拆分之后服务数量变多,调用关系复杂,服务治理就是管控微服务全生命周期,我从注册发现、配置、流量、容错、分布式事务、可观测、运维发布这几个方面落地。
服务治理核心思想:看得见、管得住、故障隔离、快速恢复。
延伸面试题
Q:服务治理和服务注册发现的区别?
服务注册发现只是服务治理其中一块;服务治理是更大的概念,还包含配置、流量、容错、可观测、发布运维整套能力。
Q:微服务治理会带来哪些开销?
生产环境调采样率、调阈值来平衡功能和性能。
微服务接口设计原则(API 设计、版本兼容、契约)面试背诵版
微服务之间通过 HTTP/Feign 通信,接口设计不合理会引发耦合、升级报错、兼容性问题。分为:接口规范、版本管理、契约设计、入参出参、异常处理、兼容性、最佳实践。
一、接口设计基本原则
不要在循环里 Feign 远程调用,会产生大量网络 IO,性能差。内部可以细粒度,对外提供粗粒度聚合接口。
- GET 查询;POST 新增;PUT 全量更新;PATCH 部分更新;DELETE 删除。
- 实际项目 Feign 不一定严格 REST,但语义要清晰。
提供 DTO 对外,DO 数据库实体仅限服务内部使用。
二、入参 & 出参设计
入参
出参
统一返回体结构,示例:
{
"code": 200,
"msg": "success",
"data": { … }
}
- code:业务状态码,区分成功、业务异常、系统异常。
- msg:提示信息。
- data:业务数据;成功放数据,失败 data 可以为 null。
❌ 不要用 HTTP status 承载业务逻辑;业务异常统一返回 200,在 code 字段区分业务错误。HTTP 状态码只代表网络 /http 层状态。
分页
统一分页结构体:pageNum、pageSize、total、list;服务端强制限制最大 pageSize,防止全表导出攻击。
三、异常设计(非常容易踩坑)
- 业务异常:参数错误、业务规则不满足,可预期,返回对应业务 code。
- 系统异常:数据库异常、网络异常,不可预期。
注意:Sentinel 熔断降级会抛出特殊异常,业务要捕获处理。
四、接口版本管理(兼容升级高频考点)
微服务不能同时把所有服务一次性全部升级;必须支持滚动升级,新旧版本共存。
核心原则:向后兼容,旧调用方不修改代码也可以调用新版本服务。
兼容修改(安全,可以直接发布)
✅ 新增可选字段;DTO 增加新字段,设置为非必填。 ✅ 新增接口。 ✅ 枚举新增值。 ✅ 接口内部逻辑优化,不改变原有输出。
不兼容修改(禁止直接发布,会导致旧调用方报错)
❌ 删除原有字段 ❌ 修改原有字段类型(String 改成 Integer) ❌ 修改字段含义,同一个字段表达不一样业务 ❌ 修改原有接口入参必填规则,把原来非必填改成必填 ❌ 修改返回数据结构
版本号三种方案
优先尽量做向后兼容,尽量不升级版本号;只有无法兼容的时候才升级版本。
生产实践:优先兼容扩展;万不得已才升级 v2;新旧版本并行一段时间,旧调用方全部迁移完成后再下线 v1。
五、服务契约(契约设计)
契约就是调用方与提供方之间的约定:接口地址、入参、出参、错误码、超时时间、业务语义。
不建议直接把对方的 DTO jar 包引入;会造成强耦合,一方 DTO 改动所有调用方都要升级。 最佳实践:接口文档作为契约,两边各自维护自己的 DTO。
反模式:公共 DTO jar 包
把服务对外 DTO 打成 jar,所有调用方依赖这个 jar。 问题:提供方修改 DTO,所有调用方必须同步升级 jar 包,微服务丧失独立发布能力,耦合严重。
六、接口安全设计
七、面试口述精简版
Q:微服务之间接口怎么设计,如何保证版本兼容?
答: 微服务接口设计,我主要从接口原则、入参出参、异常、版本兼容、服务契约几个方面考虑。
核心思想:微服务支持独立发布,修改服务不能强制所有依赖服务一起升级。
延伸面试题
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:微服务开发中有哪些常见反模式,怎么规避?
答:
微服务很多坑,本质来源于网络不可靠、分布式复杂度,设计时就要提前考虑容错、兼容、可观测。
延伸面试题
Q:微服务最大的痛点是什么?
微服务把单体复杂度转移到分布式复杂度:网络不可靠、服务调用链变长、分布式数据一致性、服务治理、运维复杂度提升。需要注册中心、容错、限流熔断、分布式事务、完整可观测体系去解决。



