Spring Cloud Alibaba 最佳实践与面试考点
本章是 Spring Cloud Alibaba 独立教程的收官之作,将前面各章的知识点整合为生产可用的最佳实践方案,并从面试视角体系化梳理核心考点。
生产部署架构
完整微服务生产部署拓扑
飞翔科技在经历数月的微服务化改造后,CTO 大翔要求架构师白歌输出一份生产部署拓扑图。白歌花了三天时间,将 Nacos、Sentinel、Seata、RocketMQ 以及可观测性组件全部整合进一套拓扑中。
白歌将这张拓扑图贴在团队 Wiki 首页,标注了一句话:"任何组件缺失,都可能导致某个维度失控——注册、配置、防护、事务、消息、可观测,六个维度缺一不可。"
Nacos 集群部署
在生产环境中,Nacos 必须高可用部署。白歌采用的方案是 3 节点 + MySQL 集群。
集群架构:
| 节点 | IP | 端口 | 角色 |
|---|---|---|---|
| Nacos-1 | 10.0.1.11 | 8848 | Leader / Follower |
| Nacos-2 | 10.0.1.12 | 8848 | Follower |
| Nacos-3 | 10.0.1.13 | 8848 | Follower |
| MySQL | 10.0.1.20 | 3306 | 配置 + 服务数据持久化 |
cluster.conf 配置:
# Nacos 集群配置(每个节点上的 cluster.conf 内容一致)
10.0.1.11:8848
10.0.1.12:8848
10.0.1.13:8848
application.properties(生产关键配置):
# 数据源配置(生产必须外接 MySQL)
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://10.0.1.20:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000
db.user=nacos
db.password=${NACOS_DB_PASSWORD}
# Nacos 集群模式启动
nacos.core.auth.enabled=true
nacos.core.auth.system.type=nacos
启动命令:
# 生产模式启动(每个节点都执行)
cd nacos/bin
./startup.sh -m cluster
关键:Nacos 3 节点容忍 1 节点故障。2 节点容错为 0——任意 1 节点挂掉即丧失过半仲裁能力,因此生产环境必须至少 3 节点。
客户端连接配置:
spring:
cloud:
nacos:
discovery:
server-addr: 10.0.1.11:8848,10.0.1.12:8848,10.0.1.13:8848 # 集群地址
namespace: prod
Seata Server 集群 + Nacos 高可用
Seata TC(Transaction Coordinator,事务协调器)同样是分布式事务的核心,必须高可用部署。
集群架构:
| 组件 | 数量 | 说明 |
|---|---|---|
| Seata Server | 2+ | 无状态,通过 Nacos 做服务发现 |
| Nacos | 3 节点 | Seata Server 注册中心 + 配置中心 |
| MySQL | 集群 | global_table / branch_table / lock_table 存储 |
registry.conf:
registry {
type = "nacos"
nacos {
application = "seata-server"
serverAddr = "10.0.1.11:8848,10.0.1.12:8848,10.0.1.13:8848"
group = "SEATA_GROUP"
namespace = "prod"
cluster = "default"
}
}
config {
type = "nacos"
nacos {
serverAddr = "10.0.1.11:8848,10.0.1.12:8848,10.0.1.13:8848"
group = "SEATA_GROUP"
namespace = "prod"
}
}
高可用保障机制:
| 机制 | 说明 |
|---|---|
| TC 无状态 | Seata Server 不存储事务状态(全部在 MySQL global_table 中),任意节点宕机后 Nacos 自动踢除 |
| 客户端重试 | RM(Resource Manager,资源管理器)/ TM(Transaction Manager,事务管理器)发现 TC 不可用时自动切换至 Nacos 返回的其他健康 TC 实例 |
| 全局锁一致性 | lock_table 存储在 MySQL 中,所有 TC 共享同一数据源,保证全局锁的分布式一致性 |
| 事务恢复 | TC 宕机后,同一集群中其他 TC 可接管未完成的全局事务(通过 global_table 查询) |
RocketMQ Dledger 集群架构
Dledger 是 RocketMQ 4.5+ 引入的基于 Raft(Raft Consensus Algorithm)协议的自动主从切换方案,替代传统人工主从模式。
Dledger 关键配置(broker.conf):
enableDLegerCommitLog=true
dLegerGroup=broker-group-01
dLegerPeers=n0-10.0.1.31:40911;n1-10.0.1.32:40911;n2-10.0.1.33:40911
dLegerSelfId=n0
Dledger vs 传统主从:
| 维度 | 传统主从(Master-Slave) | Dledger(Raft-based) |
|---|---|---|
| 故障切换 | 人工切换,分钟级 | 自动选举,~10s |
| 数据一致性 | 异步复制可能丢消息 | Raft 过半提交保证不丢 |
| 运维成本 | 高(需人工介入) | 低(自动恢复) |
| 部署复杂度 | 低 | 中(需至少 3 节点) |
监控整合方案
SkyWalking 接入 Spring Cloud Alibaba
SkyWalking(Apache SkyWalking)是开源的应用性能管理(APM,Application Performance Monitoring)系统,通过 Java Agent 探针实现零侵入的链路追踪。
Agent 下载与配置:
# 下载 SkyWalking Agent
wget https://dlcdn.apache.org/skywalking/java-agent/9.3.0/apache-skywalking-java-agent-9.3.0.tgz
tar -xzf apache-skywalking-java-agent-9.3.0.tgz -C /opt/
agent.config 关键配置:
# SkyWalking OAP Server 地址
agent.service_name=${spring.application.name}
collector.backend_service=10.0.1.41:11800
# 采样率(生产环境建议 10%~30%)
agent.sample_n_per_3_secs=30
Java 启动参数:
java -javaagent:/opt/skywalking-agent/skywalking-agent.jar \
-Dskywalking.agent.service_name=order-service \
-Dskywalking.collector.backend_service=10.0.1.41:11800 \
-jar order-service.jar
白歌的运维经验:SkyWalking Agent 对 Spring Cloud Alibaba 组件(Nacos、Sentinel、Feign、RocketMQ)的链路采集是自动的——Agent 内置了对 Nacos 客户端、Sentinel、Feign 客户端、RocketMQ 的拦截插件,开发者无需写任何埋点代码。
SkyWalking 拓扑图中可见的调用链:
Gateway → OrderService → UserService(Feign)
→ StorageService(Feign)
→ RocketMQ(发送消息)
UserService → RocketMQ(消费消息)
Sentinel Dashboard + Prometheus + Grafana
Sentinel Dashboard 内置了轻量级监控,但在生产环境中白歌推荐接入 Prometheus(指标采集)+ Grafana(可视化)体系。
Sentinel 暴露 Prometheus 指标:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-prometheus-metric-exporter</artifactId>
<version>1.8.6</version>
</dependency>
Prometheus 配置(prometheus.yml):
scrape_configs:
- job_name: 'sentinel'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['10.0.1.11:8719', '10.0.1.12:8719', '10.0.1.13:8719']
PromQL 查询示例:
| 查询目标 | PromQL 语句 |
|---|---|
| 某资源 QPS(每秒查询数) | rate(sentinel_qps_total{resource="getOrder"}[1m]) |
| 某资源平均响应时间 | rate(sentinel_rt_total{resource="getOrder"}[1m]) / rate(sentinel_success_total{resource="getOrder"}[1m]) |
| 某资源被限流次数 | rate(sentinel_blocked_total{resource="getOrder"}[1m]) |
| 某资源熔断状态 | sentinel_degrade_state{resource="getOrder"}(0=CLOSED, 1=OPEN, 2=HALF_OPEN) |
| 服务实例 CPU 使用率 | system_cpu_usage{instance=~"10.0.1.*"} |
Grafana 仪表盘布局建议:
┌───────────────────────────────────────────────────┐
│ 第一行:整体概览 QPS / 平均 RT / 错误率 / 熔断状态 │
├───────────────────────┬───────────────────────────┤
│ 第二行左:资源级 QPS │ 第二行右:资源级 RT 分布 │
├───────────────────────┼───────────────────────────┤
│ 第三行左:限流/熔断趋势 │ 第三行右:系统 Load / CPU │
└───────────────────────┴───────────────────────────┘
关键监控指标速查表
Nacos 监控指标
| 指标 | 含义 | 告警阈值 |
|---|---|---|
nacos_server_connections | 当前长连接数 | > 10,000(单节点 2.x gRPC 连接) |
nacos_cpu_usage | Nacos Server CPU 使用率 | > 80% |
nacos_heap_usage | JVM 堆内存使用率 | > 85% |
nacos_service_count | 注册服务总数 | 突变 > 50%(短时间大量服务下线) |
nacos_instance_healthy_ratio | 实例健康率 | < 80%(大量实例不健康) |
nacos_config_push_qps | 配置推送 QPS | > 5000/s(异常配置推送风暴) |
Sentinel 监控指标
| 指标 | 含义 | 告警阈值 |
|---|---|---|
sentinel_qps_total | 资源每秒请求数 | 依据集群容量设定 |
sentinel_blocked_total | 被限流/熔断的请求数 | > 100/s(持续被限制) |
sentinel_rt_avg | 资源平均响应时间 | > 基准 RT × 3 |
sentinel_degrade_state | 熔断器状态(0/1/2) | = 1(处于 OPEN 状态) |
sentinel_exception_ratio | 异常比例 | > 20% |
Seata 监控指标
| 指标 | 含义 | 告警阈值 |
|---|---|---|
seata_global_transaction_active | 活跃全局事务数 | > 1000 |
seata_global_transaction_rollback_rate | 全局事务回滚率 | > 30%(高频回滚说明业务异常) |
seata_lock_conflict_count | 全局锁冲突次数 | > 100/min |
seata_branch_transaction_timeout_count | 分支事务超时数 | > 10/min |
seata_global_transaction_avg_rt | 全局事务平均 RT | > 5s(长事务阻塞其他事务) |
RocketMQ 监控指标
| 指标 | 含义 | 告警阈值 |
|---|---|---|
rocketmq_broker_tps | Broker 消息 TPS | 突降 > 50% |
rocketmq_producer_tps | Producer 发送 TPS | 突降 > 50% |
rocketmq_consumer_tps | Consumer 消费 TPS | 连续低于 Producer TPS(消费堆积) |
rocketmq_diff_total | 消息堆积量 | > 100,000 |
rocketmq_commitlog_disk_ratio | CommitLog 磁盘使用率 | > 85% |
告警规则参考
白歌根据生产经验建立了四级告警体系:
| 级别 | 响应时间 | 典型触发条件 | 通知方式 |
|---|---|---|---|
| P0 紧急 | 5 分钟 | Nacos 全集群不可用、RocketMQ Broker 不可用、订单 QPS 暴跌 90% | 电话 + 短信 + 企业微信 |
| P1 严重 | 15 分钟 | Sentinel 熔断器连续 OPEN、消息堆积 > 10 万、数据库连接池耗尽 | 短信 + 企业微信 |
| P2 一般 | 30 分钟 | 单服务实例不健康、配置灰度推送延迟、少量消息重试 | 企业微信 |
| P3 提醒 | 2 小时 | CPU / 内存 > 70%、磁盘使用率 > 70%、全局事务回滚率异常波动 | 企业微信(静默模式) |
性能调优速查
Nacos 客户端调优
| 参数 | 默认值 | 建议值 | 说明 |
|---|---|---|---|
spring.cloud.nacos.discovery.heart-beat-interval | 5000ms | 3000ms | 缩短心跳间隔加快故障检测(牺牲少量网络开销) |
spring.cloud.nacos.discovery.heart-beat-timeout | 15000ms | 10000ms | 缩短不健康判定时间 |
spring.cloud.nacos.discovery.ip-delete-timeout | 30000ms | 20000ms | 缩短已挂实例的自动剔除时间 |
spring.cloud.nacos.config.timeout | 3000ms | 5000ms | 首次拉取配置的超时时间,网络差时适当增大 |
spring.cloud.nacos.config.refresh-enabled | true | true(保持) | 必须保持开启以支持配置自动刷新 |
本地缓存调优:
spring:
cloud:
nacos:
discovery:
naming-load-cache-at-start: true # 启动时立即加载本地缓存,加速首次调用
config:
enable-remote-sync-config: true # 远程配置同步到本地快照,Nacos 宕机时兜底
白歌的实战经验:飞翔科技在一次 Nacos 集群短暂不可用期间(数据库主从切换),依赖本地快照缓存保证所有服务依然能发现彼此——"本地快照就是你的保险绳。"
Sentinel 调优
| 参数 | 默认值 | 建议值 | 说明 |
|---|---|---|---|
统计窗口 statIntervalMs | 1000ms | 1000ms(保持) | 保持 1s 精度,不建议调大 |
窗口槽位数 sampleCount | 2 | 2~5 | 越大统计精度越高但内存开销越大 |
| Warm Up 预热时长 | 10s | 30s(冷启动场景) | 冷启动时给 JVM 和连接池预留充分预热时间 |
熔断最小请求数 minRequestAmount | 5 | 20(生产) | 避免少量异常触发误熔断 |
| 集群流控 Token Server 连接超时 | 1000ms | 500ms | Token Server 请求应快速失败,避免拖垮业务线程 |
QPS 阈值参考(基于 4 核 8G 实例):
| 接口类型 | 建议初始 QPS 阈值 | 说明 |
|---|---|---|
| 简单查询(无 DB) | 500 | 纯内存/缓存操作 |
| 数据库查询 | 200 | 考虑 DB 连接池和查询耗时 |
| 复杂计算 | 50 | 涉及文件/网络 IO 或 CPU 密集 |
| 第三方 API 调用 | 20 | 下游不可控,保守限流 |
重要:以上是初始建议值,生产环境必须通过压测确定真实阈值。小崔第一次直接把所有接口限流到 QPS=10,导致正常流量也被拒绝——孔蓝在运营群连发 5 条消息催修复。
Seata 调优
| 参数 | 默认值 | 建议值 | 说明 |
|---|---|---|---|
client.rm.lock.retryInterval | 10ms | 20ms | 全局锁重试间隔 |
client.rm.lock.retryTimes | 30 | 60 | 全局锁重试最大次数(高并发场景适当增大) |
client.rm.reportRetryCount | 5 | 5(保持) | 分支状态上报重试 |
server.maxCommitRetryTimeout | -1(无限) | 600000ms(10min) | TC 发起提交请求的最大等待时间 |
server.maxRollbackRetryTimeout | -1(无限) | 600000ms(10min) | TC 发起回滚请求的最大等待时间 |
client.tm.commitRetryCount | 5 | 5(保持) | 全局提交重试 |
client.tm.rollbackRetryCount | 5 | 5(保持) | 全局回滚重试 |
UNDO LOG 定期清理:
-- 清理 7 天前的已完成 UNDO LOG
DELETE FROM undo_log
WHERE log_status = 1 -- 1 = 已完成
AND log_created < DATE_SUB(NOW(), INTERVAL 7 DAY)
LIMIT 1000; -- 分批删除,避免锁表
警告:UNDO LOG 不会自动清理!飞翔科技曾经因为
undo_log表膨胀到 50GB 导致数据库磁盘告警——孔蓝半夜被运维电话叫醒后,给白歌下了一个"每周清理"的死命令。
全局事务超时:
@GlobalTransactional(timeoutMills = 300000) // 5 分钟超时(默认 60s 可能太短)
public void createOrder(Order order) { ... }
RocketMQ Producer / Consumer 调优
Producer 调优:
| 参数 | 默认值 | 建议值 | 说明 |
|---|---|---|---|
sendMessageTimeout | 3000ms | 5000ms | 同步发送超时,跨机房场景适当增大 |
retryTimesWhenSendFailed | 2 | 3 | 发送失败重试 |
compressMsgBodyOverHowmuch | 4096 | 2048 | 消息体 > 2KB 自动压缩 |
maxMessageSize | 4194304(4MB) | 1048576(1MB) | 限制消息体大小,避免大消息阻塞 |
Consumer 调优:
| 参数 | 默认值 | 建议值 | 说明 |
|---|---|---|---|
consumeThreadMin | 20 | 10~20 | 最小消费线程数 |
consumeThreadMax | 64 | 32~64 | 最大消费线程数 |
pullBatchSize | 32 | 16~32 | 每次拉取消息数 |
consumeMessageBatchMaxSize | 1 | 1~10 | 批量消费上限 |
maxReconsumeTimes | 16 | 16(保持) | 最大重试次数,最终进入死信队列 |
JVM 通用建议
白歌为各组件制定了 JVM 配置建议:
| 组件 | 推荐堆内存 | 推荐 GC 策略 | 说明 |
|---|---|---|---|
| Nacos Server | 2G~4G | G1GC | Nacos 对象生灭快,G1GC 的 Region 化管理更合适 |
| Sentinel Dashboard | 1G~2G | G1GC | 内存中存储大量指标数据 |
| Seata Server | 2G~4G | G1GC | TC 处理大量分支事务请求 |
| RocketMQ Broker | 4G~8G | G1GC | CommitLog 大量使用堆外内存,堆内存不必过大 |
| RocketMQ NameServer | 1G~2G | G1GC | 轻量级,内存需求低 |
| 业务微服务 | 2G~4G | G1GC(JDK 17+ 默认) | 结合虚拟线程(Virtual Threads)释放传统线程池开销 |
# 典型 JVM 启动参数示例
java -Xms2g -Xmx4g -XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/opt/logs/heapdump.hprof \
-jar order-service.jar
故障排查手册
Nacos 注册失败排查(6 个维度)
小崔某天部署新版本后,订单服务在 Nacos 控制台看不到。白歌带着他按以下维度逐一排查并最终定位。
| 维度 | 检查项 | 诊断命令/方法 | 修复方案 |
|---|---|---|---|
| 1. 网络连通性 | Nacos Server 端口可达 | telnet 10.0.1.11 8848 | 检查防火墙/安全组放行 8848 端口 |
| 2. gRPC 端口 | Nacos 2.x gRPC 端口 | telnet 10.0.1.11 9848 | Nacos 2.x 额外需要 9848(gRPC),这是最常见的遗漏 |
| 3. 配置一致性 | namespace / group 匹配 | 对比 bootstrap.yml 和 Nacos 控制台 | namespace 必须用 UUID 而非名称;group 大小写敏感 |
| 4. 客户端日志 | Nacos 客户端启动日志 | grep "nacos" startup.log | 查找 failed to req API 或 ConnectException |
| 5. 版本兼容性 | Nacos Client 版本 vs Server 版本 | cat pom.xml | grep nacos | Nacos 2.x Client 连 1.x Server 降级为 HTTP,部分功能不可用 |
| 6. 认证授权 | Nacos 账号密码 | Nacos 控制台检查 | 2.x 默认开启认证,确保 username / password 配置正确 |
最常见问题 Top 3:① gRPC 9848 端口未放行(2.x 专属);② namespace 填名称而非 UUID;③
spring.application.name未配置。
Sentinel 规则不生效排查(5 个维度)
| 维度 | 检查项 | 诊断命令/方法 | 修复方案 |
|---|---|---|---|
| 1. Dashboard 连接 | 客户端是否成功注册到 Dashboard | Dashboard 机器列表查看 | 检查 transport.dashboard 配置和 transport.port |
| 2. 资源名匹配 | 规则中的 resource 名和实际资源名是否一致 | 查看 Dashboard 簇点链路 | 核对 @SentinelResource("getOrder") 与 Dashboard 规则中资源名 |
| 3. 规则类型 | 流控模式/效果是否匹配预期 | Dashboard 规则详情 | 确认 grade(QPS/线程)、strategy(直接/关联/链路)、controlBehavior(快速/WarmUp/排队) |
| 4. REST URL | URL 资源是否默认被 Sentinel Web Filter 拦截 | curl http://localhost:8080/getOrder 看响应 | REST URL 无需 @SentinelResource 即自动作为资源;但需要 blockHandler 则必须注解 |
| 5. 规则持久化 | 是否为拉取到了持久化规则 | 查看应用日志中 DataSource 输出 | 确认 Nacos DataSource 配置正确 |
| 6. Sentinel 日志 | 本地日志中有无异常 | ${user.home}/logs/csp/sentinel-record.log | grep "ERROR" sentinel-record.log |
Sentinel 诊断命令(本地接口):
# 查看所有已加载的流控规则
curl http://localhost:8719/getRules?type=flow
# 查看某资源的实时 QPS
curl http://localhost:8719/cnode?id=getOrder
# 查看客户端与 Dashboard 的连接状态
curl http://localhost:8719/getDashboardServer
Seata 回滚失败排查(5 个维度)
| 维度 | 检查项 | 诊断方法 | 修复方案 |
|---|---|---|---|
| 1. undo_log 表 | 是否存在且结构完整 | SHOW TABLES LIKE 'undo_log'; DESCRIBE undo_log; | 每个业务库都要创建,对照 Seata 官方 DDL |
| 2. 前镜像序列化 | 前镜像是否成功保存 | SELECT * FROM undo_log WHERE xid='xxx' | 检查 rollback_info 字段是否非空 |
| 3. 全局锁冲突 | 回滚时是否被其他事务持有锁 | SELECT * FROM lock_table WHERE row_key='xxx' | 超时后 TC 自动释放,或手动清理 |
| 4. 补偿 SQL 执行 | 反向 SQL 是否生成并执行成功 | 业务日志中查找 rollback undo log | 表结构变更后前镜像的反向 SQL 可能失败(字段类型不兼容) |
| 5. TC 端驱动 | TC 是否成功发起了回滚指令 | TC 日志 grep "rollback" seata-server.log | TC 与 RM 的 gRPC 连接是否中断 |
Seata 错误码对照:
| 错误码/异常 | 含义 | 处理方案 |
|---|---|---|
LockConflictException | 全局锁冲突 | 增大 lock.retryTimes 或检查是否有死锁 |
BranchRollbackFailed_Retriable | 分支回滚失败可重试 | TC 会自动重试,检查 RM 日志 |
ShouldNeverHappenException | UNDO LOG 前镜像与当前数据不一致(脏写) | 严重:数据已被非 Seata 事务修改,需人工介入 |
GlobalTransactionNotFound | XID 对应的全局事务不存在 | TC 数据已被清理或 XID 传递丢失 |
RocketMQ 消息丢失排查(4 个丢失环节)
| 环节 | 丢失原因 | 修复方案 |
|---|---|---|
| ① 发送环节 | 异步发送 + 网络抖动(sendStatus 非 SEND_OK) | 改用同步发送 sync: true;检查 sendResult.getSendStatus() |
| ② 存储环节 | 异步刷盘 + Broker 宕机 | 改为同步刷盘 flushDiskType=SYNC_FLUSH(牺牲吞吐) |
| ③ 投递环节 | 主从异步复制 + Master 宕机后 Slave 没有全部数据 | 使用 Dledger(Raft 过半提交)或同步复制 brokerRole=SYNC_MASTER |
| ④ 消费环节 | Consumer 自动确认 + 业务异常未处理 | Consumer 改为手动确认模式;或集群消费重试 16 次后进入死信队列 |
发送端保障(同步发送 + 重试):
// Producer 同步发送 + 结果检查
SendResult result = rocketMQTemplate.syncSend("order-topic", message);
if (!result.getSendStatus().equals(SendStatus.SEND_OK)) {
log.error("消息发送失败: {}", result);
// 写入本地失败表,定时补偿
failedMessageMapper.insert(order.getId(), message, result);
}
消息重复消费——幂等性设计方案
RocketMQ 的至少一次(At-Least-Once)语义意味着消息可能被重复消费。白歌为团队设计了三种幂等方案。
方案一:数据库唯一索引去重
-- 在业务表中使用消息 ID 做唯一约束
CREATE TABLE order_event (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
msg_id VARCHAR(128) NOT NULL,
order_id BIGINT NOT NULL,
status VARCHAR(32),
UNIQUE KEY uk_msg_id (msg_id) -- 消息 ID 唯一,重复抛异常
);
@Bean
Consumer<Message<Order>> input() {
return message -> {
String msgId = message.getHeaders().get("ROCKET_MQ_MESSAGE_ID");
try {
orderEventMapper.insert(msgId, message.getPayload());
} catch (DuplicateKeyException e) {
log.warn("消息重复消费,已跳过: {}", msgId);
}
};
}
方案二:Redis SETNX(原子性不存在则写入)
String msgKey = "msg:dedup:" + msgId;
Boolean success = redisTemplate.opsForValue()
.setIfAbsent(msgKey, "1", Duration.ofHours(24));
if (Boolean.FALSE.equals(success)) {
log.info("消息已处理,跳过");
return;
}
// 执行业务逻辑...
方案三:消息 ID + 业务状态查重
// 先查业务状态,已处理则跳过
if (orderService.isAlreadyProcessed(orderId)) {
log.info("订单已处理,跳过: {}", orderId);
return;
}
orderService.process(order);
故障排查速查表
| 现象 | 优先排查组件 | 典型根因 |
|---|---|---|
| 所有服务调用失败 | Nacos | Nacos 集群不可用或网络不通 |
| 部分服务调用失败 | Nacos | 服务实例不健康、Group 不匹配 |
| 请求被拒绝返回 429 | Sentinel | 流控/熔断规则触发 |
| 订单创建成功但库存没扣 | Seata 或 RocketMQ | 全局事务回滚失败 / 消息丢失 |
| 消息发了但消费者没收到 | RocketMQ | Consumer Group 不同、Tag 过滤不匹配、消费堆积 |
| 系统响应越来越慢 | Sentinel + JVM | 未配置限流导致线程池耗尽 |
| 配置改了没生效 | Nacos Config | 未加 @RefreshScope、Data ID 不匹配 |
| 服务重启后规则丢失 | Sentinel | 规则未持久化到 Nacos |
面试体系化梳理
白歌在帮助团队几位同事准备面试时,系统整理了以下考点。这些内容既是对前面八章的复习提纲,也是面试的高频提问方向。
组件级面试
Nacos
Q1:Nacos 的 AP/CP 双模式是什么意思?各自适用什么场景?
- AP 模式(Distro 协议,默认):优先保证可用性(Availability)和分区容错性(Partition Tolerance),牺牲强一致性。适用于服务注册与发现——宁可短暂不一致(查到已下线的实例),也不能完全不返回实例。
- CP 模式(Raft 协议):优先保证一致性(Consistency)和分区容错性,牺牲可用性。适用于配置管理——配置数据的强一致性必须保障,Leader 选举期间短暂不可写。
- 可以在同一个 Nacos 集群中共存:临时实例走 AP,持久化实例和配置走 CP。
Q2:Nacos 1.x 与 2.x 的通信协议有何变化?
| 维度 | 1.x | 2.x |
|---|---|---|
| 服务注册/心跳 | HTTP 短连接(5s 一次心跳请求) | gRPC 长连接(双向流,单 TCP 连接复用) |
| 配置推送 | HTTP 长轮询(Long Polling,30s 超时) | gRPC 双向流(同一个长连接即推送) |
| 连接数 | 每个实例 1 个 HTTP 心跳连接 + 1 个长轮询连接 | 每个实例 1 个 gRPC 连接(复用) |
| 端口 | 8848 | 8848(HTTP)+ 9848(gRPC) |
Q3:Nacos 的保护阈值是什么?触发后会发生什么?
保护阈值(Protection Threshold)是一个 0-1 之间的浮点数。当健康实例占比低于保护阈值时,Nacos 触发保护模式:不再剔除任何实例(即使是不健康的),同时返回所有实例(健康 + 不健康)给消费者,由消费者自行判断。
为什么需要保护模式? 防止因网络波动导致大量实例被误剔——比如 10 个实例中 5 个因网络短暂不通被标记不健康,如果此时剔除这 5 个,剩余 5 个直接被流量冲垮,引发雪崩。
Sentinel
Q1:Sentinel 的 Slot Chain(插槽链)用了什么设计模式?核心 Slot 有哪些?
使用责任链模式(Chain of Responsibility Pattern)。每个 Slot 有明确的职责,按固定顺序执行,支持通过 SPI(Service Provider Interface)机制扩展。
核心 Slot 及其职责:
| Slot | 职责 |
|---|---|
NodeSelectorSlot | 构建调用链路树,记录资源间调用关系 |
ClusterBuilderSlot | 构建 ClusterNode 聚合同一资源所有链路的指标 |
StatisticSlot | 核心统计插槽:记录 QPS / RT / 异常数 |
FlowSlot | 流量控制判定 |
DegradeSlot | 熔断降级判定 |
AuthoritySlot | 黑白名单授权 |
SystemSlot | 系统自适应保护 |
Q2:LeapArray 滑动窗口的底层原理是什么?
LeapArray 是基于环形数组 + 滑动窗口的统计算法。以默认配置(1s 周期 / 2 个窗口)为例:
- 环形数组有 2 个槽位,每个槽位对应 500ms 的时间窗口
- 当前时间通过
currentTimeMillis / 500 % 2定位当前槽位 - 每 500ms 窗口滑动一次,最旧槽位被重置复用
- 统计时累加所有槽位数据,覆盖最近 1s 的精确窗口
为什么不用简单计数器? 简单计数器在跨秒边界精度崩溃——前一秒最后 100ms + 后一秒前 100ms 如果都计数,合并计算会翻倍。滑动窗口不存在此问题。
Q3:四种流控效果的区别?
| 效果 | 行为 | 适用场景 |
|---|---|---|
| 快速失败(Fast Fail) | 直接抛出 FlowException | 秒杀、对超出流量直接拒绝 |
| Warm Up(预热) | 阈值从 阈值/coldFactor 逐步升至设定值 | 服务刚启动时逐步放开流量 |
| 排队等待(Rate Limiter) | 请求以恒定速率通过,多余的排队等待 | 需要流量整形的场景(如外部 API 调用) |
| Warm Up + 排队等待 | 预热 + 恒定速率 | 冷启动 + 流量整形的综合场景 |
Seata
Q1:AT 模式二阶段的"二阶段"与 XA 的"二阶段"有何本质区别?
| 维度 | AT 模式 | XA |
|---|---|---|
| 一阶段 | 直接提交了本地事务(释放数据库行锁),同步保存 UNDO LOG | 仅 Prepare 本地事务,数据库行锁一直持有 |
| 二阶段 | 全局提交:异步删除 UNDO LOG;全局回滚:根据前镜像反向补偿 | 全局提交:正式提交本地事务(释放行锁);全局回滚:回滚本地事务 |
| 锁持有时间 | 短(一阶段提交后释放) | 长(直到二阶段完成后才释放) |
| 性能 | 高 | 低(长事务锁持有影响并发) |
关键差异:AT 模式在一阶段就已提交本地事务,锁持有时间远短于 XA。代价是如果二阶段回滚前发生了脏写(其他事务修改了同一行数据),回滚会失败。
Q2:TCC 模式的三大核心问题是什么?如何解决?
| 问题 | 场景 | 解决方案 |
|---|---|---|
| 空回滚 | Try 未执行(网络超时),但 Cancel 却到达 | 在 Cancel 中查询 Try 是否执行,未执行则插入空回滚记录直接返回 |
| 悬挂 | Cancel 先于 Try 到达(Cancel 被重发绕过 Try) | Try 执行前检查是否有空回滚记录,有则阻止 Try 执行 |
| 幂等 | 同一个 Try/Confirm/Cancel 被重复执行 | 用事务控制表的状态字段做幂等判断(如 TRY→CONFIRM→CANCEL 状态流转) |
Q3:Seata 的全局锁机制如何工作?
全局锁存储在 lock_table 中,键为 row_key(resourceId + tableName + pk)。一阶段提交前 RM 向 TC 申请全局锁:
- 无冲突 → 插入
lock_table记录,申请成功 - 有冲突 → 等待重试(默认 30 次,间隔 10ms)→ 超时抛
LockConflictException
全局锁的作用是防止多个全局事务并发修改同一行数据导致的脏写问题。事务完成后 RM 通知 TC 释放锁。
RocketMQ
Q1:RocketMQ 事务消息的"半消息"机制是如何工作的?
事务消息分为三个阶段:
1. Producer 发送半消息(Half Message)→ Broker
半消息对 Consumer 不可见(不会投递)
2. Producer 执行本地事务
- 成功 → 向 Broker 发送 COMMIT → 半消息变为可见,投递给 Consumer
- 失败 → 向 Broker 发送 ROLLBACK → 半消息被删除
3. 如果 Broker 未收到二次确认(网络问题/Producer 宕机)→ Broker 发起回查
→ Producer 的 checkLocalTransaction() 返回最终状态
Q2:RocketMQ 如何保证分区顺序消息?
- 生产者端:发送时指定
hashKey(如订单 ID),RocketMQ 按hashKey取模将消息路由到固定的 MessageQueue - Broker 端:同一 MessageQueue 内消息按写入顺序存储(FIFO)
- 消费者端:Consumer 使用有序消费模式(ORDERLY),同一 MessageQueue 同一时刻只被一个线程消费
Q3:RocketMQ 如何保证消息可靠性?
逐层保障:
| 层级 | 保障机制 |
|---|---|
| Producer | 同步发送 + 失败重试(默认 2 次) |
| Broker 写入 | 同步刷盘(SYNC_FLUSH):写入 Page Cache 后还强制 fsync 落盘 |
| Broker 复制 | 同步复制(SYNC_MASTER)或 Dledger(Raft 过半提交) |
| Consumer | 手动确认 + 消费重试(默认 16 次)→ 死信队列兜底 |
架构级面试
Q1:Spring Cloud 原生方案 vs Spring Cloud Alibaba 全家桶如何选型?
从四个维度展开分析:
1. 组件功能维度
| 需求 | 原生方案 | Alibaba 方案 | 建议 |
|---|---|---|---|
| 注册 + 配置 | Eureka + Config + Bus(三个组件拼接) | Nacos(一个组件搞定) | Alibaba |
| 流控/熔断 | Hystrix(停更) | Sentinel(规则更丰富、Dashboard 可视化) | Alibaba |
| 分布式事务 | 无原生方案 | Seata(AT/TCC/Saga/XA 全覆盖) | Alibaba |
| 消息驱动 | Spring Cloud Stream + RabbitMQ / Kafka | Spring Cloud Stream + RocketMQ(事务消息、延迟消息原生支持) | 按需,阿里系选 RocketMQ |
2. 项目活跃度维度
- Netflix OSS 系列(Eureka、Hystrix、Ribbon)已相继停更或进入维护模式
- Alibaba 系列由阿里巴巴持续维护,双十一核心链路验证,迭代活跃
- Sentinel GitHub Star 数远超 Hystrix
3. 国产化与合规维度
- 全部为中国主导的开源项目(归属 Apache 或阿里巴巴),中文文档完善
- 符合信创(信息技术应用创新)政策导向
- 阿里云 MSE 企业版提供商业兜底支持
4. 生态整合维度
- Alibaba 全家桶内部深度整合:Nacos 同时作为 Sentinel 规则持久化数据源、Seata TC 注册中心、RocketMQ 可选服务发现
- 四个中间件(Nacos + Sentinel + Seata + RocketMQ)覆盖全部 MSA 需求,减少运维组件数量
面试加分回答:如果面试官追问"有没有场景适合继续用 Spring Cloud 原生方案",可以说:"存量系统中,如果已深度定制了 Eureka 的客户端行为、或者团队对 Hystrix 的参数调优非常有经验,迁移成本可能高于收益。但对于新项目或即将重构的系统,Alibaba 方案更全面、更活跃。"
Q2:如何设计一个高可用的微服务治理体系?
从五个维度构建完整方案:
1. 注册中心高可用
Nacos 3 节点集群 + MySQL 集群
├── 服务发现:AP 模式(Distro),保证可用性
├── 配置管理:CP 模式(Raft),保证一致性
└── 本地快照:Nacos 宕机时各客户端本地缓存兜底
2. 配置动态刷新
Nacos Config + @RefreshScope
├── 配置集中管理 + 版本回滚
├── 敏感信息加密(Jasypt / Vault)
└── 灰度配置:按标签向指定实例推送
3. 熔断降级
Sentinel + Dashboard
├── 流控规则:QPS 限流 + 排队等待 + Warm Up 预热
├── 熔断降级:慢调用比例 / 异常比例 / 异常数
├── 系统保护:Load / CPU / RT 维度自适应保护
├── 集群流控:Token Server 统一计数
└── Feign 整合:降级方法 FallbackFactory
4. 分布式事务
Seata AT 模式(默认)或 TCC(高性能场景)
├── undo_log 表 + 全局锁 table
├── TC 高可用(Seata Server 集群注册到 Nacos)
└── 事务日志定期清理(global_table / branch_table / lock_table)
5. 监控告警
SkyWalking(链路追踪)+ Prometheus(指标采集)+ Grafana(可视化)
├── 自动采集 Nacos/Sentinel/Feign/RocketMQ 调用链
├── Sentinel 指标 PromQL 查询 + Grafana 仪表盘
└── P0~P3 四级告警:电话 → 短信 → 企业微信 → 静默
Q3:单体迁移微服务的演进路线图
各阶段关键决策:
| 阶段 | 典型动作 | 核心技术 | 适用范围 |
|---|---|---|---|
| 单体 | — | Spring Boot 单应用 + 单数据库 | 创业初期、团队 < 5 人 |
| 垂直拆分 | 前后端分离 + 抽取公共模块 | Spring Cloud Gateway + 共享 JAR | 团队 10 人左右 |
| 服务化 | 核心业务拆分为独立微服务 | SCA 全家桶(Nacos + Sentinel + Seata + RocketMQ) | 团队 20+ 人,多业务线 |
| 网格化 | 治理能力下沉 Sidecar | Istio / MSE 服务网格 + Kubernetes | 团队 50+ 人,多语言/跨国部署 |
白歌的忠告:不要一上来就上网格化——大多数公司永远停在服务化阶段就够了。单体→服务化是质的飞跃,服务化→网格化只是运维模式的微调。
场景题
场景1:双十一大促——如何用 Sentinel + RocketMQ 扛住瞬时流量峰值?
问题:双十一零点的流量是平时的 50 倍,如何保证核心下单链路不雪崩?
设计方案:
用户请求
│
▼
Gateway(Sentinel 网关流控)
│ QPS = 10,000(集群 Token Server 统一计数)
│ 超出 → HTTP 429 "活动太火爆,请稍后再试"
▼
下单服务(Sentinel 流控)
│ QPS = 5,000
│ 排队等待模式(Rate Limiter)
│ 超出 → 返回降级提示
▼
下单业务逻辑
│ ① 校验库存(Redis 缓存预扣)
│ ② 发送 RocketMQ 事务消息(半消息)
│ ③ 写订单 DB
│ ④ 提交事务消息
▼
RocketMQ(削峰填谷)
│ Broker 暂存消息
│ Consumer 匀速消费(库存真实扣减、积分发放)
▼
异步消费者
├── 库存服务:真实扣减 DB 库存
├── 积分服务:发放积分
└── 通知服务:发送短信/推送
关键设计点:
| 设计点 | 方案 |
|---|---|
| 前端分流 | 商品详情页静态化 + CDN;下单按钮点击后显示"排队中" |
| 网关限流 | Sentinel Gateway 流控,集群 QPS = 10,000,超出直接 429 |
| 业务限流 | 下单接口排队等待(Rate Limiter),平滑流量而非拒绝 |
| 削峰 | 不直接调库存/积分服务,而是发 RocketMQ 消息异步处理 |
| 库存预热 | Redis 缓存库存计数,减少 DB 压力;下单时 Redis 先扣,异步更新 DB |
| 熔断兜底 | 库存服务慢调用比例 > 30% → Sentinel 熔断 → 走排队重试 |
| 降级方案 | 积分服务不可用 → 降级为"积分稍后到账"通知 |
场景2:跨机房部署——Nacos 多数据中心 + 就近路由
问题:飞翔科技业务扩展到全国,需要在北京、上海、广州三个数据中心部署微服务。用户如何就近路由?
方案:Nacos 多数据中心就近路由
北京数据中心 上海数据中心 广州数据中心
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Nacos-1 │◄────────►│ Nacos-2 │◄────────►│ Nacos-3 │
│ (Raft CP) │ 跨机房 │ (Raft CP) │ 跨机房 │ (Raft CP) │
└──────────────┘ 复制 └──────────────┘ 复制 └──────────────┘
│ │ │
服务实例 服务实例 服务实例
cluster: BJ cluster: SH cluster: GZ
配置路由规则(Spring Cloud LoadBalancer):
- 北京用户 → 优先路由到 cluster=BJ 的实例
- 北京实例全挂 → 降级路由到 cluster=SH
- 心跳就近上报(实例只向本地 Nacos 节点发心跳)
关键配置:
spring:
cloud:
nacos:
discovery:
server-addr: local-nacos.bj.datacenter:8848 # 就近连接本地 Nacos
metadata:
cluster: BJ # 集群标记
cluster-name: BJ
场景3:分布式事务选型——什么场景用 AT、什么场景用 TCC、什么场景用可靠消息?
白歌画了下面这张决策图:
┌── 需要强一致实时回滚?
│
┌────┴────┐
│ 是 │ 否(可接受最终一致)
└────┬────┘ │
│ ▼
┌────┴────┐ 使用可靠消息最终一致性
│ 业务代码 │ (RocketMQ 事务消息)
│ 能改造? │ 示例:积分发放、通知推送、日志
└────┬────┘
│
┌────能───┴───不能(或数据库交互复杂)───┐
│ │
▼ ▼
TCC 模式 AT 模式
示例:金融转账 示例:电商下单扣库存
(需要显式冻结/解冻资源) (基于关系型数据库的微服务)
| 场景 | 推荐模式 | 原因 |
|---|---|---|
| 电商下单(订单+库存+账户) | AT 模式 | 基于关系型数据库,SQL 单一,代码零侵入 |
| 金融转账(冻结→扣款→解冻) | TCC 模式 | 需要显式资源冻结和确认,性能要求高 |
| 积分发放、通知推送 | 可靠消息最终一致性 | 非关键链路,允许短暂不一致 |
| 长流程订单(创建→支付→发货→完成) | Saga 模式 | 每个步骤独立提交,失败后正向补偿 |
学习路径与后续进阶
推荐学习顺序
白歌为团队制定的学习路径(同时也是本教程的章节阅读顺序):
阶段一:概述
└── 第 1 章:Spring Cloud Alibaba 概述与技术选型
├── 理解全家桶定位
└── 掌握版本体系
阶段二:基础设施
├── 第 2 章:Nacos 注册中心(服务发现基础)
└── 第 3 章:Nacos 配置中心(配置管理基础)
阶段三:稳定性
├── 第 4 章:Sentinel 流量控制(流量防护第一道防线)
└── 第 5 章:Sentinel 降级与熔断(防止级联雪崩)
阶段四:关键能力
├── 第 6 章:Seata 分布式事务(数据一致性保障)
└── 第 7 章:RocketMQ 消息驱动(异步解耦 + 削峰)
阶段五:综合实战
└── 第 8 章:综合案例:电商微服务全栈实战
└── 所有组件的整合验证
收尾:最佳实践
└── 第 14 章:最佳实践与面试考点(本章)
├── 生产部署架构
├── 监控整合 + 性能调优 + 故障排查
└── 面试体系化梳理
进阶方向
| 方向 | 核心技术 | 说明 |
|---|---|---|
| 云原生网关 | Higress | 阿里巴巴开源的云原生网关,基于 Istio + Envoy,支持 Wasm 插件扩展 |
| AI 微服务 | Spring AI Alibaba | 基于 Spring AI 框架,将通义千问等大模型作为微服务体系的一等公民 |
| 企业级微服务引擎 | 阿里云 MSE(Microservice Engine) | Nacos 企业版、全链路灰度、无损上下线、服务鉴权、云原生网关 |
| 可观测性 | SkyWalking + Prometheus + Grafana + ELK | 链路追踪 + 指标监控 + 日志聚合,三支柱全覆盖 |
| 容器化编排 | Kubernetes + KubeVela | 将所有 SCA 微服务容器化并编排部署 |
配套实践项目建议
白歌建议每位读者完成以下三个层次的实践:
Level 1:基础实践(必做)
- [ ] 搭建 Nacos 单机 Server + Spring Boot 服务注册/发现
- [ ] 使用 Nacos Config 实现配置动态刷新
- [ ] 搭建 Sentinel Dashboard + 配置 QPS 流控规则
- [ ] 编写 Seata AT 模式电商下单示例(含 undo_log)
Level 2:进阶实践(推荐)
- [ ] Nacos 3 节点集群 + MySQL 持久化部署
- [ ] Sentinel 集群流控(Token Server 模式)
- [ ] RocketMQ 事务消息 + 订单状态机
- [ ] TCC 模式金融转账示例(冻结→确认→解冻)
- [ ] 全链路压测(JMeter 模拟大促流量 + Sentinel Dashboard 观测)
Level 3:生产级实践(选做)
- [ ] SkyWalking + Prometheus + Grafana 全栈可观测性
- [ ] Nacos + Sentinel + Seata 规则全量持久化到 Nacos
- [ ] 多机房就近路由 + 全链路灰度发布
- [ ] Spring AI Alibaba 大模型接入 + Sentinel 保护
- [ ] Kubernetes 集群部署 + MSE 微服务引擎
本章小结:从生产部署架构到故障排查手册,从性能调优速查到面试体系化梳理,本章将前面八章的知识点整合为可落地的生产实践和面试指南。CTO 大翔看完后对白歌说了一句话:"这份文档,值三个架构师的年薪。"
教程完:感谢你学完 Spring Cloud Alibaba 独立教程。这是一个以飞翔科技团队故事串联的微服务全家桶教程,从概述到最佳实践,覆盖了 Nacos、Sentinel、Seata、RocketMQ 四大核心组件的完整学习路径。预祝你在微服务架构的道路上越走越远。
[memory_id: memory_00_8u9RKB0pxBPUy0Bm0zxz0289]