menu

分布式中间件QQ

  • date_range 23/01/2023 21:37
    点击量:
    info
    sort
    网随云动
    label
    aPAAS
    iPAAS
    IaaS
    SaaS
    CNCF

技术组件 × 场景 全覆盖速查手册

用途:面试被问”X 组件你怎么用的、什么场景”时,30 秒内能给出场景 + 数据 + 踩坑 原则:每个组件至少有 1 个具体场景锚点(不能只说”我用过”) 适配:Operator 增强版简历的所有技术名词


答题通用框架(每个组件套这个模板)

1. 场景 (一句话):  在 X 项目里用 Y 组件解决 Z 问题
2. 关键参数:        我设的核心参数是什么 + 为什么这么设
3. 数据 / 效果:     量化的指标(带场景限定)
4. 踩坑:            一个真实故障 + 修复方案
5. 选型理由:        为什么选它不选 A/B

一、消息队列类

1.1 Kafka(简历主推)

维度 内容
场景 backbone-controller OAM 事件总线,日均 5 亿事件
角色 异构事件统一总线(OAM 监控 / Pod 生命周期 / 拓扑变化)
关键参数 50 个 Topic / 单 Topic 8 分区 / 副本 3 跨 AZ / acks=all + min.insync.replicas=2 / enable.idempotence=true
设计模式 业务域为骨架 + 分区 key=tenantId / 大租户动态预分裂
数据 集群总分区 < 500 / 单 Topic 8 分区 / 7 天保留
踩坑 早期没设 min.insync.replicas,ISR 缩到 1 时 acks=all 等于 acks=1,Leader 切换丢消息;后强制 ≥2
EO 方案 没用 Kafka 原生事务(降吞吐 20%);用幂等 Producer + Redis SET NX + DB UNIQUE KEY 三层兜底
30 秒答 “OAM 事件总线日均 5 亿,50 Topic 分区 key=tenantId,Acks=all+ISR≥2;EO 没用原生事务,用幂等键+去重表三层兜底,因为业务侧本就需要去重,Kafka 事务降吞吐 20% 不划算。”

1.2 RabbitMQ

维度 内容
场景 早期项目里小规模消息(< 1w QPS),解耦同步调用
常见问题 消费模型(Direct / Topic / Fanout / Header) / 持久化 / 镜像队列高可用
vs Kafka RabbitMQ 强一致 + 单消费 + AMQP 协议 / Kafka 高吞吐 + 消费组 + 自定义协议
30 秒答 “RabbitMQ 强项是 AMQP 标准协议 + 消费模型丰富,适合金融场景的强一致小消息;backbone 选 Kafka 是因为吞吐(5 亿/天)和回溯能力。RabbitMQ 我熟悉原理,生产用得少,主要是 Kafka 主导。”

1.3 RocketMQ

维度 内容
场景 RPA 项目用过事务消息,保障”任务下发 → 机器人执行 → 状态回写”链路最终一致
关键能力 事务消息(半消息 + 回查机制) / 顺序消息 / 延迟消息
vs Kafka RocketMQ 阿里系国产 + 事务消息原生支持 / Kafka 国际生态强 + 大数据集成好
30 秒答 “RPA 项目用 RocketMQ 事务消息——业务侧先写本地事务表,RocketMQ 半消息发出 + 回查;比 Kafka 自己实现幂等键 + 去重表对业务侵入小一点,但 RocketMQ 集群运维成本高。”

二、缓存类

2.1 Redis(简历主推)

维度 内容
场景 backbone 拓扑缓存 / 配置缓存 / 分布式锁 / 限流;Elevate 多租户配置 + 会话存储
部署模式 Codis(早期)→ Sentinel(中期,踩过坑)→ Redis Cluster(现在)
关键参数 maxclients / maxmemory-policy=allkeys-lru / 主从异步复制
数据 跨机房 RTT 30-50ms / 单实例上限 ~10w QPS / 集群 6 节点扛 ~30w QPS
踩坑 1 连接池默认 200 在 5w QPS 下耗尽 → 改 2000 + minIdle 100
踩坑 2 K8s StatefulSet 部署 Redis,节点漂移 + Sentinel 失协导致 60s 双 master 数据丢失 → 回退独立部署
30 秒答 “Redis 主要场景三块:多级缓存 / 分布式锁 / 限流。踩过两个坑——连接池默认 200 在 5w QPS 耗尽,后改 2000 + minIdle 100;K8s 上跑 Redis 主从切换异常,后回退独立部署,K8s 主要承载无状态业务。”

2.2 Redisson

维度 内容
场景 DetnetResourcePool 资源预留防超卖 / 配置版本编排 / 缓存击穿互斥锁
关键能力 可重入锁 / 信号量 / RRateLimiter / 延迟队列 / Pub/Sub
Watchdog 不传 leaseTime 时启动,Netty HashedWheelTimer 每 10s 续期 30s
失效场景 ① 显式 leaseTime;② 长 GC > 10s;③ 主从异步复制丢锁
真兜底 Fencing Token(每次加锁 epoch+1,下游识别过期持锁者)
30 秒答 “Redisson 用 Lua 把’加锁+过期+重入’原子化,Watchdog 用 HashedWheelTimer 每 10s 续期。但 GC、主从异步、显式 leaseTime 都可能失效——真兜底是 Fencing Token,下游识别过期持锁者。资源池场景我们用三层锁:Redisson + DB FOR UPDATE + 业务校验。”

2.3 Caffeine

维度 内容
场景 应用本地缓存(L1),配 Redis(L2)做多级缓存
关键参数 maximumSize / expireAfterWrite / recordStats / Window TinyLFU 算法
vs Guava Cache Caffeine 性能 5-10x;基于 W-TinyLFU 命中率高;Java 8+ 推荐
踩坑 早期用 ConcurrentHashMap 当缓存没设上限,导致 Old Gen 持续上涨 Full GC;改 Caffeine + maximumSize=10w + expireAfterWrite(1h)
30 秒答 “Caffeine 是本地缓存默认选择,基于 W-TinyLFU 命中率比 LRU 好。生产 PereDoc 项目用 Caffeine + maximumSize + expireAfterWrite 修复过一个 ConcurrentHashMap 无上限导致的 Full GC 问题——这事写进了 Code Review Checklist:任何 Map 作缓存必须设上限和过期。”

三、协调/注册中心

3.1 ZooKeeper(Curator)

维度 内容
场景 backbone 多 controller 实例 Leader 选举 / 配置中心 / 分布式锁(部分)
协议 ZAB(epoch + zxid 64 位 / Recovery + Broadcast 两阶段)
关键参数 sessionTimeoutMs=6000(默认 30s 太长) / connectionTimeoutMs=3000
选主原理 临时顺序节点 / 序号最小者为 Leader / 其他 Watch 前一个节点(避免羊群)
脑裂防御 ① 缩短 Session Timeout;② Fencing Token(终极武器);③ 业务侧 verify + System.exit
踩坑 Session Timeout 30s 时主控网络分区 ZK 还能心跳成功 → 双主 1 分钟 → 引入 fencing 后归零
30 秒答 “backbone 用 ZK 选主,基于 ZAB——临时顺序节点 + 最小序号者为 Leader。脑裂防御三层:Session 6s + Fencing Token + 业务侧 verify。Fencing 是终极武器,即使旧 Leader 没感知降级,下游 epoch 校验也能拒绝它的写入。”

3.2 Nacos

维度 内容
场景 Elevate-SaaS 多租户配置中心(按 tenantId 分 Namespace) / 服务注册发现
关键能力 配置秒级推送(长轮询 30s) / 多环境隔离 / 灰度发布
vs Apollo Nacos 一体化(配置+注册);Apollo 配置更专 + 灰度强
vs ZK Nacos 选 AP(可用性优先);ZK 选 CP;配置中心通常选 AP 比 CP 合适
30 秒答 “Elevate 用 Nacos 做多租户配置中心 + 服务注册一体化,按 tenantId 分 Namespace 实现租户级配置隔离 + 灰度推送。配置中心选 AP 不选 CP——配置读取 > 写入,部分节点失联仍能读本地缓存。”

3.3 etcd

维度 内容
场景 TenantOperator/DetNetController/VMPoolOperator 隐含使用——所有 K8s ApiServer 后端
协议 Raft(强 Leader + 随机选举 + 日志连续)
关键能力 mvcc 版本(直接对应 ResourceVersion) / Watch + 长连接 / Range 查询
vs ZK etcd 大数据量 KV / Go 优先 / K8s 生态;ZK 协调原语 / Java 生态
30 秒答 “Operator 不需要自己引入一致性组件——K8s ApiServer 后端就是 etcd。Reconcile 本质是’对 etcd 状态做对账’,ResourceVersion 直接是 etcd mvcc revision。所以 TenantOperator 隐含用了 Raft。”

四、RPC 框架

4.1 Dubbo

维度 内容
场景 内部微服务调用(部分历史模块);新模块多用 OpenFeign
核心机制 SPI 扩展 / 负载均衡(随机/轮询/最少活跃)/ 服务治理(限流/熔断)
协议 Dubbo 协议(单一长连接 + NIO)默认;支持 gRPC / Hessian
30 秒答 “Dubbo 早期模块用,核心机制是 SPI 扩展点 + 多种负载均衡;新模块倾向 OpenFeign(更贴 Spring Cloud)。Dubbo 在性能(单一长连接)和服务治理上更强,适合公司级中台架构。”

4.2 Spring Cloud OpenFeign

维度 内容
场景 backbone 30+ 微服务间同步调用(查询为主)
关键能力 声明式 HTTP / 集成 Sentinel 熔断 / 集成 Sleuth 链路追踪
30 秒答 “OpenFeign 优点是声明式 HTTP + Spring 生态深度集成,缺点是 HTTP 比 Dubbo 长连接性能略低。backbone 同步调用大部分用 Feign + Sentinel 熔断,异步调用走 Kafka。”

4.3 gRPC

维度 内容
场景 部分跨语言场景(Java ↔ Go);南向部分协议
vs Feign gRPC HTTP/2 + Protobuf 性能高 / 跨语言;Feign 仅 Java 但生态深
30 秒答 “gRPC 主要在跨语言场景用,VMPoolOperator 的 Go 进程跟 Java 控制器之间走 gRPC。优点是 HTTP/2 + Protobuf 紧凑高效;缺点是调试比 HTTP 麻烦,需要工具。”

五、Java 核心 + JVM

5.1 Disruptor

维度 内容
场景 1 PereDoc DICOM 影像分发,单节点压测峰值 ~10w QPS(压测口径)
场景 2 RPA Flowable 二次开发,任务派发用 Disruptor 替代 ABQ
关键设计 RingBuffer + Sequence + CAS / 缓存行填充防伪共享 / 预分配对象
WaitStrategy BusySpin(占满核) / Yielding / Blocking
数据 ABQ 800μs/条 → Disruptor 80μs/条(锁竞争消除)
水分认知 10w QPS 是压测,生产实际千级;Disruptor 在大文件场景只占链路 20-30% 优化
30 秒答 “Disruptor 替代 ABQ 在 PereDoc 把单节点峰值从 8w 拉到 10w+,但这是压测口径——生产实际千级足矣。Disruptor 真正消除的是锁竞争(ABQ ReentrantLock 上下文切换 1-3μs),大文件场景下 Netty 零拷贝才是延迟大头。”

5.2 ThreadPoolExecutor / JUC

维度 内容
场景 业务异步任务 / Reconcile 线程池 / Kafka Consumer 多线程
关键参数 corePoolSize / maxPoolSize / keepAliveTime / 拒绝策略(CallerRuns 推荐)
AQS state + CLH 队列 + park/unpark;ReentrantLock(独占) / CountDownLatch / Semaphore(共享)
30 秒答 “ThreadPoolExecutor 我看场景配——CPU 密集 N+1,IO 密集 2N。AQS 是 JUC 基础,state + CLH 队列 + park/unpark 三件套;ReentrantLock 用 state 表重入计数,CountDownLatch / Semaphore 用 state 表剩余资源。99% 场景不要自定义 AQS——重入和 state 编码极易写错。”

5.3 G1 GC

维度 内容
场景 backbone 控制面服务,堆 16GB
关键参数 -Xms16g -Xmx16g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:G1HeapRegionSize=8M -XX:IHOP=45
5 步排查 jstat → jmap histo → Arthas ognl → jmap dump + MAT → 修复验证
真实案例 PereDoc Old Gen 持续 92% → 定位 ConcurrentHashMap 280w 条目 → 改 Caffeine + 上限
数据 Full GC 小时级 → 周级(PereDoc 业务场景)
30 秒答 “backbone 用 G1,核心参数 MaxGCPauseMillis=200 + IHOP=45 + Region=8M。线上排查 5 步:jstat 看 GC 频率 → jmap histo 找大对象 → Arthas ognl 定位代码 → jmap dump + MAT 看支配树 → 修复验证。PereDoc 一次 Full GC 排查就是这套流程,改 Caffeine 加上限和 TTL 修复。”

5.4 ZGC / CMS

维度 内容
场景 暂时没用 ZGC(评估过但 ARM 平台稳定性差 + 16GB 堆 G1 够用);CMS 已淘汰
ZGC 设计 染色指针 + 读屏障 / +15% 内存开销 / < 10ms STW
何时选 ZGC 堆 > 32GB / SLA < 50ms / JDK ≥ 15
30 秒答 “backbone 16GB 堆 G1 够用没上 ZGC——ZGC 优势是 < 10ms STW 但 +15% 内存,在我们 200ms SLA 场景性价比不高。CMS 已 deprecated,新项目不考虑。”

5.5 Arthas / jstack / jmap

维度 内容
场景 线上诊断 / Full GC 排查 / 死锁定位 / 内存泄漏
常用命令 dashboard / thread -n 5 / ognl(读取静态字段) / sc / sm / jad(反编译验证)
真实案例 Flowable 死锁回滚事故,Arthas thread -n 5 看到两个 Worker 互锁,定位到加锁顺序问题
30 秒答 “Arthas 是线上必备,dashboard 看实时 / thread -n 5 看 CPU 高的线程 / ognl 直接读静态字段值。Flowable 死锁那次就是 Arthas 看到 Worker 互锁,30 分钟定位根因。”

六、网络

6.1 Netty(简历主推)

维度 内容
场景 backbone 南向网关,千级 SDN 设备 Netconf/OpenFlow 长连接(压测 1.5K,生产略小)
核心设计 主从 EventLoopGroup / 池化 ByteBuf / FileRegion 零拷贝 / IdleStateHandler 心跳
零拷贝 PooledByteBufAllocator / CompositeByteBuf / FileRegion(sendfile) / DirectBuffer
关键参数 bossGroup=1, workerGroup=CPU*2 / SO_BACKLOG / SO_KEEPALIVE / TCP_NODELAY
踩坑 早期用 Unpooled.copiedBuffer 大量复制 → 改 PooledByteBufAllocator GC 时间下降 60%
30 秒答 “Netty 在南向网关用,千级 SDN 长连接(压测 1.5K)。主从 EventLoopGroup + 池化 ByteBuf + FileRegion 零拷贝;心跳用 IdleStateHandler。踩过 ByteBuf 没池化导致 GC 高的坑,改池化后 GC 时间下降 60%。”

6.2 HTTP/2 / gRPC

维度 内容
场景 gRPC 跨语言通信 / Spring Cloud Gateway HTTP/2
关键 多路复用 / 头部压缩 / 服务端推送

七、数据存储

7.1 MySQL + ShardingSphere

维度 内容
场景 RPA 平台 ACT_RU_JOB 表分库分表(按 processInstanceId hash) / Elevate-SaaS 多租户 schema
关键策略 分库分表 16×16 / Worker 分片亲和 / 跨分片用 Saga 不用 XA
慢 SQL 治理 慢日志阈值 100ms / EXPLAIN 看索引 / 必要时强制 index hint
vs MyCat ShardingSphere(JDBC 模式)无中间件 / 性能高;MyCat Proxy 模式运维方便
30 秒答 “RPA 项目 Flowable 表用 ShardingSphere 分 16×16,按 processInstanceId hash;Worker 按分片亲和拉取避免行锁。跨分片避免 XA,业务用 Saga;同一流程实例必然在同一分片,天然不跨分片。”

7.2 InfluxDB

维度 内容
场景 DetNetController OAM 时序数据(SLA 指标:时延/抖动/丢包)/ JVM 监控历史
关键参数 保留 7 天细粒度 + 90 天聚合 / Continuous Query 自动降采样
30 秒答 “DetNet 的 SLA 指标(时延/抖动/丢包)走 InfluxDB,7 天细粒度 + 90 天聚合。配 Continuous Query 自动降采样降低存储压力。”

7.3 Elasticsearch

维度 内容
场景 早期日志检索 / 全文搜索;曾从 Solr 切换
切换原因 社区可持续性 / ELK 生态集成 / 运维工具丰富
30 秒答 “ES 主要做日志检索 + 全文搜索。早期项目用 Solr 后切到 ES——耗时一个 Sprint;教训是技术选型必须评估 3 年期社区可持续性,不能只看当前功能。”

7.4 Druid / HikariCP 连接池

维度 内容
场景 业务侧 DB 连接池 / 监控 SQL
关键参数 maxActive / minIdle / maxWait=100ms / testOnBorrow=false(高并发别 ping)
踩坑 连接池上限按”峰值 QPS × 平均调用次数 × 平均耗时”估算,默认 200 在 5w QPS 必崩
30 秒答 “连接池上限必须按公式估——峰值 QPS × 平均调用次数 × 平均耗时,不能用默认值。Elevate-SaaS 一次活动 5w QPS 把 Redis 连接池(默认 200)耗尽,改 2000 + minIdle 100 解决。这事写进 Code Review Checklist。”

八、微服务治理

8.1 Sentinel

维度 内容
场景 应用 QPS 限流 / 熔断降级 / 网关入口流控
关键能力 接口级 / 租户级 / Hot Spot 热点参数 / 集群限流(中心 + 本地)
千万级 Sentinel + ClusterServer 中心配额下发 + 本地令牌桶(单机 200w QPS)
30 秒答 “Sentinel 限流我用接口级 + 租户级双维度。千万级 QPS 必走中心 + 本地架构——ClusterServer 每秒下发配额,本地令牌桶 < 100ns 延迟单机 200w QPS。Elevate-SaaS 早期纯 Redis 限流把 Redis 打到 80% CPU,改本地+中心后降到 15%。”

8.2 SkyWalking

维度 内容
场景 backbone 30+ 微服务链路追踪 / Reconcile 内调用全链路可见
关键 字节码增强(无侵入)/ 服务拓扑自动发现 / TraceId 跨服务传递
vs Jaeger SkyWalking 字节码增强 + 国产 + 拓扑分析强;Jaeger 协议标准 + 生态丰富
30 秒答 “backbone 用 SkyWalking,字节码增强无侵入 + 服务拓扑自动发现。30+ 微服务调用链路一眼可见,定位跨服务问题 MTTD 从 30 分钟降到 1-3 分钟。”

8.3 Spring Cloud Gateway

维度 内容
场景 Elevate-SaaS API 网关层(鉴权 / 限流 / 熔断 / 灰度路由)
vs Zuul 2 Gateway 基于 WebFlux 背压天然支持;Zuul 2 已不主推
30 秒答 “Elevate 用 Spring Cloud Gateway 是因为 WebFlux 背压天然 + Spring 生态深度集成。10w QPS 内 Java 网关够用,极致性能场景才考虑 Higress / Envoy。”

8.4 Dapr Sidecar

维度 内容
场景 Elevate-SaaS 中间件解耦,业务通过 localhost:3500 调 Pub/Sub / State / Service
真实反向决策 早期评估低估 Sidecar 成本(中小租户压力大)→ 大客户保留 + 中小 SDK 直连
30 秒答 “Dapr Sidecar 让中间件可声明式替换,业务调 localhost:3500 不绑定具体 MQ/Cache。但中小租户 Sidecar 资源开销 15%+ 不划算——这是简历明确的反向决策之一,改成大客户保留 + 中小 SDK 直连。”

九、容器 / 云原生

9.1 Docker / K8s

维度 内容
场景 全栈容器化部署
关键能力 StatefulSet(有状态)/ Deployment(无状态)/ Service / Ingress
HPA 基于自定义指标(Kafka Lag)扩缩容
30 秒答 “K8s 是基础栈,有状态用 StatefulSet,无状态用 Deployment。HPA 不要只用 CPU,要用业务指标(Kafka Lag / Redis QPS)——CPU 低不代表业务不忙。”

9.2 Helm

维度 内容
场景 中间件部署模板化 / TenantOperator 中调用 Helm Java SDK 管理租户模块
关键 Chart.yaml / values.yaml / Helm Java SDK(marcnuri)
30 秒答 “Helm 做中间件部署模板化,TenantOperator 里用 Helm Java SDK 给每个租户安装业务模块。每个 Tenant CR 触发对应 HelmRelease 创建。”

9.3 Kustomize

维度 内容
场景 多环境配置(dev / test / prod 用同一份基础 + overlay)
vs Helm Kustomize 无模板引擎(纯 patch);Helm 有 Go template 表达能力强
30 秒答 “Kustomize 适合多环境共用基础配置,patch 模式比 Helm 模板简单。我们生产 Helm + Kustomize 混用——Helm 管 Chart 版本,Kustomize 管环境差异。”

9.4 Argo CD / Flux GitOps

维度 内容
场景 Elevate-SaaS 配置变更可审计零停机发布
关键 Git 是 SoT(Source of Truth)/ 自动同步 / 回滚便捷
30 秒答 “Elevate 用 Flux GitOps 管中间件配置变更——Git 是真相之源,任何配置改动走 PR,Flux 自动同步到集群,审计可追溯。”

十、Operator 工具链(简历核心差异化)

10.1 JOSDK 5.x + Fabric8 6.x

维度 内容
场景 TenantOperator + DetNetController(Java 写)
核心能力 DependentResources DAG / EventSource 抽象 / Workflow + Condition
vs Operator SDK Java JOSDK 文档完整 + 社区活跃 / 选型 ADR-001
30 秒答 “TenantOperator 用 JOSDK 5.x + Fabric8 6.x,核心是 DependentResources 模型——声明式编排子资源 DAG,失败粒度细到单 Dependent 重试。比手写 Reconcile 代码减 40%。”

10.2 controller-runtime + kubebuilder

维度 内容
场景 VMPoolOperator(Go 写)
核心能力 Informer + Workqueue / Webhook 内置 / leader election 内置
vs JOSDK controller-runtime 更贴 K8s 生态 / Go 优先 / 启动 100ms
30 秒答 “VMPoolOperator 用 Go controller-runtime + kubebuilder,因为要直接调 libvirt 和 SR-IOV 这些 cgo 库——选型原则就一句话,业务逻辑重 Java、系统调用重 Go。”

10.3 CRD 五件套

维度 内容
5 件套 spec/status 分离(subresources.status) + observedGeneration 防漂移 + conditions 多维度 + Finalizer 反向清理 + PrinterColumns 运维体感
死循环坑 不写 observedGeneration → updateStatus 触发 Update 事件 → 又触发 Reconcile → 无限循环
30 秒答 “CRD 五件套是 P8 硬门槛:spec/status 分离 + observedGeneration 防漂移 + conditions 替代单一 phase + Finalizer 级联清理 + PrinterColumns 运维体感。少任何一件就不是合格的 K8s CRD。”

10.4 Webhook(Validating + Mutating)

维度 内容
场景 TenantOperator 校验 spec 合规 / 注入默认值
部署 单独 Service + cert-manager 签 TLS / API Server 拒绝阶段拦截
30 秒答 “Validating 在 API Server 阶段拒绝非法 spec,Mutating 注入默认值——比如 region 默认 cn-east、quota 按 level 注入。坏数据进不了 etcd,不需要 Reconcile 兜底。”

十一、分布式事务

11.1 TCC

维度 内容
场景 RPA 跨机器人执行的强一致场景
机制 Try / Confirm / Cancel 三阶段 / 业务侧自己实现补偿
30 秒答 “TCC 适合跨服务强一致(订单扣库存 + 扣款 + 创建发货单)。代价是业务侵入大,要自己实现 try/confirm/cancel 三阶段。RPA 项目用 TCC + Saga 混合。”

11.2 Saga

维度 内容
场景 backbone 配置下发”设备校验 → 配置推送 → 流量切换 → 回滚”链
机制 DAG 链 / 每节点独立补偿单元 / 任一失败反向链
30 秒答 “Saga 在 DynamicWorkChain 落地,DAG 拓扑融合 Saga 模式——每个节点既是执行单元又是补偿单元;失败时反向触发补偿,不需要全局事务。比 BPMN 流程引擎轻 5-10 倍。”

11.3 本地消息表 + RocketMQ 事务消息

维度 内容
场景 RPA”任务下发 → 机器人执行 → 状态回写”最终一致
机制 业务事务 + 消息表(同 DB)→ 异步发 MQ → 消费方消费 → 失败回查
30 秒答 “本地消息表是最常用的最终一致方案——业务和消息表写在同一个 DB 事务,异步发 MQ。RocketMQ 事务消息原生支持半消息 + 回查,比自己实现侵入小。”

十二、分布式 ID

12.1 Snowflake / Leaf / UidGenerator

维度 内容
场景 全局唯一 ID(订单/事件/请求 ID)
关键 时钟回拨处理(抛异常 / 等待 / 切 workerId)
选型 Leaf-Snowflake(依赖 ZK 分配 workerId)/ UidGenerator 性能更强
30 秒答 “backbone 用 Leaf-Snowflake 因为 ZK 已经在 / 分布式 workerId 协调天然有 ZK 帮忙。时钟回拨阈值 100ms 内等待,超过告警切备机。曾因 NTP + Pod 漂移导致机器 ID 复用,后改 ZK 持久顺序节点 + 启动校验。”

十三、可观测性

13.1 Prometheus + Grafana

维度 内容
场景 全栈监控 / 自定义指标 / 告警
关键 JMX Exporter / 服务发现自动注册 / Thanos 长期存储
30 秒答 “Prometheus 是默认监控栈,JMX Exporter 暴露 JVM 指标 / Burrow 监 Kafka Lag / redis_exporter 监 Redis。配 AlertManager + Grafana 大盘,backbone 一类典型告警 MTTD 从 30 分钟降到 1-3 分钟。”

13.2 ELK / Loki

维度 内容
场景 日志聚合 / 全文搜索
vs Loki ELK 完整但重;Loki 只索引 label 轻量
30 秒答 “ELK 是经典栈但重(ES 集群运维成本高)。Loki 只索引 label,日志体走对象存储,适合中小规模。”

13.3 Jaeger / SkyWalking

维度 内容
场景 链路追踪
选型 SkyWalking 国产 + 字节码增强(无侵入)/ Jaeger 协议标准开源生态广

十四、CI / CD

14.1 GitLab CI / Jenkins

维度 内容
场景 应用 + Operator 镜像构建 + 测试 + 发布
关键 多阶段 pipeline / 镜像分层 / 缓存

14.2 Argo CD / Flux

(已在 9.4 覆盖)


十五、特殊领域

15.1 Flowable BPMN

维度 内容
场景 RPA 项目工作流引擎,二次开发优化高并发
改造点 Disruptor 替 ABQ / 分库分表 / 乐观锁替部分悲观锁 / 责任链异步
数据 TPS 300 → 千级(受流程复杂度影响)
踩坑 上线首周一类锁顺序死锁,90 分钟现场回滚;改进:全局有序 ID 加锁 + ThreadMXBean 检测 + Chaos 压测
30 秒答 “Flowable 6.x 二次开发改了 4 个点:Disruptor 替 ABQ、分库分表、乐观锁替部分悲观、责任链异步化。TPS 300 → 千级。上线首周踩过死锁回滚,根因是异步并发后多资源加锁顺序不一致;修复方案是按全局有序 ID 加锁 + 死锁监控 + Chaos 压测。”

15.2 Netconf / OpenFlow / SRv6

维度 内容
场景 DetNetController 南向协议
关键 YANG 模型 / 协议适配层(XPath 按字段名解析,不依赖顺序)
踩坑 OEM 设备字段顺序不符 RFC → 客户现场配置失败 → 抽象 Adapter + CI 多厂商契约测试

答题速查 · 被问”X 你怎么用的”统一模板

【场景】我在 [项目名] 用 [组件],解决 [具体问题]。
【关键】我设的关键参数是 [参数] = [值],因为 [理由]。
【数据】[量化指标],带场景限定([压测口径 / 内部观测 / 业务场景限定])。
【踩坑】踩过一次 [坑],修复方案是 [方法],写进了 Code Review Checklist。
【方法论】所以我对 [组件] 的判断原则是 [一句话]。

紧急时刻 · “我没用过” 的 3 段式应对

被问到没真用过的组件时,绝不硬编:

1. 承认:"这块我没有深度生产实战。"
2. 类比:"但我做过类似的 X(简历真实经验),思路上应该是 ABC..."
3. 表态:"如果加入贵司,我会优先 1-2 周补这块——我学新组件惯用读官方文档 + 看一段源码 + 写个 demo 三步。"

绝对不要硬编场景——硬编一秒钟,被识破代价是整场失败。


今晚消化建议(下次面试前)

按”被问概率”排序,熟读以下顺序:

🔥 必熟(每个 5 分钟):

  1. Kafka(简历最重)
  2. Redis + Redisson
  3. ZooKeeper(选主 / 脑裂防御)
  4. Netty(零拷贝 / 主从 EventLoop)
  5. G1 GC + Arthas 排查
  6. CRD 五件套
  7. Disruptor(含水分认知)

🔵 次熟(每个 3 分钟):

  1. Sentinel(千万级架构)
  2. ShardingSphere(分库分表)
  3. SkyWalking(链路追踪)
  4. JOSDK + controller-runtime 选型
  5. Saga / TCC / 本地消息表
  6. Caffeine(防 Map 无上限坑)
  7. 连接池配置(Druid / Hikari / Jedis)
  8. Helm + Kustomize

⚪ 速读(每个 1-2 分钟,知道场景就行):

  1. RabbitMQ / RocketMQ
  2. Nacos / Consul / etcd
  3. Dubbo
  4. ZGC / CMS
  5. Argo CD / Flux
  6. ELK / Loki / Jaeger
  7. Snowflake / Leaf
  8. Dapr Sidecar
  9. Flowable

改进自我介绍 · 控制简历组件露出

下次面试前,自我介绍里只点出 3-5 个组件,把面试官引向你最熟的方向。

❌ 不要说:”我做过 Kafka / Redis / ZK / Netty / Disruptor / Caffeine / Spring Cloud / Dubbo / Sentinel / SkyWalking / K8s / JOSDK / Fabric8 / controller-runtime…“(信息过载,面试官随便挑一个考你)

✅ 改成:”主要在做中间件治理 + Operator 落地——Kafka / Redis / ZK / Netty 是日常打交道的几个“(只点 4 个,引导面试官追问这 4 个)。


核心方法论:简历宽 + 准备深 = 致命。简历宽 + 准备宽 = 才能扛住广度打。下次面试前用这份手册做一遍”30 秒每组件”演练,把宽度补齐。

组件深度补救 · 第 1 批 · Kafka 全景

用途:被问”Kafka 你怎么用的、结构是什么、调优过哪些参数”时,30/90/180 秒都能答出 P8 深度


一、结构组成(必画图,白板用)

                  ┌──────────────────────────────────┐
                  │      ZooKeeper / KRaft (元数据)   │
                  │  - 集群成员                        │
                  │  - Topic / Partition 元数据        │
                  │  - Controller 选举                 │
                  └──────────────┬───────────────────┘
                                 │
       ┌─────────────────────────┼──────────────────────────┐
       │                         │                          │
┌──────▼─────┐         ┌────────▼─────────┐       ┌────────▼────────┐
│ Producer   │         │  Broker 集群      │       │ Consumer Group   │
│            │         │                  │       │                  │
│ - 序列化   │         │ ┌─────────────┐  │       │ ┌──────────────┐ │
│ - 分区器   │  send   │ │ Controller  │  │ poll  │ │ Coordinator  │ │
│ - 累加器   ├────────→│ │ (1 broker)  │  ├──────→│ │              │ │
│ - 发送线程 │         │ └─────────────┘  │       │ └──────────────┘ │
│ - 幂等性   │         │ ┌─────────────┐  │       │ ┌──────────────┐ │
│ - 事务     │         │ │ Topic A     │  │       │ │ Consumer 1   │ │
│            │         │ │ ├ Partition0│  │       │ │ Consumer 2   │ │
│            │         │ │ │ └ Leader  │  │       │ │ Consumer N   │ │
│            │         │ │ ├ Partition1│  │       │ └──────────────┘ │
│            │         │ │ │ └ Follower│  │       └──────────────────┘
│            │         │ │ └ ...        │  │
│            │         │ └─────────────┘  │
│            │         │ ┌─────────────┐  │
│            │         │ │ Log Segment │  │
│            │         │ │ - .log      │  │
│            │         │ │ - .index    │  │
│            │         │ │ - .timeindex│  │
│            │         │ └─────────────┘  │
│            │         │ PageCache + mmap │
│            │         │ + sendfile       │
│            │         └──────────────────┘
└────────────┘

6 大角色:

  1. Producer — 生产消息
  2. Broker — 存储消息(集群中多台)
  3. Controller — Broker 之一,管元数据(KRaft 后由 Raft Leader 担任)
  4. Topic / Partition — 逻辑分类 + 物理分片
  5. Consumer — 消费消息
  6. ZK / KRaft — 元数据存储 + 选举

二、核心工作原理(逐层讲清)

2.1 Producer 端流水线

应用 send() 
    ↓
Serializer (序列化 key + value)
    ↓
Partitioner (决定写哪个 partition)
    - 默认: key.hashCode() % partitions
    - 无 key: Sticky(2.4+) 黏到当前 batch
    ↓
RecordAccumulator (本地缓存,按 partition 分组)
    - 默认 32MB(buffer.memory)
    - 每个 batch 16KB(batch.size)或 linger.ms 触发发送
    ↓
Sender Thread (后台线程)
    ↓
NetworkClient → Broker

关键参数: | 参数 | 含义 | 推荐值 | |——|——|——–| | acks | 持久化保证 | all(强一致) | | enable.idempotence | 幂等性 | true | | max.in.flight.requests.per.connection | 并发飞行请求 | ≤ 5(开启幂等时) | | retries | 重试次数 | Integer.MAX_VALUE | | compression.type | 压缩 | zstd(性价比最高) | | batch.size | 批量大小 | 16KB-65KB | | linger.ms | 凑批等待 | 5-50ms | | buffer.memory | 缓冲区 | 32MB-128MB |

2.2 Broker 端存储

Topic A → Partition 0 → 多个 Segment
                         ├── 00000000000000000000.log     消息日志
                         ├── 00000000000000000000.index   稀疏索引(每 4KB 一条)
                         ├── 00000000000000000000.timeindex 时间索引
                         ├── 00000000000123456789.log
                         └── ...

关键设计:

  • 顺序追加: 磁盘顺序写 ~600MB/s,比随机写快 100x
  • PageCache: 写走 OS PageCache,定期 flush;读靠 PageCache 命中(90%+)
  • mmap: 索引文件用 mmap,零拷贝读
  • sendfile: 消费者读取走 sendfile 系统调用,从磁盘 → Socket 直接,不进用户态
  • 批量 + 压缩: 解压在消费端,Broker 透明传递

数据保留 3 种策略: | 策略 | 触发 | |——|——| | log.retention.hours | 7 天默认 | | log.retention.bytes | 大小上限 | | log.cleanup.policy=compact | 同 key 只保留最新(KV 主题用) |

2.3 ISR / 副本同步

Leader (Partition 0)
    ↓ replicate
Follower 1 (sync 中)        ←  ISR (In-Sync Replicas)
Follower 2 (sync 中)        ←  ISR
Follower 3 (落后超过阈值)    ← OSR (Out-of-Sync)
                              落后阈值 = replica.lag.time.max.ms = 30s

ISR 缩水场景:

  • Follower 心跳超时
  • 网络分区
  • Follower 重启

min.insync.replicas=2 的含义:

  • 写入必须 ≥ 2 个副本同步成功
  • ISR 缩到 1 时 acks=all 会被 Broker 拒绝(NotEnoughReplicasException)
  • 这是防丢数据的最后防线

2.4 Consumer Group + Rebalance

Topic A 8 partitions
   ↓
Consumer Group (4 个 Consumer)
   - Consumer 1 → P0, P1
   - Consumer 2 → P2, P3
   - Consumer 3 → P4, P5
   - Consumer 4 → P6, P7

新 Consumer 加入 → Rebalance → 重新分配 partition

Rebalance 触发:

  • Consumer 加入/离开
  • Topic partition 数变化
  • subscribe 模式变化

3 种 Assignor: | Assignor | 行为 | |———-|——| | Range(默认) | 范围分配 | | RoundRobin | 轮询分配 | | CooperativeStickyAssignor(2.4+) | 增量再平衡,只移动必要 partition |

Static Membership(2.3+):

  • group.instance.id 显式设置
  • 重启 5min 内不触发 Rebalance
  • 减少不必要切换

三、Exactly-Once 三层兜底(简历主推方案)

┌────────────────────────────────────────┐
│ ① Producer 幂等性(单会话内去重)         │
│   enable.idempotence=true                 │
│   PID + Sequence Number                  │
└────────────────────────────────────────┘
              ↓
┌────────────────────────────────────────┐
│ ② Broker 副本一致性                     │
│   acks=all + replicas=3 + ISR≥2          │
└────────────────────────────────────────┘
              ↓
┌────────────────────────────────────────┐
│ ③ 业务侧 Redis SET NX + DB UNIQUE KEY   │
│   key = eventId + tenantId               │
└────────────────────────────────────────┘

为什么不用 Kafka 原生事务:

  • 降吞吐 20%(commit RPC + __transaction_state 持久化)
  • 事务边界跨 Kafka 到 DB 时业务还得做幂等
  • OAM 事件总线吞吐敏感,不划算

详见 Q2 详解。


四、监控指标(Prometheus 必抓)

指标 阈值 含义
kafka_consumer_lag < 1000 消费滞后
kafka_under_replicated_partitions == 0 ISR 缩水告警
kafka_request_handler_avg_idle_percent > 30% Broker 处理空闲度
kafka_network_request_avg_idle_percent > 30% 网络空闲度
kafka_log_size_bytes < 80% disk 磁盘使用
kafka_isr_shrinks_total rate < 1/min ISR 频繁收缩告警

工具:

  • Burrow(LinkedIn): Consumer Lag 专用监控
  • Cruise Control(LinkedIn): partition reassignment 自动化
  • Kafka Manager: 集群管理

五、backbone-controller 真实配置(简历锚点)

# Topic 设计
oam-event:
  partitions: 8
  replication.factor: 3
  config:
    min.insync.replicas: 2
    retention.ms: 604800000          # 7 天
    compression.type: zstd
    max.message.bytes: 1048576       # 1MB

# Producer
acks: all
enable.idempotence: true
max.in.flight.requests.per.connection: 5
retries: 2147483647
compression.type: zstd
batch.size: 65536
linger.ms: 50

# Consumer
group.instance.id: oam-consumer-{POD_INDEX}    # Static Membership
partition.assignment.strategy: CooperativeStickyAssignor
session.timeout.ms: 30000
max.poll.records: 500
isolation.level: read_committed

实战数据:

  • 50 个 Topic / 单 Topic 8 分区 / 集群总分区 < 500
  • 副本 3 跨 AZ
  • OAM 主题日均 5 亿事件
  • 日均吞吐 ~6k QPS / 峰值 ~5w QPS
  • Lag 监控 Burrow 阈值 1000

六、踩坑案例集

坑 1 · ISR 缩水时 acks=all 等于 acks=1

现象: 网络抖动 ISR 缩到 1,acks=all 仍写成功;Leader 切换后丢数据 根因: 没设 min.insync.replicas 修复: 强制 ≥ 2

坑 2 · Rebalance 风暴

现象: 部署/网络抖动每天发生 3-5 次 Rebalance,每次 OAM 停摆 4 分钟 根因: 全员 Revoke + Reassign 修复: ① Static Membership;② CooperativeStickyAssignor(增量再平衡);③ 消费侧异步化(避免 max.poll.interval.ms 超时)

坑 3 · 大消息导致 OOM

现象: 某些消息超 1MB,Producer 内存暴涨 修复: ① max.message.bytes 限制;② 业务侧预校验;③ 大消息拆分或上传 OSS 只发引用

坑 4 · Consumer 慢导致 Lag 上涨

现象: 消费跟不上,Lag 持续上涨 修复: ① 增加 Consumer 实例(不超过 partition 数);② 消费端异步化(拉取 → 丢入 Disruptor);③ 增加 partition 数(扩容上限)


七、Kafka vs RocketMQ vs Pulsar 选型

维度 Kafka RocketMQ Pulsar
吞吐 极高 极高
顺序消息 partition 内有序 消息组 同 Kafka
事务 支持(2 阶段) 半消息 + 回查 支持
延迟消息 不支持原生 18 个延迟级别 支持任意延迟
存算分离 KRaft + Tiered Storage(3.3+) 不原生 原生(BookKeeper)
生态 国际广 阿里系完整 新兴
运维 成熟 国产文档好 复杂(BookKeeper + ZK)

选型逻辑:

  • 互联网/通用: Kafka
  • 金融/事务/延迟: RocketMQ
  • 大规模 + 多租户 + 存算分离: Pulsar

八、面试 30 / 90 / 180 秒答题模板

30 秒(被问”Kafka 你用过吗”)

“用过,backbone-controller OAM 事件总线日均 5 亿条,50 Topic / 单 Topic 8 分区 / 副本 3 跨 AZ。Acks=all + ISR≥2 + 幂等 Producer + 业务侧 Redis SET NX 三层做 EO,没用原生事务——降吞吐 20% 不划算。踩过 ISR 缩水丢数据 + Rebalance 风暴两个坑,Static Membership + CooperativeStickyAssignor 解决。”

90 秒(被问”Kafka 整体架构讲讲”)

“Kafka 6 大角色:Producer / Broker / Controller / Topic+Partition / Consumer / ZK 或 KRaft 元数据。

Producer 端有序列化 → 分区器 → 累加器 → Sender Thread 流水线;开启幂等需要 acks=all + max.in.flight ≤ 5。

Broker 端核心是顺序追加日志(磁盘 600MB/s)+ 稀疏索引 + PageCache + mmap + sendfile 零拷贝——这些 OS 级优化才是高吞吐的本质,不是 JVM 调优。

副本机制靠 ISR——acks=all + min.insync.replicas=2 + replicas=3 三件套防丢数据;ISR 缩水时 Broker 拒绝写入是最后防线。

Consumer 端靠 Group Coordinator 协调;Static Membership + CooperativeStickyAssignor 大幅减少 Rebalance 风暴。

我在 backbone 50 个 Topic / 集群总分区 < 500 / 日均 5 亿事件运行稳定。”

180 秒(被问”Kafka 给我从头到尾讲一遍”)

(展开上面 + 补 EO + 监控 + 选型 + 踩坑——见上文各节)


九、面试官追问预案(高频)

Q · “Kafka 怎么实现高吞吐?”

“5 个 OS 级优化叠加:① 顺序追加(磁盘 600MB/s);② PageCache 命中 90%+;③ mmap 索引零拷贝读;④ sendfile 消费零拷贝;⑤ 批量 + 压缩。核心是 OS 优化不是 JVM 调优——Kafka 堆 6GB 就够,PageCache 才是主战场。”

Q · “ISR 是什么?什么时候缩水?”

“In-Sync Replicas,与 Leader 同步差距 < replica.lag.time.max.ms(30s)的副本集合。Follower 心跳超时 / 网络抖动 / Follower 重启都会缩水。ISR < min.insync.replicas 时 acks=all 被拒绝——这是防丢的最后防线。”

Q · “为什么开启幂等 max.in.flight 必须 ≤ 5?”

“Broker 只缓存最近 5 个 batch 的 Sequence Number 做去重。> 5 个 in-flight 乱序到达时,Broker 无法正确判断重复,会抛 OutOfOrderSequenceException。”

Q · “KRaft 比 ZK 好在哪?”

“① 去 ZK 化运维简化;② 元数据规模从十万级到百万级分区;③ 启动更快;④ 一致性模型更清晰(Raft)。但 KRaft 在 3.3+ 才 GA,老集群仍用 ZK。”

Q · “PageCache 命中怎么观察?”

“Linux vmstat 看 buff/cache;Kafka 启动后会观察 OS bi/bo 指标。如果 bi(读磁盘)持续高 → PageCache 命中差,要么内存不够要么消费太慢导致冷读。”

Q · “Topic 怎么设计分区数?”

“公式:消费者最大并发数 × 1.5。单 Broker < 4000 partition,集群总 partition 留 8x buffer。我在 backbone 单 Topic 平均 8 分区,集群总分区 < 500。”


十、贴墙速记

5 个数字:

  • 顺序写 600MB/s
  • min.insync.replicas = 2
  • max.in.flight ≤ 5
  • 单 Broker partition < 4000
  • PageCache 命中率 90%+

5 个关键词:

  1. 顺序追加 + PageCache + mmap + sendfile(高吞吐 4 件套)
  2. ISR 多数派(副本一致性)
  3. PID + Sequence(单会话幂等)
  4. Static Membership + CooperativeStickyAssignor(防 Rebalance 风暴)
  5. eventId + tenantId(业务侧幂等键)

1 个杀手句:

“Kafka 高吞吐的本质是 OS 级零拷贝,不是 JVM 调优。”

组件深度补救 · 第 1 批 · Redis 全景

用途:被问”Redis 数据结构 / 持久化 / 集群 / 调优”时,30/90/180 秒都能答出 P8 深度


一、结构组成(必背)

┌────────────────────────────────────────────────┐
│                Redis 单实例                     │
│  ┌──────────────────────────────────────────┐ │
│  │ 客户端连接池(默认上限 maxclients=10000)    │ │
│  └─────────────────┬────────────────────────┘ │
│                    │                            │
│  ┌─────────────────▼────────────────────────┐ │
│  │ 主线程(单线程处理命令)                     │ │
│  │ Network I/O (Redis 6+ 多线程)             │ │
│  │ Command Executor (单线程,无锁)             │ │
│  └─────────────────┬────────────────────────┘ │
│                    │                            │
│  ┌─────────────────▼────────────────────────┐ │
│  │ 数据结构层                                 │ │
│  │ ┌──────┬──────┬──────┬──────┬─────────┐ │ │
│  │ │String│ Hash │ List │  Set │ ZSet... │ │ │
│  │ │SDS   │压缩列│quicklist│ ht │ ziplist│ │ │
│  │ │      │表 ht │     │      │skiplist │ │ │
│  │ └──────┴──────┴──────┴──────┴─────────┘ │ │
│  └─────────────────┬────────────────────────┘ │
│                    │                            │
│  ┌─────────────────▼────────────────────────┐ │
│  │ 持久化                                     │ │
│  │ RDB (内存快照,异步 fork)                   │ │
│  │ AOF (命令日志,fsync 策略)                  │ │
│  │ 混合(4.0+,RDB + AOF)                      │ │
│  └─────────────────┬────────────────────────┘ │
│                    │                            │
│  ┌─────────────────▼────────────────────────┐ │
│  │ 主从复制                                   │ │
│  │ 增量(PSYNC + repl_backlog)                │ │
│  │ 全量(BGSAVE + RDB)                        │ │
│  └──────────────────────────────────────────┘ │
└────────────────────────────────────────────────┘

二、5 大数据结构 + 底层实现(高频考点)

2.1 String (SDS)

底层: Simple Dynamic String(简单动态字符串)

struct SDS {
    int len;        // 已用长度
    int free;       // 剩余空间
    char buf[];     // 字符数组(以 \0 结尾兼容 C)
}

优势 vs C 字符串:

  • O(1) 获取长度
  • 杜绝缓冲区溢出
  • 减少内存重分配(预分配 + 惰性释放)
  • 二进制安全

使用场景: 缓存对象 / 计数器 / 分布式锁(SETNX)

2.2 Hash

底层: 元素少时 ziplist(压缩列表);多时 hashtable 触发阈值: hash-max-ziplist-entries=128 / hash-max-ziplist-value=64

使用场景: 对象存储(用户信息) / Redisson 分布式锁(field=threadId, value=重入次数)

2.3 List

底层(3.2+): quicklist(双向链表 + ziplist) 老版本: ziplist(短)+ linkedlist(长)

使用场景: 消息队列(LPUSH + BRPOP) / 时间线 / 最近访问

2.4 Set

底层: 全是整数 → intset;否则 hashtable

使用场景: 去重 / 标签 / 共同好友

2.5 ZSet (有序集合)

底层: ziplist(短) / skiplist + hashtable(长)

  • skiplist 提供 O(log N) 范围查询
  • hashtable 提供 O(1) 单点查询

使用场景: 排行榜 / 滑动窗口限流 / 延迟队列(score=执行时间)


三、持久化(必懂)

3.1 RDB(内存快照)

触发: SAVE(同步,阻塞)/ BGSAVE(fork 子进程)/ 定时(save 900 1)
机制: fork 子进程 → 子进程遍历内存 → 写 dump.rdb 文件
优点: 文件紧凑 / 恢复快
缺点: fork 成本(大内存可能阻塞几百 ms)/ 数据丢失窗口大

3.2 AOF(命令追加日志)

触发: 每条写命令追加到 AOF 文件
fsync 策略:
  - always: 每条命令都 fsync(慢,但数据安全)
  - everysec: 每秒 fsync(默认,推荐)
  - no: 由 OS 决定(快,但风险大)

AOF 重写: BGREWRITEAOF 把命令日志合并为最小命令集

3.3 混合持久化(4.0+,推荐)

AOF 重写时:
  开头是 RDB 格式快照
  后面是 AOF 命令日志
优势: 启动快(RDB 加载) + 数据安全(AOF 增量)

3.4 持久化选型决策

场景 推荐
只做缓存(可丢) 关闭持久化
一般业务 RDB + AOF everysec
金融级数据 AOF always(慢但绝对安全)
大内存(> 16GB) 混合持久化

四、主从复制 + 哨兵 + Cluster

4.1 主从复制

全量同步(初次连接):
  Slave → PSYNC ? -1 → Master
  Master → BGSAVE → 发 RDB → Slave 加载

增量同步(已连接过):
  Master 维护 repl_backlog 环形缓冲区
  Slave 断线重连发 PSYNC <runid> <offset>
  Master 从 backlog 取增量发给 Slave

异步复制 → Master 写成功就返回,不等 Slave
风险: Master 宕机,未同步数据丢失(主从异步复制丢锁就是这个原因)

4.2 Sentinel(哨兵)

职责:

  1. 监控 Master 健康
  2. Master 失联自动 failover
  3. 客户端订阅 Sentinel 获取 Master 地址变更

部署: 3 或 5 个 Sentinel,多数派达成共识

failover 流程:

  1. Sentinel 主观下线(单 Sentinel 心跳超时)
  2. 多数派客观下线(quorum)
  3. 选 Leader Sentinel 执行 failover
  4. 在 Slave 中选新 Master(优先级 / 复制偏移量 / runid)
  5. 通知其他 Slave 复制新 Master
  6. 通知客户端

4.3 Cluster(集群)

16384 个 slot 划分:
  hash = CRC16(key) % 16384
  slot 分布到不同节点

集群规模建议: 6-9 个 master(每 master 1-2 slave)
单实例上限: ~10w QPS(GET/SET)
集群上限: master 数 × 单实例 QPS × 0.5 折扣

特性:

  • 客户端直连(不经代理)
  • MOVED / ASK 重定向
  • 不支持跨 slot 多键操作(需要 hash tag {user1})

4.4 三种部署对比

模式 数据规模 高可用 适用
单实例 < 10GB 测试 / 缓存
主从 + Sentinel < 64GB failover 中型
Cluster 100GB+ failover + 分片 生产推荐

五、缓存三大问题 + 防御

5.1 缓存穿透

现象: 请求不存在的 key,每次打到 DB 防御:

  • BloomFilter 前置(误判率 1%,1000w key 占 ~1.2MB)
  • 空值缓存 60s

5.2 缓存击穿

现象: 热点 key 过期瞬间,大量请求打到 DB 防御:

  • 互斥锁(Redisson tryLock 100ms 内只放一个回源)
  • 逻辑过期(不直接过期,后台异步刷新)
  • 永不过期(主动维护)

5.3 缓存雪崩

现象: 大量 key 同时过期,DB 被打爆 防御:

  • TTL 随机化(baseTTL ± rand(0, 60s))
  • 多级缓存兜底(L1 Caffeine + L2 Redis)
  • 熔断降级(打 DB 失败直接返回兜底数据)

六、关键参数 + 调优

6.1 内存策略

maxmemory 16gb
maxmemory-policy allkeys-lru
# allkeys-lru: 任何 key 都可被淘汰(推荐)
# volatile-lru: 仅有 TTL 的 key 被淘汰
# allkeys-lfu(4.0+): 访问频率淘汰
# noeviction: 不淘汰,内存满了写报错

6.2 网络参数

maxclients 10000           # 客户端连接上限
tcp-backlog 511            # TCP 队列
timeout 0                   # 0 = 不主动断开
tcp-keepalive 300           # 5 分钟心跳

6.3 持久化

# RDB
save 900 1                  # 900s 内有 1 次写入触发
save 300 10                 # 300s 内有 10 次
save 60 10000               # 60s 内有 10000 次

# AOF
appendonly yes
appendfsync everysec        # 推荐

6.4 慢查询

slowlog-log-slower-than 10000   # 超过 10ms 记录(单位微秒)
slowlog-max-len 128             # 保留最近 128 条

监控:SLOWLOG GET 10


七、客户端 Java 选型

客户端 优点 缺点
Jedis 简单 + 性能好 需手动管连接池
Lettuce Reactive + Netty 异步 API 复杂
Redisson 分布式锁 / 限流 / 延迟队列原生支持 略重

Spring Boot 默认: 2.x Jedis,3.x Lettuce 生产推荐: Lettuce 主连接 + Redisson 复杂数据结构


八、连接池配置(踩坑核心)

8.1 错误配置(简历踩坑)

JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(200);       // ⚠️ 默认 200,5w QPS 必崩

8.2 正确配置(简历修复)

JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(2000);              // 上限拉到 2000
config.setMaxIdle(500);
config.setMinIdle(100);                 // 最少保留 100 避免冷启动
config.setTestOnBorrow(false);          // 高并发别 ping
config.setBlockWhenExhausted(true);
config.setMaxWaitMillis(100);           // 最多等 100ms 拿不到失败

8.3 连接池估算公式

最大并发连接 ≥ 峰值 QPS × 平均调用次数 × 平均耗时(秒)

举例:
5w QPS × 2 次/请求 × 0.005s = 500 连接
留 2-4x buffer → 设置 1000-2000

九、backbone-controller 真实场景

9.1 多级缓存设计

L1 Caffeine(本地,TTL 30s)
  ↓ miss
L2 Redis Cluster(共享,TTL 5min)
  ↓ miss
L3 MySQL(持久层)

缓存对象:

  • 拓扑数据(L1)
  • 路径计算结果(L2)
  • 配置数据(Apollo + 内存)

9.2 Redisson 分布式锁

RLock lock = redisson.getLock("lock:tenant:" + tid + ":resource:" + rid);
lock.lock();   // Watchdog 自动续期
try {
    // 资源预留
} finally {
    lock.unlock();
}

9.3 监控告警

- alert: RedisHighCPU
  expr: redis_cpu_sys_seconds_total > 0.8
  for: 5m

- alert: RedisHighMemory
  expr: redis_memory_used_bytes / redis_memory_max_bytes > 0.85
  for: 10m

- alert: RedisSlowlog
  expr: redis_slowlog_length > 10
  for: 1m

- alert: RedisConnectionPoolExhausted
  expr: jedis_pool_active_connections / jedis_pool_max_total > 0.8
  for: 1m

十、踩坑案例集

坑 1 · 连接池默认值耗尽

现象: 5w QPS 时 JedisConnectionException: Could not get a resource from the pool 修复: 改 maxTotal=2000 + minIdle=100;上线监控连接池占用率告警

坑 2 · K8s StatefulSet 主从切换异常

现象: PVC 跨节点挂载延迟 + Sentinel 切换 → 双 master 60s 数据丢失 修复: 回退独立部署(K8s 主要承载无状态业务)

坑 3 · 大 Key 阻塞主线程

现象: 一个 List 100w 元素 LRANGE 全部 → Redis 主线程阻塞秒级 修复: 业务拆分大 Key;使用 SCAN 替代 KEYS;监控 bigkey 工具

坑 4 · 热 Key 打爆单实例

现象: 某热点商品页面 QPS 占比 30%,集中在一个 master 修复: 本地缓存 + 热 Key 多副本(hotkey:{slot1} hotkey:{slot2})

坑 5 · BGSAVE 期间内存翻倍

现象: fork 子进程做 BGSAVE 时,Copy-On-Write 导致内存翻倍 OOM 修复: ① 降低 maxmemory 到机器内存 50%;② 关闭 RDB 用 AOF;③ 大内存场景必须用 Cluster 拆小


十一、面试 30 / 90 秒答题

30 秒(被问”Redis 你怎么用的”)

“Redis 三个主要场景:多级缓存 / 分布式锁 / 限流。backbone 用 Redis Cluster 6 节点,搭 Caffeine 本地缓存做 L1+L2 架构。Redisson 做分布式锁防资源超卖。踩过两个坑——连接池默认 200 在 5w QPS 耗尽,改 2000 + minIdle 100;K8s 上跑 Redis 主从切换异常,回退独立部署。”

90 秒(被问”Redis 整体架构”)

“Redis 单线程处理命令(Redis 6+ I/O 多线程),5 大数据结构底层各有优化:String SDS 二进制安全;Hash 小用 ziplist 大用 hashtable;ZSet 用 skiplist + hashtable 提供 O(log N) 范围查询和 O(1) 单点查询。

持久化两套:RDB fork 子进程做内存快照(快但有丢失窗口),AOF 命令追加日志(everysec 推荐)。4.0+ 混合持久化结合两者优点。

高可用三档:单实例 → 主从+Sentinel → Cluster 16384 slot 分片。生产推荐 Cluster 6-9 master + 跨 AZ。

缓存三大问题——穿透防 BloomFilter / 击穿防互斥锁 / 雪崩防 TTL 随机化 + 多级兜底。

我在 backbone-controller 用 Redis Cluster 6 节点 + Caffeine L1+L2 多级缓存 + Redisson 分布式锁。”


十二、面试官追问预案

Q · “Redis 单线程怎么扛 10w QPS?”

“① 纯内存(无 IO)/ ② I/O 多路复用(epoll)/ ③ 简单命令避免锁开销 / ④ 高效数据结构(O(1) hashtable + O(log N) skiplist)。Redis 6+ 把网络 I/O 改多线程进一步突破。”

Q · “Redis 怎么做高可用?选型?”

“三档:Sentinel(中型,< 64GB)/ Cluster(大型,分片)/ 跨机房双活(异地多活)。简单业务 Sentinel 够用,数据规模大必走 Cluster,跨机房要按数据分类做 CAP 决策——强一致单写主+同步,最终一致双写+异步。”

Q · “AOF 和 RDB 怎么选?”

“纯缓存关持久化;一般业务 RDB+AOF everysec(混合持久化更优);金融级 AOF always 牺牲性能换安全;大内存(> 16GB)必须混合,因为纯 AOF 加载慢。”

Q · “Redis 主从异步复制丢数据怎么解决?”

“三个方案:① 同步复制(WAIT 命令,但牺牲性能);② Redlock 多 master 加锁;③ 业务侧 fencing token(每次加锁 epoch+1,下游识别过期持锁者)。生产推荐 fencing token,从语义上根本解决问题。”

Q · “你说大 Key 阻塞主线程,大 Key 怎么发现?”

“① redis-cli --bigkeys 工具扫描;② SCAN + MEMORY USAGE 抽查;③ 监控 slowlog;④ 业务侧规范——禁止单 Key 超 10MB 或单 List/Hash 超 10w 元素;⑤ 改成多个小 Key 或 ZSet 分桶。”


十三、贴墙速记

5 个数字:

  • 单实例上限 ~10w QPS
  • maxclients 默认 10000
  • AOF everysec 数据丢失窗口 1 秒
  • BloomFilter 1000w key 占 ~1.2MB
  • Cluster slot 数 16384

5 个关键词:

  1. SDS / ziplist / skiplist + hashtable(数据结构底层)
  2. RDB fork + AOF everysec(持久化)
  3. PSYNC + repl_backlog(增量同步)
  4. BloomFilter / 互斥锁 / 随机 TTL(三大问题防御)
  5. Caffeine + Redis 多级 + 旁路失效(缓存一致性)

1 个杀手句:

“Redis 性能不靠多线程,靠纯内存 + epoll + O(1) 数据结构 + 简单命令 4 件套。”

组件深度补救 · 第 1 批 · ZooKeeper + Netty

用途:被问”ZK 数据模型 / Watch 机制 / Curator 用法”和”Netty 线程模型 / 内存管理 / 编解码”时的完整答案


第 1 部分 · ZooKeeper

一、结构组成

                ┌─────────────────────────────┐
                │ ZooKeeper 集群(奇数节点)    │
                │                             │
                │  ┌────────┐    ┌────────┐  │
                │  │Leader  │←──→│Follower│  │
                │  │        │    │        │  │
                │  └────────┘    └────────┘  │
                │       ↑          ↑         │
                │       │          │         │
                │  ┌────┴────┐ ┌──┴────┐    │
                │  │Follower │ │Observer│    │
                │  │         │ │(可选) │    │
                │  └─────────┘ └───────┘    │
                └────────────────────────────┘
                        ↑       ↑
                        │       │
                Client  │       │ Curator(Java SDK)
                        ↓       ↓
                 [TCP 长连接 + 心跳]
                 
数据模型(树形 znode):
/
├── /controller/
│   ├── leader/_c_uuid-lock-0      (临时顺序节点,Leader 候选)
│   ├── leader/_c_uuid-lock-1
│   └── leader/_c_uuid-lock-2
├── /config/
│   ├── kafka/min.insync.replicas
│   └── ...
└── /services/
    ├── auth-service/instance-1   (临时节点,服务下线自动删)
    └── ...

二、4 类节点(必背)

类型 持久 顺序 用途
持久节点(PERSISTENT) 配置 / 元数据
持久顺序节点(PERSISTENT_SEQUENTIAL) 任务队列
临时节点(EPHEMERAL) 否(Session 失效自删) 服务注册 / 心跳
临时顺序节点(EPHEMERAL_SEQUENTIAL) Leader 选举(最常用)

三、Watch 机制

3.1 工作原理

Client → ZK: getData("/config", watch=true)
            ↓
ZK 在该 znode 注册 Watcher
            ↓
某客户端 setData("/config", "newValue")
            ↓
ZK 触发 Watcher → 推送通知给 Client
            ↓
Client 收到 NodeDataChanged 事件
            ↓
Client 必须 重新注册 Watcher(Watch 是一次性的!)

3.2 关键特性

  • 一次性触发: 每次 Watcher 触发后必须重新注册
  • 顺序保证: 通知按 zxid 顺序发送
  • 不带数据: 通知只说”变了”,值要再 getData
  • 会话级: Session 失效 Watcher 自动清除

3.3 vs etcd Watch

维度 ZK Watch etcd Watch
触发 一次性 持续
数据 不带新值 带完整变更
范围 单节点 单 key 或前缀
历史回放 不支持 支持(基于 mvcc)

四、ZAB 协议(简版,详见 Q10)

两阶段:
  ① Recovery (崩溃恢复)
     - 选主:FastLeaderElection
     - 同步:Leader 把已 committed 日志发给 Follower
  
  ② Broadcast (正常广播)
     - Leader 提议(PROPOSE)
     - Follower 回 ACK
     - 多数 ACK → COMMIT
     - Follower 应用

zxid 64 位:
  高 32 位 = epoch(每次新主 +1)
  低 32 位 = counter(同 epoch 内单调递增)

五、Curator(Java SDK)关键能力

// 1. 客户端
CuratorFramework client = CuratorFrameworkFactory.builder()
    .connectString("zk-1:2181,zk-2:2181,zk-3:2181")
    .sessionTimeoutMs(6000)            // ⚠️ 缩短(默认 30s 太长)
    .connectionTimeoutMs(3000)
    .retryPolicy(new ExponentialBackoffRetry(1000, 3))
    .build();
client.start();

// 2. Leader 选举
LeaderLatch latch = new LeaderLatch(client, "/controller/leader");
latch.addListener(new LeaderLatchListener() {
    public void isLeader()    { startScheduler(); }
    public void notLeader()   { stopScheduler(); }
});
latch.start();

// 3. 分布式锁
InterProcessMutex lock = new InterProcessMutex(client, "/lock/resource-A");
if (lock.acquire(5, TimeUnit.SECONDS)) {
    try { /* 业务 */ } finally { lock.release(); }
}

// 4. 配置中心 (NodeCache)
NodeCache cache = new NodeCache(client, "/config/app");
cache.getListenable().addListener(() -> {
    byte[] data = cache.getCurrentData().getData();
    reloadConfig(new String(data));
});
cache.start();

// 5. 服务注册 (临时节点)
client.create()
    .creatingParentsIfNeeded()
    .withMode(CreateMode.EPHEMERAL)
    .forPath("/services/auth/instance-" + uuid, ip.getBytes());

六、关键参数 + 调优

# zoo.cfg
tickTime=2000              # 心跳间隔(ms)
initLimit=10               # Follower 初始化 Leader 同步超时(tickTime 倍数)
syncLimit=5                # Leader/Follower 同步超时
dataDir=/var/lib/zk
clientPort=2181
maxClientCnxns=60          # 单 IP 最大连接

# JVM
-Xms2g -Xmx2g              # ZK 不需要大堆
-XX:+UseG1GC
-XX:MaxGCPauseMillis=100

七、踩坑案例(简历真实)

坑 · Session Timeout 30s 双主脑裂

  • 现象: 主控网络分区但仍向 ZK 心跳成功(IP 没变只是网络抖动)→ ZK 没感知 → 双主持续 1 分钟 → 配置冲突
  • 修复: ① Session Timeout 6s + JVM GC 监控;② Fencing Token(终极武器);③ 业务侧 verify + System.exit
  • 结果: 双主事故归零

(详见 Q32 详解)

八、面试 30 / 90 秒答题

30 秒(被问”ZK 你怎么用的”)

“backbone 用 ZK 做 30+ 微服务的 Leader 选举 + 配置中心。Curator 框架 + ZAB 协议;选主用临时顺序节点 + 序号最小者为 Leader,其他实例 Watch 前一个节点避免羊群效应。Session Timeout 调到 6s + Fencing Token 防双主——踩过 30s 默认值导致 1 分钟双主的坑。”

90 秒(被问”ZK 整体架构和 Watch 机制”)

“ZK 集群奇数节点(典型 3/5),Leader+Follower(+Observer)。数据模型是树形 znode,4 类节点——持久 / 持久顺序 / 临时 / 临时顺序。临时顺序节点是选主基石——序号最小者为 Leader,其他 Watch 前一个节点。

Watch 机制是核心——一次性触发,通知按 zxid 顺序,不带新值要再 getData。Session 失效 Watcher 自动清除。

ZAB 协议两阶段:Recovery(选主+同步)和 Broadcast(提议+ACK+COMMIT)。zxid 64 位前 32 位 epoch + 后 32 位 counter,全局有序。

我用 Curator 做选主 + 分布式锁 + 配置中心,Session Timeout 缩到 6s + Fencing Token 防双主。踩过默认 30s 导致 1 分钟双主的坑。”

九、面试官追问预案

Q · “ZK 为什么是 CP?”

“ZK 设计哲学是分区时优先一致性。多数派达成共识才能写入 → 少数派不可用。但多数派在线时可用性极高(99.99%)。这跟 etcd 一样,也是 CP 系统。”

Q · “Watch 触发后必须重新注册,有什么坑?”

“重新注册的窗口期内变更会丢——客户端代码要保证”取值 + 注册 Watch + 处理事件” 三步原子。Curator 的 NodeCache 已经封装好这套逻辑,直接用就行。”

Q · “ZK 的羊群效应是什么?怎么避免?”

“如果所有 Follower 都 Watch /lock 节点,锁释放时所有 Follower 都被唤醒抢锁——网络风暴 + CPU 飙升。避免方法是 Curator 的 InterProcessMutex 让每个客户端 Watch 前一个节点而不是 /lock,只有前一个释放才唤醒下一个。”

Q · “ZK 性能上限多少?”

“读操作可线性扩展(每个节点都能服务读),典型集群 10w+ 读 QPS;写必须经 Leader,单 Leader 上限约 1-2w 写 QPS。所以 ZK 适合读多写少 + 数据规模小(< 1GB)场景。大规模 KV 用 etcd 或 TiKV。”

十、贴墙速记

4 类节点: 持久 / 持久顺序 / 临时 / 临时顺序(选主用)

Watch 4 特性: 一次性 / 顺序 / 不带数据 / 会话级

1 个杀手句:

“ZK 是协调原语之王,适合读多写少 + 小数据 + Java 生态;K8s 时代新项目优先 etcd。”


第 2 部分 · Netty

一、结构组成

┌─────────────────────────────────────────────────────┐
│                 Netty Server                         │
│                                                      │
│  ┌─────────────────────────────────────────────┐   │
│  │ BossGroup (1-4 线程)                         │   │
│  │ - ACCEPT 新连接                              │   │
│  │ - 注册 Channel 到 WorkerGroup                │   │
│  └─────────────────────┬──────────────────────┘   │
│                         │                            │
│  ┌─────────────────────▼──────────────────────┐   │
│  │ WorkerGroup (CPU * 2 线程)                   │   │
│  │ - READ / WRITE / IO 事件                     │   │
│  │ - 每个 Channel 绑定到一个 EventLoop          │   │
│  │   (线程亲和性,避免锁竞争)                    │   │
│  └─────────────────────┬──────────────────────┘   │
│                         │                            │
│  ┌─────────────────────▼──────────────────────┐   │
│  │ ChannelPipeline (责任链)                     │   │
│  │ ┌─────────┐ ┌─────────┐ ┌─────────┐        │   │
│  │ │Decoder  │→│Business │→│Encoder  │        │   │
│  │ │(协议解码)│ │Handler  │ │(协议编码)│       │   │
│  │ └─────────┘ └─────────┘ └─────────┘        │   │
│  └────────────────────────────────────────────┘   │
│                                                      │
│  ┌────────────────────────────────────────────┐   │
│  │ ByteBufAllocator                             │   │
│  │ - PooledByteBufAllocator (jemalloc 思路)    │   │
│  │ - DirectBuffer (堆外)                        │   │
│  └────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────┘

二、线程模型(主从 Reactor)

传统 BIO(每连接一线程):
  Client 1 → Thread 1 (阻塞 read)
  Client 2 → Thread 2 (阻塞 read)
  ...
  N 连接 = N 线程 → 内存爆 + 上下文切换
  
Netty 主从 Reactor:
  BossGroup (1 线程) ─accept─→ Channel
                         ↓ register
  WorkerGroup (CPU*2)   每个线程 epoll 多个 Channel
                         ↓
  单线程处理一个 Channel 的所有事件 → 无锁
  
百万连接需要的线程: ~16 个(8 核机器)

关键点:

  • 每个 Channel 绑定到一个固定 EventLoop(线程亲和)
  • 同一 Channel 的所有事件单线程处理 → 无锁
  • 业务处理慢 → 走业务线程池(DefaultEventExecutorGroup)避免阻塞 IO 线程

三、内存管理

3.1 ByteBuf vs java.nio.ByteBuffer

特性 ByteBuf ByteBuffer
读写指针 双指针(readerIndex / writerIndex) 单指针 + flip()
容量自动扩展
池化 支持
引用计数 支持(release())

3.2 池化(PooledByteBufAllocator)

设计借鉴 jemalloc:
  - Tiny:      < 512B
  - Small:     512B - 8KB
  - Normal:    8KB - 16MB
  - Huge:      > 16MB
  - Chunk:     16MB(分配单位)
  - Page:      8KB

效果:
  - 避免反复 malloc/free
  - 减少 GC 压力(减少短命对象)
  - PereDoc 实测:GC 时间下降 60%

3.3 零拷贝 4 个层次

层次 实现 效果
Pooled ByteBuf jemalloc 池化 避免 malloc/free
CompositeByteBuf 多 ByteBuf 逻辑组合 不复制底层数组
FileRegion sendfile 系统调用 文件 → Socket 直传
DirectBuffer 堆外内存 避免堆 → 堆外拷贝

3.4 引用计数(踩坑核心)

ByteBuf buf = ctx.alloc().buffer();
try {
    buf.writeBytes(data);
    ctx.writeAndFlush(buf);    // ⚠️ Netty 内部会 release
} catch (Exception e) {
    buf.release();              // ⚠️ 异常路径必须手动 release
}

: 异常分支没 release → 内存泄漏 → 启用 ResourceLeakDetector 持续检测(简历提到)

四、Pipeline + 编解码

4.1 ChannelPipeline 模型

入站(Inbound):  ChannelRead → Decoder → Business → ...
出站(Outbound): WriteAndFlush → ... → Business → Encoder

执行顺序:
  - Inbound: 头到尾
  - Outbound: 尾到头

4.2 编解码器

编解码器 用途
LineBasedFrameDecoder 按行分隔(\n)
DelimiterBasedFrameDecoder 自定义分隔符
LengthFieldBasedFrameDecoder TLV 协议(最常用)
HttpServerCodec HTTP 协议
ProtobufDecoder Protobuf

LengthFieldBasedFrameDecoder 关键参数:

  • maxFrameLength: 最大帧长(防 OOM)
  • lengthFieldOffset: 长度字段偏移
  • lengthFieldLength: 长度字段字节数
  • lengthAdjustment: 长度修正
  • initialBytesToStrip: 跳过头部字节数

4.3 解决 TCP 粘包/拆包

TCP 是字节流,没有消息边界 → 粘包/拆包

3 种解决方案:
  ① 定长消息(简单但浪费)
  ② 分隔符(LineBasedFrameDecoder)
  ③ 长度字段(LengthFieldBasedFrameDecoder,推荐)

五、心跳 + 断线重连

pipeline.addLast(new IdleStateHandler(
    30,    // readerIdleTimeSeconds (30s 没读到数据 → READER_IDLE)
    0,     // writerIdleTimeSeconds
    0,     // allIdleTimeSeconds
    SECONDS
));

pipeline.addLast(new SimpleChannelInboundHandler<...>() {
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent e = (IdleStateEvent) evt;
            if (e.state() == IdleState.READER_IDLE) {
                ctx.close();   // 超时关闭
            }
        }
    }
});

断线重连(客户端):

// 指数退避
long delay = Math.min(60_000, 1000 * (1L << retries));   // 1s/2s/4s.../60s

六、关键参数 + 调优

6.1 ServerBootstrap 参数

ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
 .channel(NioServerSocketChannel.class)
 .option(ChannelOption.SO_BACKLOG, 1024)              // accept 队列
 .option(ChannelOption.SO_REUSEADDR, true)
 .childOption(ChannelOption.SO_KEEPALIVE, true)        // TCP 心跳
 .childOption(ChannelOption.TCP_NODELAY, true)         // 禁 Nagle
 .childOption(ChannelOption.ALLOCATOR, 
     PooledByteBufAllocator.DEFAULT)                   // ⭐ 池化
 .childOption(ChannelOption.WRITE_BUFFER_WATER_MARK,
     new WriteBufferWaterMark(32*1024, 64*1024));      // 写缓冲水位

6.2 内核参数(/etc/sysctl.conf)

# 百万连接必调
fs.file-max = 1048576
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
net.ipv4.ip_local_port_range = 1024 65535
# 进程级
ulimit -n 1048576

七、backbone-controller 真实场景(简历)

7.1 南向网关(Netconf / OpenFlow)

单实例千级 SDN 设备长连接(压测 1.5K,生产略小)
  ├ Netconf XML 协议
  └ OpenFlow 二进制协议

线程模型:
  Boss=1  Worker=CPU*2(典型 16)

零拷贝:
  PooledByteBufAllocator + DirectBuffer
  
心跳:
  IdleStateHandler 30s
  
断线重连:
  客户端指数退避 1s/2s/4s/8s/最大 60s
  链路抖动场景 5 秒内自动恢复

7.2 监控

指标 阈值
netty_active_channels < pool.max
netty_pending_writes < write_buffer_high
netty_idle_disconnects rate < 1/min
GC 时间(主要看 Direct 内存泄漏) < 200ms

八、踩坑案例

坑 1 · ByteBuf 资源泄漏(简历明确)

现象: 上线前压测发现内存持续增长 → 堆 dump 分析定位异常分支未释放 ByteBuf 修复: ① 补全资源释放逻辑 + try-finally;② 启用 ResourceLeakDetector 持续检测;③ Code Review 强制审查 release

坑 2 · ByteBuf 没池化导致 GC 高

现象: 早期用 Unpooled.copiedBuffer 大量复制,GC 时间高 修复: 改 PooledByteBufAllocator + DirectBuffer,GC 时间下降 60%

坑 3 · 业务慢导致 IO 线程阻塞

现象: 业务 DB 调用慢 → IO 线程被阻塞 → 所有 Channel 不响应 修复: 业务处理走 DefaultEventExecutorGroup(独立业务线程池),IO 线程只做协议封解码

坑 4 · LengthFieldBasedFrameDecoder 配错

现象: 协议头部 4 字节 length,但参数配 lengthFieldLength=2 → 解析错乱 修复: 仔细对照协议规范,写测试用例覆盖各种长度

九、Netty vs 原生 NIO vs Servlet

维度 Netty 原生 NIO Servlet
线程模型 主从 Reactor 自己实现 默认每请求一线程
内存管理 池化 + 零拷贝 自己实现 JVM 堆
编解码 完整生态 自己写 HTTP 内置
学习曲线 中等 陡峭 简单
性能 极高 极高(但难写对) 一般

结论: 高并发 + 自定义协议 → Netty;HTTP 标准业务 → Servlet/Spring;不要从零写 NIO

十、面试 30 / 90 秒答题

30 秒(被问”Netty 你用过吗”)

“用过,backbone 南向网关单实例千级 SDN 长连接(压测 1.5K)。主从 EventLoopGroup + 池化 ByteBuf + FileRegion 零拷贝;心跳用 IdleStateHandler 30s + 客户端指数退避重连。踩过 ByteBuf 没池化 GC 高 + 异常分支没 release 内存泄漏两个坑,启用 ResourceLeakDetector 持续检测。”

90 秒(被问”Netty 线程模型 + 内存管理”)

线程模型 主从 Reactor:BossGroup 1 线程做 ACCEPT,WorkerGroup CPU×2 做 READ/WRITE/IO,每个 Channel 绑定到一个固定 EventLoop 实现线程亲和,同一 Channel 单线程处理无锁。业务慢走 DefaultEventExecutorGroup 独立线程池,避免阻塞 IO 线程。

内存管理 零拷贝 4 层:① PooledByteBufAllocator(jemalloc 池化);② CompositeByteBuf(逻辑组合不复制);③ FileRegion(sendfile);④ DirectBuffer(堆外)。引用计数管理,异常路径必须 release 不然泄漏。

百万连接 必调内核参数:fs.file-max / somaxconn / tcp_max_syn_backlog;ulimit -n 1048576。

我在 backbone 单实例千级长连接(压测 1.5K),链路抖动 5 秒内重连恢复。”

十一、面试官追问预案

Q · “Netty 怎么避免业务慢拖累 IO 线程?”

“业务处理走独立线程池——pipeline.addLast(new DefaultEventExecutorGroup(16), 'business', businessHandler)。IO 线程只做协议封解码,业务通过 EventExecutorGroup 切换到业务线程。”

Q · “ByteBuf 引用计数为什么这么设计?”

“Netty 用引用计数管理堆外内存,避免依赖 GC(GC 不及时回收堆外内存)。每个 ByteBuf 初始 refCnt=1,被传递到下一个 Handler 时不变;writeAndFlush 后 Netty 内部 release。异常分支必须手动 release 不然内存泄漏。”

Q · “百万连接 + 1 个 Worker EventLoop 处理?”

“实际是 100w / WorkerGroup 大小 = 单 EventLoop ~62500 连接(16 个 Worker 时)。epoll 内核态注册,单线程能处理几万 Channel 的 IO 事件。瓶颈通常在业务处理速度,不在 IO 线程数。”

Q · “FileRegion 零拷贝具体怎么实现?”

“底层调 Linux sendfile 系统调用——sendfile(out_fd, in_fd, offset, count)。文件从磁盘 → 内核 PageCache → Socket Buffer 直传,不经过用户态拷贝。Netty 的 DefaultFileRegion 封装了这个调用。”

Q · “为什么 Netty 选 epoll 不选 select/poll?”

“select 1024 上限 + O(N) 扫描;poll 没上限但仍 O(N);epoll O(1) 事件回调 + 边缘触发。Linux 2.6+ 必走 epoll;Netty 在 Linux 自动选 EpollEventLoopGroup,Mac/Windows 退化到 NioEventLoopGroup(基于 NIO 的 Selector)。”

十二、贴墙速记

线程模型 3 句话:

  • BossGroup ACCEPT
  • WorkerGroup IO
  • 业务线程池 BIZ

零拷贝 4 层:

  • Pooled ByteBuf
  • CompositeByteBuf
  • FileRegion(sendfile)
  • DirectBuffer

踩坑 3 个:

  • ByteBuf 异常分支不 release
  • 没池化 GC 高
  • 业务阻塞 IO 线程

1 个杀手句:

“Netty 高性能 = 主从 Reactor + 线程亲和 + 零拷贝 4 层 + 引用计数管理堆外内存。”


进度提示

批次 组件 状态
第 1 批 Kafka / Redis / ZooKeeper / Netty
第 2 批 Caffeine / Sentinel / SkyWalking / Dubbo 待续
第 3 批 RabbitMQ / RocketMQ / Nacos / etcd 待续
第 4 批 MySQL+ShardingSphere / 连接池 / TCC/Saga / Snowflake 待续
第 5 批 Helm / Kustomize / Argo CD / Dapr / ZGC / Flowable 待续

组件深度补救 · 第 2 批 · Caffeine / Sentinel / SkyWalking / Dubbo

高频微服务 4 件套 · 每个 30/90 秒答案


第 1 部分 · Caffeine

一、结构组成

┌─────────────────────────────────────────┐
│ Caffeine Cache                           │
│                                          │
│  ┌──────────────────────────────────┐  │
│  │ ConcurrentHashMap (主存储)        │  │
│  └─────────────┬────────────────────┘  │
│                │                          │
│  ┌─────────────▼────────────────────┐  │
│  │ W-TinyLFU 算法                    │  │
│  │ - Window LRU (1%): 新增           │  │
│  │ - Probation LRU (主区,80%)        │  │
│  │ - Protected LRU (主区,20%)        │  │
│  │ - Frequency Sketch (Count-Min)    │  │
│  └──────────────────────────────────┘  │
│                                          │
│  ┌──────────────────────────────────┐  │
│  │ Eviction(过期 + 容量驱逐)          │  │
│  │ Refresh(异步刷新)                  │  │
│  │ RemovalListener(回调)              │  │
│  │ Stats(命中率统计)                  │  │
│  └──────────────────────────────────┘  │
└─────────────────────────────────────────┘

二、W-TinyLFU 算法(为什么命中率高)

传统 LRU 痛点:
  - 一次性大量访问扫描会污染缓存(把热数据全挤出去)
  
传统 LFU 痛点:
  - 新数据没机会(频率初始化为 0)

W-TinyLFU 解决:
  ① Window LRU 让新数据先在小窗口缓冲
  ② Frequency Sketch 用 Count-Min 4-bit 计数器记录访问频率
  ③ 当 Window 满了,新数据 vs Main 区随机一个比频率
  ④ 频率高的留下,频率低的淘汰

实测命中率:Caffeine W-TinyLFU > Guava LRU 约 5-15 个百分点

三、关键 API + 配置

Cache<String, User> cache = Caffeine.newBuilder()
    .maximumSize(100_000)                           // ⭐ 容量上限
    .expireAfterWrite(Duration.ofHours(1))          // ⭐ 写入后过期
    .expireAfterAccess(Duration.ofMinutes(10))      // 访问后过期
    .refreshAfterWrite(Duration.ofMinutes(5))       // 异步刷新(配 LoadingCache)
    .recordStats()                                   // 暴露统计
    .removalListener((key, value, cause) -> 
        log.info("removed {} cause {}", key, cause))
    .build();

// LoadingCache(自动加载)
LoadingCache<String, User> loading = Caffeine.newBuilder()
    .maximumSize(100_000)
    .expireAfterWrite(Duration.ofHours(1))
    .build(key -> userMapper.findById(key));   // miss 自动回源

User u = loading.get("123");   // miss 触发加载

// AsyncLoadingCache(异步)
AsyncLoadingCache<String, User> async = Caffeine.newBuilder()
    .maximumSize(100_000)
    .buildAsync((key, executor) -> 
        CompletableFuture.supplyAsync(() -> userMapper.findById(key), executor));

四、统计指标

CacheStats stats = cache.stats();
stats.hitRate()           // 命中率(目标 > 90%)
stats.missCount()
stats.loadSuccessCount()
stats.totalLoadTime()
stats.evictionCount()

五、踩坑案例(简历真实)

坑 · ConcurrentHashMap 当缓存无上限

现象: PereDoc 项目 SliceCacheManager 用静态 ConcurrentHashMap → 280w 条目 → Old Gen 持续 92% Full GC 修复: 改 Caffeine + maximumSize=100000 + expireAfterWrite(1h) 沉淀: Code Review Checklist 加一条——任何 Map 作缓存必须设上限和过期

六、面试 30/90 秒答题

30 秒

“Caffeine 是本地缓存默认选择,基于 W-TinyLFU 命中率比 Guava LRU 高 5-15 个百分点。Caffeine 4 个核心配置:maximumSize / expireAfterWrite / refreshAfterWrite / recordStats。生产 PereDoc 项目用它修过一个 ConcurrentHashMap 无上限导致的 Full GC 问题——这事写进了 Code Review Checklist。”

90 秒

“Caffeine 底层基于 ConcurrentHashMap + W-TinyLFU 算法。W-TinyLFU 解决两个痛点:LRU 一次性扫描会污染缓存;LFU 新数据没机会。它用 Window LRU 缓冲新数据 + Count-Min Sketch 记录频率,Window 满了和 Main 区比频率,频率高的留下。

配置上 maximumSize 防爆 + expireAfterWrite 自动过期 + refreshAfterWrite 异步刷新 + recordStats 监控命中率。AsyncLoadingCache 配 CompletableFuture 用于高并发异步加载。

backbone 用 Caffeine 做 L1 本地缓存(TTL 30s),配 Redis Cluster L2(TTL 5min)做多级架构。关键原则:任何 Map 作缓存必须设上限和过期——这是 PereDoc Full GC 事故沉淀的 Code Review 规则。”


第 2 部分 · Sentinel

一、结构组成

┌─────────────────────────────────────────────┐
│ Sentinel(阿里限流熔断框架)                    │
│                                              │
│  ┌────────────────────────────────────────┐ │
│  │ ResourceManager(资源管理)               │ │
│  │ - 标记保护的资源(@SentinelResource)      │ │
│  └─────────────────┬──────────────────────┘ │
│                     │                          │
│  ┌─────────────────▼──────────────────────┐ │
│  │ Slot Chain(责任链)                       │ │
│  │ ┌──────────┐ ┌─────────┐ ┌─────────┐   │ │
│  │ │ NodeSlot │→│Statistic│→│ FlowSlot│→  │ │
│  │ │          │ │ Slot    │ │(限流)    │   │ │
│  │ └──────────┘ └─────────┘ └─────────┘   │ │
│  │ ┌──────────┐ ┌─────────┐ ┌─────────┐   │ │
│  │ │DegradeSlot│→│SystemSlot│→│Authority│  │ │
│  │ │ (熔断)   │ │(系统保护)│ │ Slot     │   │ │
│  │ └──────────┘ └─────────┘ └─────────┘   │ │
│  └────────────────────────────────────────┘ │
│                                              │
│  ┌────────────────────────────────────────┐ │
│  │ Cluster Server(集群限流中心)             │ │
│  │ - 全局配额管理                          │ │
│  │ - 配额下发(每秒)                         │ │
│  └────────────────────────────────────────┘ │
└─────────────────────────────────────────────┘

二、3 大能力

2.1 限流(Flow Control)

算法 用途
令牌桶 允许突发流量
漏桶 平滑出流量
滑动窗口 精准计数
Warm Up 冷启动避免突刺
@SentinelResource(value = "createOrder",
    blockHandler = "handleBlock")
public Result createOrder(Order o) {
    // 业务
}

public Result handleBlock(Order o, BlockException ex) {
    return Result.fail("系统繁忙");
}
flow:
  - resource: createOrder
    grade: 1                    # QPS 模式
    count: 5000
    strategy: 0                 # 直接限流
    controlBehavior: 2          # 排队等待
    maxQueueingTimeMs: 500

2.2 熔断降级(Circuit Breaker)

策略 触发条件
慢调用比例 RT 超阈值的请求比例 > X
异常比例 异常请求比例 > X
异常数 异常数 > N

熔断后进入”半开”状态试探,成功则关闭熔断。

2.3 系统保护(System)

基于 Load / CPU / 入口 QPS / 平均 RT 总体评估,系统过载时直接拒绝。

三、千万级 QPS 架构(简历核心)

┌──────────────────────────────────────────────┐
│ ClusterServer(基于 Raft 选主)                  │
│ - 全局总配额(10w QPS)                          │
│ - 按 Worker 权重分配                           │
└─────────────────┬────────────────────────────┘
                  │ 每秒 1 次配额下发
       ┌──────────┼──────────┬──────────┐
       ↓          ↓          ↓          ↓
  Worker 1    Worker 2    Worker 3    Worker N
  本地令牌桶  本地令牌桶  本地令牌桶  本地令牌桶
  (1w QPS)    (1w QPS)    (1w QPS)    (1w QPS)
   ↑           ↑            ↑           ↑
   │ 业务流量   │           │           │

为什么”中心+本地”而不是纯 Redis:

  • 纯 Redis 上限 ~30w QPS(Redis 极限)
  • 本地令牌桶 < 100ns 延迟,单机扛 200w QPS
  • 中心配额下发 1 秒精度,本地精度 ms 级

Elevate-SaaS 真实踩坑:

  • 早期纯 Redis 限流,1k 并发把 Redis 打到 80% CPU
  • 改”本地+中心”后 Redis CPU 降到 15%

四、Spring Cloud Gateway + Sentinel 网关层

// Gateway 配置
spring:
  cloud:
    gateway:
      routes:
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/order/**
          filters:
            - name: Sentinel
              args:
                spec: order-route-spec

// Sentinel 规则
spring.cloud.sentinel.transport.dashboard=sentinel-dashboard:8080

五、面试 30/90 秒答题

30 秒

“Sentinel 三大能力:限流 / 熔断 / 系统保护。限流 4 种算法——令牌桶突发友好 / 漏桶平滑 / 滑动窗口精准 / 预热防冷启动突刺。我在 Elevate-SaaS 用过——早期纯 Redis 限流 1k 并发把 Redis 打到 80% CPU,改 Sentinel 中心配额 + 本地令牌桶后 Redis CPU 降到 15%。千万级 QPS 必走中心+本地架构。”

90 秒

“Sentinel 责任链 Slot Chain 架构:NodeSlot → StatisticSlot → FlowSlot 限流 → DegradeSlot 熔断 → SystemSlot 系统保护。

限流维度:接口级 / 用户级 / 租户级 / 热点参数 / 集群限流。算法选择令牌桶最常用(突发友好)。

千万级 QPS 必走中心+本地架构——ClusterServer(Raft 选主)管全局配额,每秒下发给 Worker;Worker 本地令牌桶 AtomicLong + ScheduledExecutor 定时填充,< 100ns 延迟单机 200w QPS。

Elevate-SaaS 早期纯 Redis 限流踩过 80% CPU 的坑,改架构后 15%。关键认知:Redis 限流上限约 30w QPS,千万级必须本地化。”


第 3 部分 · SkyWalking

一、结构组成

┌──────────────────────────────────────────┐
│ Application(业务应用)                     │
│                                           │
│  ┌────────────────────────────────────┐  │
│  │ -javaagent:skywalking-agent.jar     │  │
│  │ ↓ 启动时字节码增强                  │  │
│  │ 自动埋点 Servlet/Spring/RPC/SQL/MQ  │  │
│  └────────────┬───────────────────────┘  │
└───────────────┼────────────────────────┘
                │ 异步上报(gRPC)
                ↓
┌──────────────────────────────────────────┐
│ OAP Server(后端聚合)                      │
│ - 接收 Trace/Metrics/Log                  │
│ - 拓扑分析                                 │
│ - 告警规则                                 │
└────────────┬─────────────────────────────┘
              │
              ↓
┌──────────────────────────────────────────┐
│ Storage(存储)                              │
│ ElasticSearch(主流) / H2(测试) / TiDB     │
└──────────────────────────────────────────┘
              ↑
              │
┌──────────────────────────────────────────┐
│ UI(可视化)                                 │
│ - 服务拓扑图                               │
│ - 链路追踪明细                             │
│ - JVM 大盘                                 │
└──────────────────────────────────────────┘

二、核心概念

2.1 链路追踪 3 元素

元素 含义
TraceId 一次请求全链路唯一 ID
Span 一次操作(HTTP/SQL/RPC)
SpanContext 跨服务传递(HTTP Header)

2.2 字节码增强 vs 显式埋点

字节码增强(SkyWalking 默认):

  • 启动时 -javaagent 注入
  • 业务代码 0 改动
  • 性能损耗 < 5%
  • 覆盖主流框架(Spring/MyBatis/Dubbo/Kafka 等)

显式埋点(OpenTelemetry SDK):

  • 业务代码加 @Trace 注解或显式 Span
  • 灵活但侵入

SkyWalking 优势: 国产 + 字节码增强 + 拓扑分析强 + 中文文档完整

三、跨服务 TraceId 传递

HTTP Header:
  sw8: 1-{traceId}-{segmentId}-{spanId}-{parentService}-{parentInstance}-{parentEndpoint}-{addressUsed}

举例:
  sw8: 1-abc123-segment-1-1-app-A-instance-1-/api/order-host:port

服务 A → 服务 B 调用时 SkyWalking agent 自动注入这个 Header,服务 B 收到后自动解析继承上下文。

四、关键能力

4.1 服务拓扑

OAP Server 根据 Trace 数据自动绘制服务调用图——不需要手工维护拓扑文件。

4.2 慢方法定位

Trace 视图:
  /api/order (200ms)
    ├ AuthService.check (10ms)
    ├ OrderService.create (180ms)        ⚠️ 慢
    │   ├ MyBatis SELECT * FROM orders   (150ms)   ⚠️ 真凶
    │   └ Redis SET                       (5ms)
    └ KafkaProducer.send                  (10ms)

4.3 告警规则

rules:
  service_resp_time_rule:
    metrics-name: service_resp_time
    threshold: 1000
    op: ">"
    period: 10
    count: 3
    silence-period: 5
    message: "Service {} 响应时间 > 1s"

五、面试 30/90 秒答题

30 秒

“SkyWalking 是国产 APM 框架,基于字节码增强(-javaagent)无侵入埋点。我在 backbone 30+ 微服务用 SkyWalking 做链路追踪——一类典型告警 MTTD 从 30 分钟降到 1-3 分钟。比 Jaeger 优势是国产 + 字节码增强 + 拓扑分析强 + 中文文档完整。”

90 秒

“SkyWalking 4 大组件:Agent(字节码增强埋点)/ OAP Server(后端聚合)/ Storage(ES 主流)/ UI。

字节码增强 vs 显式埋点:SkyWalking 启动时 -javaagent 注入,业务代码 0 改动,性能损耗 < 5%;OpenTelemetry SDK 需要业务侧加 @Trace 或显式 Span。

跨服务通过 sw8 HTTP Header 传递 TraceId,自动继承上下文。

核心能力:服务拓扑自动发现 / 慢方法定位 / JVM 大盘 / 告警规则。

backbone 30+ 微服务接入 SkyWalking,链路定位 MTTD 30 分钟 → 1-3 分钟。比 Jaeger 优势是字节码增强无侵入 + 国产中文生态 + 拓扑分析。”


第 4 部分 · Dubbo

一、结构组成

                ┌──────────────────────┐
                │ Registry(注册中心)     │
                │ ZK / Nacos / Redis    │
                └──────┬───────────────┘
                       │
         ┌─────────────┼─────────────┐
         │             │             │
    ┌────▼────┐                ┌────▼────┐
    │Provider │                │Consumer │
    │(服务提供)│ ─── invoke ──→ │(服务消费)│
    └─────────┘                └─────────┘
         │                          │
         └─── Monitor(监控中心)──┘

二、SPI 扩展机制(Dubbo 核心)

Dubbo 一切皆可扩展:

  • 协议: Dubbo / gRPC / Hessian / HTTP
  • 注册中心: ZK / Nacos / Redis / Consul
  • 序列化: Hessian / Protobuf / Kryo
  • 负载均衡: Random / RoundRobin / LeastActive / ConsistentHash
  • 集群容错: Failover / Failfast / Failsafe / Failback / Forking / Broadcast
// 自定义 LoadBalance
@SPI("random")
public interface LoadBalance {
    <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation inv);
}

// 实现并配置
META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.LoadBalance:
mylb=com.example.MyLoadBalance

三、协议层

Dubbo 默认协议:

  • 单一长连接(NIO)
  • 二进制 Hessian2 序列化
  • 适合小数据量 + 高并发

vs Spring Cloud OpenFeign(HTTP): | 维度 | Dubbo | OpenFeign | |——|——-|———–| | 协议 | Dubbo 二进制 + 长连接 | HTTP/1.1 短连接 | | 性能 | 高 | 中 | | 可读性 | 二进制(需工具) | HTTP 易调试 | | 生态 | 阿里全家桶 | Spring Cloud |

四、负载均衡 4 种

策略 行为
Random 随机(默认,带权重)
RoundRobin 轮询(带权重)
LeastActive 最少活跃(慢节点收到少请求)
ConsistentHash 一致性哈希(同 key 落同一节点,适合本地缓存场景)

五、集群容错 6 种

策略 行为
Failover 失败重试(默认 2 次)
Failfast 快速失败(写操作)
Failsafe 失败安全(异常忽略,如日志)
Failback 失败自动恢复(后台重试,适合通知)
Forking 并行调用(取最快,适合实时性高)
Broadcast 广播(全部调用,适合通知)

六、面试 30/60 秒答题

30 秒

“Dubbo 早期模块用,核心是 SPI 扩展点 + 协议高性能 + 6 种容错策略。SPI 让协议 / 注册中心 / 序列化 / 负载均衡都可插拔。Dubbo 默认协议是二进制 + 单一长连接,性能比 HTTP 高;但调试不如 OpenFeign 方便。新模块倾向 OpenFeign(贴 Spring Cloud)。”

60 秒

“Dubbo 5 层架构:Service / Config / Proxy / Registry / Cluster / Monitor / Protocol / Exchange / Transport / Serialize。最有特色的是 SPI——协议、注册中心、序列化、负载均衡 全都是 SPI 扩展点,一切皆可替换。

协议层默认 Dubbo + Hessian2 二进制 + 单一长连接,性能高但调试不如 HTTP。负载均衡 4 种,推荐 LeastActive 让慢节点少收请求;集群容错 6 种,Failover 默认重试 2 次。

backbone 部分模块用 Dubbo,新模块倾向 OpenFeign 因为贴 Spring Cloud + 调试方便。”


进度提示

批次 组件 状态
第 1 批 Kafka / Redis / ZooKeeper / Netty
第 2 批 Caffeine / Sentinel / SkyWalking / Dubbo
第 3 批 RabbitMQ / RocketMQ / Nacos / etcd 待续
第 4 批 MySQL+ShardingSphere / 连接池 / TCC/Saga / Snowflake 待续
第 5 批 Helm / Kustomize / Argo CD / Dapr / ZGC / Flowable 待续

组件深度补救 · 第 3 批 · RabbitMQ / RocketMQ / Nacos / etcd

用途:被问”你提到 RabbitMQ / RocketMQ 用过吗”和”Nacos / etcd / Consul 选型”时的标准答案


第 1 部分 · RabbitMQ

一、结构组成

┌──────────────────────────────────────────────┐
│ RabbitMQ Cluster                              │
│                                                │
│  ┌──────────────────────────────────────┐    │
│  │ Producer                               │    │
│  └──────────────┬───────────────────────┘    │
│                 │ publish                       │
│  ┌──────────────▼───────────────────────┐    │
│  │ Exchange (交换机)                      │    │
│  │ ┌────────┐ ┌────────┐ ┌────────┐    │    │
│  │ │ Direct │ │  Topic │ │ Fanout │    │    │
│  │ └────┬───┘ └────┬───┘ └────┬───┘    │    │
│  └───────┼─────────┼──────────┼────────┘    │
│          │ binding(routing key)              │
│          ↓         ↓          ↓                │
│  ┌──────────────────────────────────────┐    │
│  │ Queue                                  │    │
│  │ - 镜像队列(高可用)                     │    │
│  │ - Persistent(持久化)                  │    │
│  └──────────────┬───────────────────────┘    │
│                 │ consume                       │
│  ┌──────────────▼───────────────────────┐    │
│  │ Consumer                              │    │
│  └──────────────────────────────────────┘    │
└──────────────────────────────────────────────┘

二、4 种 Exchange 类型(必背)

类型 路由规则 用途
Direct routing_key 完全匹配 点对点
Topic routing_key 模式匹配(* #) 发布订阅
Fanout 无视 routing_key,广播 广播
Headers 按 Header 匹配 不常用

举例:

Topic Exchange: order.*.created
  binding: order.us.created → Queue A
  binding: order.cn.created → Queue B
  binding: order.#         → Queue C(全收)

三、消息可靠性保证

Producer 端:
  - confirm 模式(消息到 Broker 回 ACK)
  - return 模式(无队列接收时回退)

Broker 端:
  - 队列持久化(durable=true)
  - 消息持久化(deliveryMode=2)
  - 镜像队列(跨节点副本)

Consumer 端:
  - 手动 ACK(autoAck=false)
  - 失败 nack 重投或 reject 死信
  - 死信队列(DLX)

四、与 Kafka 关键差异

维度 RabbitMQ Kafka
协议 AMQP 标准协议 自定义二进制
吞吐 中(单机 ~10w QPS) 极高(单机 100w QPS)
延迟 极低(< 1ms) 中(几 ms)
消息保留 消费完即删 时间/大小窗口保留(默认 7 天)
回溯消费 不支持 支持(从任意 offset)
顺序保证 单队列单消费者 partition 内有序
路由灵活 4 种 Exchange 灵活 简单(按 partition)
典型场景 业务解耦 + 异步 大数据 + 日志流

五、面试 30/60 秒答题(承认深度有限)

30 秒

“RabbitMQ 我熟悉原理但生产用得少——主导用的是 Kafka。RabbitMQ 强项是 AMQP 标准协议 + 4 种 Exchange 灵活路由 + 极低延迟(< 1ms),适合金融场景的小消息强一致;Kafka 强项是大吞吐 + 回溯消费 + 大数据集成。backbone 选 Kafka 是因为吞吐(5 亿/天)和回溯能力。”

60 秒

“RabbitMQ 是 AMQP 标准实现,核心有 Exchange 4 种类型——Direct 点对点 / Topic 模式匹配 / Fanout 广播 / Headers。可靠性靠 Producer confirm + 持久化 + 镜像队列 + Consumer 手动 ACK + 死信队列。

跟 Kafka 关键差异:RabbitMQ 协议标准 + 路由灵活 + 极低延迟,适合金融场景;Kafka 吞吐大 + 回溯消费 + 大数据生态。

我熟悉 RabbitMQ 原理(消费模型 / 死信 / 镜像),生产场景以 Kafka 为主——backbone 5 亿/天事件量级 RabbitMQ 不够扛。如果硕磐有 RabbitMQ 场景我可以快速补——AMQP 协议 + 镜像队列我做过 demo。”

🛡️ 没用过的应对策略(诚实):

“深度生产实战 RabbitMQ 我没有,但原理 + Demo 验证过。如果加入贵司有这个场景,我能 1-2 周补到生产级。”


第 2 部分 · RocketMQ

一、结构组成

┌──────────────────────────────────────────┐
│ RocketMQ                                   │
│                                            │
│  ┌──────────────────────────────────┐    │
│  │ NameServer (轻量级注册中心)        │    │
│  │ - 不存数据,只维护 Broker 路由     │    │
│  │ - 集群但无主从                     │    │
│  └────────────┬─────────────────────┘    │
│                │ heartbeat                   │
│  ┌────────────▼─────────────────────┐    │
│  │ Broker(主从模式)                  │    │
│  │  ┌────────┐  ┌──────────┐        │    │
│  │  │ Master │←→│  Slave   │        │    │
│  │  └────────┘  └──────────┘        │    │
│  │                                    │    │
│  │ Topic → Queue(类似 Kafka Partition)│    │
│  │ - 每 Topic 4-8 Queue              │    │
│  │ - 队列同步 / 异步复制              │    │
│  └────────────────────────────────────┘    │
│                                            │
│  Producer ←──────→  Consumer Group         │
│  (PUSH/PULL 模式)   (Cluster/Broadcast)    │
└──────────────────────────────────────────┘

二、核心特色(vs Kafka)

维度 RocketMQ Kafka
注册中心 NameServer 轻量 ZK / KRaft
事务消息原生 ✅ 半消息 + 回查 ❌ 需要业务实现
延迟消息 ✅ 18 个延迟级别 ❌ 不原生
消息过滤 Tag / SQL92 过滤 ❌ 客户端过滤
顺序消息 消息组(MessageQueueSelector) partition 内有序
吞吐 高(几十万 QPS) 极高(100w QPS)
国产生态 阿里全家桶 国际生态

三、事务消息(简历真实场景)

简历原文:

“RocketMQ 事务消息保障’任务下发 → 机器人执行 → 状态回写’链路最终一致”

两阶段提交流程:

1. Producer 发"半消息"(Half Message)到 Broker
   ↓
   半消息对消费者不可见
   ↓
2. Producer 执行本地事务(INSERT 任务表)
   ↓
3a. 本地事务成功 → Producer 提交(commit)→ 消息变可见
3b. 本地事务失败 → Producer 回滚(rollback)→ 消息删除
3c. Producer 网络失败 → Broker 主动回查 Producer
   ↓
4. Broker 回查接口:checkLocalTransaction()
   ↓
5. Producer 检查本地事务状态(查任务表)→ 返回 commit / rollback / unknown
// Producer 实现
TransactionMQProducer producer = new TransactionMQProducer("rpa-task-group");
producer.setTransactionListener(new TransactionListener() {
    
    @Override
    public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
        try {
            // 本地事务:写任务表
            taskMapper.insert((Task) arg);
            return LocalTransactionState.COMMIT_MESSAGE;
        } catch (Exception e) {
            return LocalTransactionState.ROLLBACK_MESSAGE;
        }
    }
    
    @Override
    public LocalTransactionState checkLocalTransaction(MessageExt msg) {
        String taskId = msg.getKeys();
        Task task = taskMapper.findById(taskId);
        if (task != null && "DONE".equals(task.getStatus())) {
            return LocalTransactionState.COMMIT_MESSAGE;
        } else if (task == null) {
            return LocalTransactionState.ROLLBACK_MESSAGE;
        }
        return LocalTransactionState.UNKNOW;  // Broker 稍后再回查
    }
});

四、面试 30/60 秒答题

30 秒

“RocketMQ 在 RPA 项目用过事务消息——’任务下发 → 机器人执行 → 状态回写’最终一致。RocketMQ 事务消息原生支持半消息 + 回查,比 Kafka 自己实现幂等键 + 去重表对业务侵入小一点,但 RocketMQ 集群运维成本高。”

60 秒

“RocketMQ 阿里系国产 MQ,4 大特色 vs Kafka:① NameServer 轻量级注册中心(无主从)/ ② 事务消息原生支持(半消息 + 回查)/ ③ 18 个延迟级别 / ④ Tag/SQL 过滤。

RPA 项目用事务消息保障跨机器人执行的最终一致——Producer 发半消息 → 执行本地事务 → 提交 / 回滚 / Broker 回查。

RocketMQ vs Kafka 选型:金融 / 事务 / 延迟场景选 RocketMQ;大吞吐 / 大数据生态选 Kafka。我们 backbone 5 亿/天选 Kafka。

集群运维成本 RocketMQ > Kafka(NameServer + Broker 主从都要管),小团队慎重。”


第 3 部分 · Nacos

一、结构组成

┌──────────────────────────────────────────┐
│ Nacos Server(集群)                         │
│                                            │
│  ┌──────────────────────────────────┐    │
│  │ Naming Service(服务注册发现)      │    │
│  │ - AP 模式:Distro 协议             │    │
│  │ - CP 模式:Raft 协议               │    │
│  └──────────────────────────────────┘    │
│                                            │
│  ┌──────────────────────────────────┐    │
│  │ Config Service(配置中心)           │    │
│  │ - Long Polling 推送                │    │
│  │ - 多 Namespace 隔离                │    │
│  │ - 灰度发布                          │    │
│  └──────────────────────────────────┘    │
│                                            │
│  ┌──────────────────────────────────┐    │
│  │ Console(管控台)                    │    │
│  └──────────────────────────────────┘    │
└──────────────────────────────────────────┘

二、核心能力

2.1 服务注册发现

// 服务注册
@SpringBootApplication
@EnableDiscoveryClient
public class App { }

// application.yml
spring:
  cloud:
    nacos:
      discovery:
        server-addr: nacos:8848
        namespace: tenant-a
        weight: 1                        # 负载均衡权重

// 服务发现
@RestController
public class OrderController {
    @LoadBalanced
    @Autowired
    private RestTemplate rest;
    
    public Order get(String id) {
        return rest.getForObject(
            "http://user-service/users/" + id, User.class);
    }
}

2.2 配置中心

Long Polling 推送:

Client → Nacos: GET /config/v2/cs?dataId=app.yaml&timeout=30000
                                                    ↑ 服务端阻塞 30s
            ↓
   配置变化 → Nacos 立即响应(秒级推送)
   配置不变 → 30s 超时返回 304(Client 重连)

多 Namespace 隔离:

Namespace tenant-a / Group DEFAULT_GROUP / DataId app.yaml
Namespace tenant-b / Group DEFAULT_GROUP / DataId app.yaml
                                              ↑ 同名不冲突

2.3 多租户配置(Elevate-SaaS 真实)

spring:
  cloud:
    nacos:
      config:
        server-addr: nacos:8848
        namespace: ${TENANT_ID}             # 按租户分 Namespace
        group: ${ENV}                        # 按环境分 Group
        file-extension: yaml
        refresh-enabled: true

效果: 每个租户独立 Namespace,租户级配置隔离 + 灰度推送。

三、Nacos vs Apollo vs Consul vs ZK

维度 Nacos Apollo Consul ZK
配置 + 注册 一体 仅配置 一体 仅协调
CAP AP/CP 可切 AP CP CP
灰度 支持 ✅ 强
多 Namespace
长轮询/推送 Long Polling Long Polling Watch Watch
生态 Spring Cloud Alibaba Spring 强 Hashicorp Java 经典
运维
国产

选型逻辑:

  • 配置中心通常选 AP > CP —— 配置读取 > 写入,部分节点失联仍能读本地缓存
  • 国内 + Spring Cloud Alibaba: Nacos 一体化
  • 强配置中心 + 灰度: Apollo
  • Hashicorp 生态: Consul
  • 历史 ZK 项目: 继续 ZK + Curator

四、面试 30/60 秒答题

30 秒

“Nacos 是 Spring Cloud Alibaba 全家桶的服务注册 + 配置中心一体化方案。Elevate-SaaS 用 Nacos 做多租户配置中心——按 tenantId 分 Namespace 实现租户级配置隔离 + 灰度推送。配置中心通常选 AP > CP——读取 > 写入,部分节点失联仍能读本地缓存。”

60 秒

“Nacos 双服务:Naming Service(服务注册发现)+ Config Service(配置中心),CP/AP 可切换。

配置中心用 Long Polling——客户端 GET 阻塞 30s,配置变化立即响应秒级推送,配置不变 30s 超时重连;比 ZK Watch 一次性触发更适合配置场景。

Elevate-SaaS 用 Nacos 多 Namespace 实现多租户配置隔离——每个租户独立 Namespace + 按环境分 Group。

选型上:国内 + Spring Cloud Alibaba 选 Nacos 一体化;强配置 + 灰度选 Apollo;Hashicorp 生态选 Consul;历史 ZK 项目继续 ZK。关键认知:配置中心选 AP 比 CP 合适。”


第 4 部分 · etcd

一、结构组成

┌──────────────────────────────────────────┐
│ etcd Cluster(典型 3/5 节点)                │
│                                            │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐ │
│  │ Leader   │←→│ Follower │←→│ Follower │ │
│  └──────────┘  └──────────┘  └──────────┘ │
│      Raft 协议(强一致 + 多数派)           │
│                                            │
│  存储引擎: BoltDB (B+ 树)                  │
│  数据模型: KV + Range Watch + Lease        │
│  MVCC: 多版本(每次写产生新 revision)        │
└──────────────────────────────────────────┘

二、核心能力(vs ZK)

2.1 数据模型

维度 etcd ZK
模型 扁平 KV 树形 znode
范围查询 ✅ Range ❌ 单节点
Watch 范围 单 key 或前缀 单节点
Watch 持续 ✅ 持续 ❌ 一次性
历史回放 ✅ 基于 mvcc
数据规模 KV 大规模(GB+) 元数据小(MB)

2.2 Lease(租约)

// 创建 5 秒租约
lease := client.Grant(ctx, 5)

// 租约关联 key
client.Put(ctx, "/services/auth/instance-1", "ip:port", clientv3.WithLease(lease.ID))

// 续期(KeepAlive)
ch, _ := client.KeepAlive(ctx, lease.ID)
go func() {
    for resp := range ch {
        log.Printf("renew lease: %d", resp.ID)
    }
}()

// 进程退出 / KeepAlive 停止 → 5 秒后 key 自动删除(类似 ZK 临时节点)

2.3 MVCC + Watch

// 监听 prefix
watchCh := client.Watch(ctx, "/services/", clientv3.WithPrefix())
for resp := range watchCh {
    for _, ev := range resp.Events {
        log.Printf("type=%s key=%s value=%s", ev.Type, ev.Kv.Key, ev.Kv.Value)
    }
}

// 历史回放(从指定 revision 开始)
watchCh := client.Watch(ctx, "/services/", 
    clientv3.WithPrefix(), 
    clientv3.WithRev(123))

三、K8s 怎么用 etcd(简历隐含)

TenantOperator / DetNetController / VMPoolOperator
       ↓
[K8s ApiServer] ──→ [etcd 集群]
       ↑
[kubectl 等其他客户端]

K8s 资源全部存 etcd:
  /registry/pods/...
  /registry/services/...
  /registry/customresourcedefinitions/...
  /registry/pml.io/tenants/...    ← Tenant CRD
  
Reconcile 本质 = 对 etcd 状态做对账
ResourceVersion = etcd mvcc revision

四、Raft 协议简版(详见 Q10)

3 角色: Leader / Follower / Candidate
随机超时选举 (150-300ms)
日志连续性 (Log Matching Property)
任期号 term 单调递增

五、面试 30/60 秒答题

30 秒

“etcd 是 Raft 实现的强一致 KV,K8s ApiServer 后端就是它。我的 3 个 Operator 都隐含用了 etcd——Reconcile 本质是’对 etcd 状态做对账’,ResourceVersion 直接是 etcd 的 mvcc revision。etcd vs ZK:大数据量 KV 选 etcd / 协调原语选 ZK / K8s 生态必走 etcd。”

60 秒

“etcd 基于 Raft 协议的强一致 KV 存储,典型 3/5 节点集群。BoltDB B+ 树存储,KV 模型 + Range Watch + Lease + MVCC 多版本。

跟 ZK 关键差异:① 扁平 KV vs 树形 znode;② Watch 持续 vs 一次性;③ 支持历史回放;④ 大数据量 KV(GB+) vs 元数据(MB)。

Lease 类似 ZK 临时节点——客户端 KeepAlive 续期,停止则自动删 key,适合服务注册。

K8s 生态必走 etcd——ApiServer 后端就是 etcd,所有 CR / Pod / Service 都存 etcd。我的 TenantOperator / DetNetController / VMPoolOperator 都隐含用了 Raft 协议,Reconcile 是对 etcd 状态对账。

选型:K8s 生态必走 etcd;大规模 KV 选 etcd / TiKV;Java 协调原语选 ZK + Curator;新项目无 K8s 历史包袱选 etcd。”


进度提示

批次 组件 状态
第 1 批 Kafka / Redis / ZooKeeper / Netty
第 2 批 Caffeine / Sentinel / SkyWalking / Dubbo
第 3 批 RabbitMQ / RocketMQ / Nacos / etcd
第 4 批 MySQL+ShardingSphere / 连接池 / TCC/Saga / Snowflake 待续
第 5 批 Helm / Kustomize / Argo CD / Dapr / ZGC / Flowable 待续

组件深度补救 · 第 4 批 · MySQL+ShardingSphere / 连接池 / TCC+Saga / Snowflake

用途:数据存储 + 事务 + 分布式 ID 一站式


第 1 部分 · MySQL + ShardingSphere

一、MySQL 必懂基础

1.1 InnoDB 存储引擎

┌────────────────────────────────────────┐
│ InnoDB                                  │
│                                          │
│  ┌────────────────────────────────────┐│
│  │ Buffer Pool(内存,默认 128MB)       ││
│  │ - 数据页 + 索引页缓存                ││
│  └────────────────────────────────────┘│
│                                          │
│  ┌────────────────────────────────────┐│
│  │ Redo Log(WAL,先写日志再写数据)    ││
│  │ - 崩溃恢复                          ││
│  │ - 顺序写性能好                      ││
│  └────────────────────────────────────┘│
│                                          │
│  ┌────────────────────────────────────┐│
│  │ Undo Log(MVCC + 事务回滚)           ││
│  └────────────────────────────────────┘│
│                                          │
│  ┌────────────────────────────────────┐│
│  │ Binlog(主从复制 + 数据恢复)          ││
│  └────────────────────────────────────┘│
└────────────────────────────────────────┘

1.2 索引结构(B+ 树)

B+ 树特点:
  - 非叶节点只存索引 → 树更扁平 → IO 少
  - 叶节点串成链表 → 范围查询快
  - 典型 3 层 B+ 树存 2000w 行(扇出 ~1280)

聚簇索引(主键):
  叶子节点 = 完整数据行
  
辅助索引(普通索引):
  叶子节点 = 主键值
  → 回表查询(主键索引找完整行)

1.3 慢 SQL 治理

EXPLAIN 关键字段: | 字段 | 含义 | 警告值 | |——|——|——–| | type | 访问类型 | const > eq_ref > ref > range > index > ALL(警告) | | rows | 扫描行数 | > 1000 警告 | | key | 实际使用的索引 | NULL 警告(全表扫描) | | Extra | 额外信息 | Using filesort / Using temporary 警告 |

慢日志配置:

slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 0.1               # 100ms 阈值
log_queries_not_using_indexes = 1

1.4 索引优化原则

最左前缀原则:
  联合索引 (a, b, c)
  ✅ WHERE a=? / WHERE a=? AND b=? / WHERE a=? AND b=? AND c=?
  ❌ WHERE b=? (跳过 a 用不上)
  ⚠️ WHERE a=? AND c=? (a 走索引,c 不走)

避免索引失效:
  ❌ 函数:WHERE YEAR(created_at) = 2026
  ❌ 隐式转换:WHERE phone = 13800138000(phone 是字符串)
  ❌ 前导通配:WHERE name LIKE '%abc%'
  ❌ OR 条件:WHERE a=1 OR b=2(b 没索引)
  ✅ 范围查询:WHERE id > 100(走索引)

二、ShardingSphere(简历主推)

2.1 架构对比

ShardingSphere 三种部署:

ShardingSphere-JDBC: (推荐,backbone 选这个)
  应用直连 ↘
  应用直连 → Sharding-JDBC SDK → MySQL 分片
  应用直连 ↗
  优点:无中间件,性能高
  缺点:需 Java 应用嵌入 SDK

ShardingSphere-Proxy:
  应用 → Proxy(看起来像 MySQL) → MySQL 分片
  优点:跨语言,运维方便
  缺点:多一跳网络

ShardingSphere-Sidecar:
  Service Mesh 模式

2.2 RPA 项目实战配置

spring:
  shardingsphere:
    datasource:
      names: ds0,ds1,...,ds15
      ds0:
        url: jdbc:mysql://db0:3306/rpa
        ...
    
    sharding:
      tables:
        ACT_RU_JOB:
          actualDataNodes: ds${0..15}.ACT_RU_JOB_${0..15}
          databaseStrategy:
            inline:
              shardingColumn: PROC_INST_ID_
              algorithmExpression: ds${HashSharding(PROC_INST_ID_, 16)}
          tableStrategy:
            inline:
              shardingColumn: PROC_INST_ID_
              algorithmExpression: ACT_RU_JOB_${HashSharding(PROC_INST_ID_, 16)}
      
      bindingTables:
        - ACT_RU_JOB,ACT_RU_EXECUTION
      
      defaultDatabaseStrategy:
        ...

2.3 分库分表关键决策

分片键(Sharding Key)选择:

  • ✅ 高基数(避免数据倾斜)
  • ✅ 业务查询常用(避免扫全片)
  • ✅ 不可变(分片后改动代价大)

RPA 项目选 processInstanceId:

  • 同一流程实例必然在同一分片(天然不跨分片)
  • 业务查询多按 processInstanceId 过滤
  • 高基数(每流程实例一个 ID)

2.4 分片数量选择

分库数 × 分表数 = 总分片数

举例:
16 库 × 16 表 = 256 分片
单分片预估 500w 行 → 总容量 12.8 亿行
单分片 IO 上限 ~5w QPS → 总 IO 256 × 5w = ~1280w QPS(理论)

经验值:

  • 单表行数 > 1000w 考虑分表
  • 单表数据 > 100GB 考虑分表
  • QPS > 5w 考虑分库

2.5 跨分片查询难题

痛点:
  ① 跨分片 JOIN — 慎用,性能差
  ② 跨分片排序 + 分页 — 内存合并,limit 大时灾难
  ③ 跨分片事务 — XA 慢 + 不可靠
  ④ 跨分片 COUNT — 各分片 COUNT 求和

解决:
  ① 绑定表(binding table):同一分片键的多个表绑定一起
  ② 广播表:小表全分片复制
  ③ 业务侧避免跨分片(分片键设计)
  ④ 跨分片必须分布式事务(Saga)

三、ShardingSphere vs MyCat 选型

维度 ShardingSphere-JDBC MyCat
部署 SDK 内嵌 独立 Proxy
性能 高(无网络跳) 中(多一跳)
跨语言 ❌ Java 限定 ✅ 任意语言
运维 跟应用一起 独立运维
社区活跃 ✅ Apache 顶级 一般
推荐 Java 项目 跨语言项目

四、连接池(简历踩坑核心)

4.1 Druid vs HikariCP

维度 Druid HikariCP
性能 ✅ 极高(Spring Boot 默认)
监控 ✅ 丰富(WebStat 大盘)
SQL 防御 ✅ WallFilter
国产 ✅ 阿里 国际
推荐 需要监控 性能优先

生产推荐: Spring Boot 默认 HikariCP + 业务侧需要监控时叠加 Druid

4.2 关键参数(Druid)

spring:
  datasource:
    druid:
      initial-size: 10                 # 初始连接数
      min-idle: 10                      # 最小空闲
      max-active: 100                   # 最大活跃
      max-wait: 60000                   # 获取连接超时(ms)
      time-between-eviction-runs-millis: 60000  # 空闲连接检查间隔
      min-evictable-idle-time-millis: 300000     # 连接最小空闲时间
      validation-query: SELECT 1
      test-on-borrow: false             # 高并发别 ping
      test-while-idle: true             # 空闲时 ping
      filters: stat,wall,slf4j          # 监控 + SQL 防御 + 日志

4.3 连接池估算公式(简历踩坑)

最大并发连接 ≥ 峰值 QPS × 平均调用次数 × 平均耗时(秒)

举例:
5w QPS × 2 次/请求 × 0.005s = 500 连接
留 2-4x buffer → 设置 max-active = 1000-2000

Elevate-SaaS 真实事故:

  • 默认 max-active=200 在 5w QPS 下耗尽
  • 改 2000 + minIdle=100 解决
  • Code Review 加规则:连接池上限必须按公式估算

五、面试 30/90 秒答题

MySQL 30 秒

“MySQL 必懂三件事:① B+ 树索引——非叶只存索引,典型 3 层存 2000w 行;② 聚簇索引(主键)叶节点存完整数据,辅助索引叶节点存主键要回表;③ EXPLAIN 看 type / rows / key / Extra,警告 ALL / filesort / temporary。索引优化最左前缀 + 避开函数 / 隐式转换 / 前导通配 / OR 条件 4 个坑。”

ShardingSphere 60 秒

“ShardingSphere-JDBC 是 SDK 内嵌方案,RPA 项目用它做 16×16 分库分表。分片键选 processInstanceId——高基数 + 业务查询常用 + 不可变;同一流程实例必然在同一分片,天然不跨分片。

跨分片查询 4 个难题:JOIN / 排序分页 / 事务 / COUNT。我们靠绑定表(同分片键的表绑定)+ 业务侧避免跨分片设计;真要跨分片走 Saga 不走 XA。

分片数量经验值:单表 > 1000w 行考虑分表,> 100GB 考虑分表,QPS > 5w 考虑分库。

连接池踩过 max-active=200 在 5w QPS 耗尽的坑——按公式估算(峰值 QPS × 调用次数 × 耗时)改 2000 + minIdle=100。”


第 2 部分 · 分布式事务(TCC / Saga / 本地消息表)

一、3 种方案对比

方案 一致性 业务侵入 性能 适用
2PC / XA 强一致 极差 几乎不用
TCC 强一致 ✅ 高(三阶段) 跨服务强一致(扣款/扣库存)
Saga 最终一致 中(每步要补偿) 长流程(订单/物流/通知)
本地消息表 最终一致 跨服务异步(最常用)
事务消息(RocketMQ) 最终一致 同上,RocketMQ 原生

二、TCC(Try-Confirm-Cancel)

2.1 三阶段

1. Try — 资源预留(冻结)
   - 订单:校验 + 创建预订单
   - 库存:冻结商品(stock - X 移到 frozen)
   - 账户:冻结金额(balance - X 移到 frozen)
   
2. Confirm — 提交(全部成功后)
   - 订单:状态改 PAID
   - 库存:frozen → deducted
   - 账户:frozen → deducted
   
3. Cancel — 回滚(任意失败)
   - 订单:删除
   - 库存:frozen 还回 stock
   - 账户:frozen 还回 balance

2.2 业务侧要实现

@Compensable(confirmMethod = "confirm", cancelMethod = "cancel")
public void try(...) {
    // 资源冻结
}

public void confirm(...) {
    // 真扣减
}

public void cancel(...) {
    // 资源释放
}

框架: Seata TCC / Hmily / ByteTCC

2.3 TCC 难点 · 空回滚 / 悬挂 / 幂等

空回滚: Try 没执行(网络失败),Cancel 先到 → 必须做空回滚处理(记录”未 try 过”) 悬挂: Cancel 已执行,Try 才到 → 必须拒绝 Try 幂等: Try / Confirm / Cancel 都可能重试,必须幂等

三、Saga(简历主推)

3.1 链式 + 补偿

T1 → T2 → T3 → T4 → T5(全部成功)
                    ↓
                  完成

T1 → T2 → T3 → ❌ T4 失败
                    ↓
                C3 → C2 → C1(逆向补偿)
                    ↓
                  回滚完成

每个 Ti 必须有对应的 Ci(补偿动作)

3.2 backbone DynamicWorkChain 真实场景

简历原文:

“DynamicWorkChain 工作流引擎,基于 Saga 实现’配置下发 → 设备校验 → 流量切换 → 回滚’的分布式事务保障”

public class ConfigDeployChain {
    
    @Step(order = 1, compensate = "rollbackConfig")
    public void deployConfig(Context ctx) {
        netconfClient.apply(ctx.config);
    }
    
    @Step(order = 2, compensate = "noop")  // 校验只读不需补偿
    public void verifyDevice(Context ctx) {
        if (!checkDevice(ctx)) throw new SagaException("verify failed");
    }
    
    @Step(order = 3, compensate = "rollbackTraffic")
    public void switchTraffic(Context ctx) {
        trafficController.switchTo(ctx.newPath);
    }
    
    public void rollbackConfig(Context ctx) {
        netconfClient.rollback(ctx.config);
    }
    
    public void rollbackTraffic(Context ctx) {
        trafficController.switchTo(ctx.oldPath);
    }
}

3.3 Saga vs TCC 关键差异

维度 Saga TCC
阶段 1 阶段(直接执行 + 失败补偿) 3 阶段(Try-Confirm-Cancel)
资源 业务直接占用 先冻结再确认
中间态 业务可见(部分操作已完成) 业务不可见(冻结)
适用 长流程、可补偿 强一致(扣款)

简单理解:

  • TCC 像”先冻结资金,后确认扣款”——强一致但侵入大
  • Saga 像”先扣款,失败再退款”——最终一致灵活

四、本地消息表(最常用)

4.1 流程

1. 业务事务(同 DB):
   BEGIN;
   INSERT INTO orders (...);                    -- 业务表
   INSERT INTO local_messages (event=...) ;     -- 消息表(同 DB!)
   COMMIT;
   
2. 异步发 MQ:
   定时任务扫 local_messages → 发 Kafka → 标记已发
   
3. 消费方处理:
   收到消息 → 业务处理 → ACK
   
4. 失败重试:
   消费失败不 ACK → MQ 重投
   消费方业务侧幂等

4.2 优势

  • 业务侵入小(只多写一张消息表)
  • 强一致 + 高性能(本地事务即可)
  • 不依赖特殊 MQ(Kafka / RabbitMQ 都可)

4.3 vs RocketMQ 事务消息

维度 本地消息表 RocketMQ 事务消息
实现 业务自己写 RocketMQ 原生
性能 中(有定时任务)
业务侵入 中(写消息表) 低(纯 API)
通用性 任何 MQ 仅 RocketMQ

五、面试 30/90 秒答题

30 秒

“分布式事务我落地过 3 种:Saga(backbone DynamicWorkChain 配置下发回滚)/ TCC(RPA 跨机器人执行)/ 本地消息表(最常用)。Saga 链式 + 补偿,长流程灵活;TCC 三阶段强一致但业务侵入大;本地消息表业务侵入小、不依赖特殊 MQ。”

90 秒

“分布式事务 5 种方案:2PC/XA(几乎不用)/ TCC(强一致)/ Saga(最终一致 + 长流程)/ 本地消息表(最常用)/ RocketMQ 事务消息(原生)。

backbone DynamicWorkChain 用 Saga——’配置下发 → 设备校验 → 流量切换 → 回滚’,每个 Step 带 compensate 方法,任一失败逆向触发补偿。比 TCC 优势是 1 阶段直接执行,适合长流程。

RPA 用 TCC——跨机器人扣资源,Try 冻结 + Confirm 确认 + Cancel 释放;难点是空回滚 / 悬挂 / 幂等三件事必须处理。

简单业务用本地消息表——业务事务 + 消息表写在同 DB,定时任务扫消息表发 MQ,消费方业务侧幂等。99% 业务场景本地消息表够用,不需要 TCC/Saga。”


第 3 部分 · 分布式 ID(Snowflake / Leaf)

一、3 大方案

1.1 Snowflake(经典 64 位)

位分布:
  1 位符号 + 41 位时间戳 + 10 位机器 ID + 12 位序列号
  
  = 1 bit + 41 bit + 10 bit + 12 bit = 64 bit
  
能力:
  - 时间戳 41 位 = 69 年
  - 机器 ID 10 位 = 1024 个节点
  - 序列号 12 位 = 单机每毫秒 4096 ID = 单机 4096w QPS

痛点: 时钟回拨(NTP 同步) → ID 重复

1.2 美团 Leaf-Snowflake(简历选这个)

改进:

  • workerId 用 ZK 持久顺序节点分配(不再人工配置)
  • 启动时校验时钟(防止机器时钟跳变)
  • 时钟回拨阈值内等待,超过则告警 + 切备机

简历踩坑:

  • 早期因 NTP 同步 + Pod 漂移导致机器 ID 复用 → ID 重复
  • 改 ZK 持久顺序节点 + 启动校验

1.3 Leaf-Segment(号段模式)

DB 表:
  ┌─────────┬─────────┬───────┬─────────┐
  │ tag     │ max_id  │ step  │ updated │
  ├─────────┼─────────┼───────┼─────────┤
  │ order   │ 10000   │ 1000  │ ...     │
  ├─────────┼─────────┼───────┼─────────┤
  │ user    │ 5000    │ 500   │ ...     │
  └─────────┴─────────┴───────┴─────────┘

App 启动:
  从 DB 拿一段(max_id-step+1, max_id),如 9001-10000
  本地用完了再去 DB 拿下一段(用 SELECT ... FOR UPDATE 保证唯一)

双 Buffer:
  当前段用了 50% 时,异步预取下一段,避免阻塞

1.4 百度 UidGenerator

改进 Snowflake:

  • 时间戳用秒级(而非毫秒)
  • 序列号扩展到 13 位 → 单秒 8192 ID
  • 机器位扩展到 22 位 → 400w 机器
  • 预生成 RingBuffer,业务线程拿号无锁(借鉴 Disruptor)

二、4 种方案选型

方案 性能 时钟回拨 业务无关 推荐
Snowflake 单机 4096w/s ❌ 痛点 简单场景
Leaf-Snowflake 同上 ✅ ZK 防御 生产推荐
Leaf-Segment 极高(本地 + 双 Buffer) ✅ 无关 ❌ 需 tag 业务相关 ID
UidGenerator 极高(RingBuffer) 性能极致

三、面试 30/60 秒答题

30 秒

“分布式 ID 4 种方案:Snowflake(经典 64 位)/ Leaf-Snowflake(美团,ZK 防时钟回拨,生产推荐)/ Leaf-Segment(号段模式 + 双 Buffer)/ 百度 UidGenerator(预生成 RingBuffer 性能极致)。backbone 选 Leaf-Snowflake——ZK 已经在,workerId 协调天然方便。踩过 NTP + Pod 漂移导致机器 ID 复用的坑,改 ZK 持久顺序节点 + 启动校验。”

60 秒

“Snowflake 64 位 = 1 符号 + 41 时间戳 + 10 机器 ID + 12 序列号,单机 4096w QPS,69 年。痛点是时钟回拨——NTP 同步会让 ID 重复。

Leaf-Snowflake 改进:① workerId 用 ZK 持久顺序节点分配(不人工配);② 启动校验时钟跳变;③ 回拨阈值 100ms 内等待超过告警切备机。生产推荐 Leaf-Snowflake——backbone 选这个因为 ZK 已经在。

Leaf-Segment 是号段模式——DB 拿一段 ID 本地用,双 Buffer 避免阻塞;适合业务相关 ID(订单号 / 用户 ID)。

UidGenerator 借鉴 Disruptor 预生成 RingBuffer,业务线程无锁拿号,性能极致。

踩过的坑:NTP 同步 + Pod 漂移导致机器 ID 复用 → 连续生成相同 ID → 改 ZK 持久顺序节点 + 启动校验解决。”


进度提示

批次 组件 状态
第 1 批 Kafka / Redis / ZooKeeper / Netty
第 2 批 Caffeine / Sentinel / SkyWalking / Dubbo
第 3 批 RabbitMQ / RocketMQ / Nacos / etcd
第 4 批 MySQL+ShardingSphere / 连接池 / TCC+Saga / Snowflake
第 5 批 Helm / Kustomize / Argo CD / Dapr / ZGC / Flowable 待续

组件深度补救 · 第 5 批 · Helm / Kustomize / Argo CD / Dapr / ZGC / Flowable / OpenFeign + 全部收尾

用途:简历剩余技术名词全部补救,30/60 秒级答案


第 1 部分 · Helm + Kustomize

一、Helm

1.1 核心概念

Chart  : 一个 K8s 应用的打包(类似 RPM)
Release: Chart 在集群的一次部署实例
Values : Chart 配置参数(从命令行/values.yaml 注入)
Template: Go template 渲染 K8s 资源 YAML

1.2 目录结构

my-app/
├── Chart.yaml              # 元数据(name/version/description)
├── values.yaml              # 默认配置
├── values-prod.yaml         # 环境覆盖
├── templates/
│   ├── deployment.yaml      # K8s 资源模板
│   ├── service.yaml
│   ├── configmap.yaml
│   ├── _helpers.tpl         # 公共函数
│   └── NOTES.txt            # 安装后提示
└── charts/                   # 依赖的子 Chart

1.3 常用命令

helm install my-release ./my-app -f values-prod.yaml
helm upgrade my-release ./my-app -f values-prod.yaml
helm rollback my-release 1
helm list
helm uninstall my-release
helm template ./my-app | kubectl apply -f -    # 不用 Helm 安装,只渲染 YAML

1.4 TenantOperator 调用 Helm Java SDK

// 用 marcnuri/helm-java
HelmClient helm = HelmClient.builder()
    .clusterConfiguration(client.getConfiguration())
    .build();

helm.install()
    .withName(tenant.getName() + "-app")
    .withChart("oci://registry/app:v1")
    .withNamespace(tenant.getName() + "-ns")
    .set("replicas", tenant.getSpec().getReplicas())
    .set("tenantId", tenant.getMetadata().getName())
    .build()
    .call();

二、Kustomize

2.1 核心概念

不用 Go template,纯 YAML patch:
  base/                       基础配置
  ├── deployment.yaml
  ├── service.yaml
  └── kustomization.yaml
  
  overlays/
  ├── dev/
  │   ├── kustomization.yaml  # patches base
  │   └── replica-patch.yaml
  └── prod/
      ├── kustomization.yaml
      └── replica-patch.yaml

2.2 vs Helm

维度 Helm Kustomize
模板引擎 Go template 无(纯 patch)
学习曲线 中(模板复杂)
版本管理 ✅ 原生支持 自己管
多环境 values 文件 overlays 目录
K8s 原生 第三方 ✅ kubectl 内置
推荐 Chart 复用 + 版本 多环境 patch

生产推荐:

  • Helm 管 Chart 版本(尤其是中间件如 Kafka / Redis)
  • Kustomize 管环境差异(dev / staging / prod)
  • 两者混用

三、面试 30/60 秒

30 秒

“Helm 是 K8s 应用打包工具——Chart(元数据 + 模板 + values)→ Release(部署实例)。TenantOperator 调用 Helm Java SDK 给每个租户安装业务模块,每个 Tenant CR 触发对应 HelmRelease。Kustomize 是 K8s 原生 patch 模式,适合多环境差异管理。生产 Helm + Kustomize 混用——Helm 管 Chart 版本,Kustomize 管环境 patch。”


第 2 部分 · Argo CD / Flux GitOps

一、GitOps 核心理念

传统 CI/CD:                       GitOps:
  代码 → CI → 直接 kubectl apply   代码 → CI → 推 Git → Argo/Flux 拉取 → 集群同步
                                   ↑
                                   Git 是 SoT(Source of Truth)

收益:
  ✅ Git 是真相 → 集群状态完全可追溯
  ✅ 任何变更走 PR → 审计 + 回滚便捷
  ✅ 集群漂移自动检测 + 修正

二、Argo CD 架构

┌──────────────────────────────────────────┐
│ Argo CD Server                             │
│  ┌────────────────────────────────────┐  │
│  │ Application Controller              │  │
│  │ - 监听 Git Repository                │  │
│  │ - 比较 desired vs actual             │  │
│  │ - 自动 sync                          │  │
│  └────────────────────────────────────┘  │
└──────────────────────────────────────────┘
              ↑                    ↓
              │ pull               │ apply
              │                    │
       ┌──────┴──────┐    ┌──────▼──────┐
       │ Git Repo    │    │ K8s Cluster  │
       │ (manifests) │    │              │
       └─────────────┘    └──────────────┘

三、Argo CD vs Flux

维度 Argo CD Flux
UI ✅ 强大
多租户 ✅ 完善
Helm 支持
Kustomize 支持
学习曲线
CNCF 状态 毕业 毕业

Elevate-SaaS 选 Flux(简历明确):

“Flux GitOps 管理中间件配置变更,审计可追溯零停机发布”

理由:更轻量 + 与 K8s API 集成更紧密。

四、面试 30/60 秒

30 秒

“Elevate-SaaS 用 Flux GitOps 管中间件配置变更——Git 是真相之源,任何配置改动走 PR,Flux 自动同步到集群,审计可追溯零停机发布。GitOps 比传统 CI/CD 优势是集群状态完全可追溯 + 漂移自动修正。Argo CD UI 强 / Flux 轻量,我们选 Flux 因为更贴 K8s API。”


第 3 部分 · Dapr Sidecar(简历反向决策)

一、Dapr 是什么

Dapr = Distributed Application Runtime
       
Sidecar 模式:每个 Pod 注入 dapr 容器
  
应用 → localhost:3500 → Dapr Sidecar → 实际中间件(Kafka/Redis/...)
                          ↑
                          统一 API,屏蔽底层差异
                          
Building Blocks(组件抽象):
  - Pub/Sub(Kafka/RabbitMQ/NATS/...)
  - State Management(Redis/Cosmos/...)
  - Service Invocation(HTTP/gRPC)
  - Bindings(Inputs/Outputs)
  - Actors
  - Workflow
  - Secrets / Configuration

二、调用方式

// 业务无需引入 Kafka SDK / Redis SDK,只调 localhost:3500

// Pub/Sub
restTemplate.postForEntity(
    "http://localhost:3500/v1.0/publish/kafka-pubsub/orders",
    order,
    Void.class);

// State Management(Redis)
restTemplate.postForEntity(
    "http://localhost:3500/v1.0/state/redis-store",
    new StateItem("key", "value"),
    Void.class);

// Service Invocation
restTemplate.getForObject(
    "http://localhost:3500/v1.0/invoke/user-service/method/users/123",
    User.class);

优势: 业务侧”YAML 声明式可替换中间件”,从自建机房迁公有云零代码改动。

三、简历真实反向决策

简历原文:

“选型反思 · Dapr Sidecar 成本:前期评估低估 Sidecar 资源占用,导致中小租户成本压力较大。后期实施租户分级:大客户保留 Dapr Sidecar 模式,中小客户改为 SDK 直连模式,整体资源成本回落至预期范围。”

Sidecar 资源开销:

  • 每 Pod 多 1 个容器
  • CPU 5-15%
  • 内存 50-150MB
  • 大客户能扛(资源充裕),中小客户占比 15%+ 不划算

租户分级方案:

# 大客户(L1 / L2):保留 Dapr Sidecar
spec:
  level: L1
  middleware: 
    via: dapr
    sidecar: enabled

# 中小客户(L3):SDK 直连
spec:
  level: L3
  middleware:
    via: sdk
    direct: kafka-cluster.internal:9092

四、面试 30/60 秒

30 秒

“Dapr Sidecar 让中间件可声明式替换,业务调 localhost:3500 不绑定具体 MQ/Cache。但中小租户 Sidecar 资源开销 15%+ 不划算——这是简历明确的反向决策之一,改成大客户保留 Sidecar + 中小 SDK 直连,整体资源成本回落到预期。”

60 秒

“Dapr 是 CNCF 项目,Sidecar 模式注入到每个 Pod;应用调 localhost:3500,Dapr 屏蔽底层中间件(Kafka/Redis/NATS 等)。Building Blocks 包括 Pub/Sub / State / Service Invocation / Bindings / Actors。

Elevate-SaaS 早期全租户用 Dapr Sidecar——’YAML 声明式可替换中间件’,从自建机房迁公有云零代码改动。

但踩过坑——Sidecar 资源开销 5-15% CPU + 50-150MB 内存,中小租户成本占比 15%+ 不划算。反向决策实施租户分级:大客户保留 Sidecar(资源充裕)+ 中小客户改 SDK 直连。整体资源成本回落到预期。

这是简历明确的反向决策案例之一——选型不能一刀切,要按客户规模分级。”


第 4 部分 · ZGC + 其他 GC

一、ZGC vs G1 vs CMS 完整对比

维度 CMS G1 ZGC Shenandoah
设计目标 低延迟 平衡 极低延迟 极低延迟
算法 标记清除 分 Region 染色指针 + 读屏障 Brooks 指针 + 读屏障
STW 几十 ms < 200 ms < 10 ms < 10 ms
内存额外 标准 标准 +15% +5-10%
CPU 开销 高(读屏障)
适合堆 < 16GB 4GB-64GB 8GB-16TB 8GB-16TB
JDK 状态 9 deprecated 14 移除 默认(9+) JDK 11 实验 / 15 GA OpenJDK

二、ZGC 核心设计

2.1 染色指针

传统指针 64 位:
  [0:0:0:address(64)]

ZGC 指针:
  [Marked0:Marked1:Remapped:Finalizable:address(44 位)]
   ↑       ↑       ↑       ↑
   GC 元信息编码到指针高位(占 4 位)
   不需要单独的 mark word

2.2 读屏障

应用读对象:
  Object obj = field;           // 这里隐式插入读屏障
                                 ↓
                             检查指针标记 (是否需要 remap)
                                 ↓
                             如果需要 → 后台并发整理

→ 几乎所有 GC 阶段都并发,STW 只在 root scan(几百 μs)

三、什么时候选 ZGC(简历真实判断)

简历相关:

“DetNetController/backbone-controller 用 G1,因为业务 SLA P99 50ms,G1 200ms 暂停留 4 倍 buffer 够用;ZGC 在 ARM 平台稳定性差且 +15% 内存开销不划算。”

选型决策:

✅ 选 ZGC 的场景:
  - 堆 > 32GB
  - 业务 SLA P99 < 50ms
  - JDK ≥ 15
  - 内存充裕(能接受 +15%)
  
❌ 不选 ZGC:
  - 堆 < 16GB(G1 够用)
  - 业务 SLA 200ms 可接受
  - ARM 平台(早期 ZGC 稳定性差)
  - 内存紧张

四、面试 30 秒

“GC 选型 4 种:CMS 已 deprecated;G1 默认推荐(4-64GB 堆 + 200ms 暂停);ZGC 极低延迟(< 10ms STW)但 +15% 内存开销;Shenandoah OpenJDK。backbone 16GB 堆 G1 够用没上 ZGC——ZGC 优势是 < 10ms STW 但内存代价大,在 200ms SLA 场景性价比不高。堆 > 32GB + SLA < 50ms 才选 ZGC。”


第 5 部分 · Flowable BPMN

一、Flowable 核心组件

┌──────────────────────────────────────────┐
│ Flowable Engine                            │
│                                            │
│  ┌────────────────────────────────────┐  │
│  │ Process Engine(流程引擎)           │  │
│  │ - BPMN 2.0 解析                     │  │
│  │ - 流程定义 / 实例 / 执行             │  │
│  └────────────────────────────────────┘  │
│                                            │
│  ┌────────────────────────────────────┐  │
│  │ Async Job Executor(异步任务调度)    │  │
│  │ - JobAcquireRunnable                 │  │
│  │ - ExecuteAsyncRunnable               │  │
│  │ - ACT_RU_JOB 表(持久化)              │  │
│  └────────────────────────────────────┘  │
│                                            │
│  ┌────────────────────────────────────┐  │
│  │ Task Service(用户任务)              │  │
│  │ History Service(历史)                │  │
│  │ Identity Service(用户/组)            │  │
│  │ Form Service(表单)                  │  │
│  └────────────────────────────────────┘  │
└──────────────────────────────────────────┘

二、二次开发 4 个改造点(简历主推)

2.1 Disruptor 替 ABQ

原生:ThreadPoolExecutor + ArrayBlockingQueue
改造:Disruptor MPMC + WorkerPool

2.2 分库分表

原生:单库单表 ACT_RU_JOB
改造:ShardingSphere 16×16 按 processInstanceId hash

2.3 乐观锁替部分悲观锁

原生:SELECT ... FOR UPDATE
改造:UPDATE ... WHERE REV_=? + REV_+1

2.4 责任链异步

原生:整个 Service Task 一个事务
改造:责任链 + CompletableFuture 异步,事务边界细分

三、踩坑(简历真实)

上线首周死锁回滚

现象: 客户现场 RPA 任务全部卡死,Arthas 看到两个 Worker 互相等锁

根因: 责任链改造后多线程加锁,资源 A→B 和 B→A 混存导致环形等待

修复:

  1. 所有锁按全局有序 ID 加锁(数学保证不死锁)
  2. ThreadMXBean 死锁检测告警(每 10s 一次)
  3. Chaos 压测(随机化资源依赖顺序)

四、TPS 数据(简历明确,带场景限定)

“TPS 由约 300 提升至千级,这个数据有场景限定——简单 BPMN 流程压测能跑到 3000+,但真实客户流程包含 RPC 和子流程、30 多个步骤,实测 800 TPS 左右。’千级’是个区间,具体提升幅度受流程复杂度影响。”

五、面试 30/60 秒

30 秒

“Flowable 6.x 二次开发改了 4 个点:Disruptor 替 ABQ、分库分表、乐观锁替部分悲观、责任链异步化。TPS 从 300 到千级,带场景限定——简单流程 3000+,真实客户 30+ 步骤复杂流程 800 TPS。上线首周踩过死锁回滚,根因是异步并发后多资源加锁顺序不一致;修复是按全局有序 ID 加锁 + ThreadMXBean 检测 + Chaos 压测。二次上线 6 个月零死锁。”

(详见 Q4 详解)


第 6 部分 · Spring Cloud OpenFeign + Spring Cloud 全家桶

一、OpenFeign

1.1 声明式 HTTP 调用

@FeignClient(name = "user-service", 
             fallback = UserServiceFallback.class)
public interface UserServiceClient {
    @GetMapping("/users/{id}")
    User getUser(@PathVariable("id") String id);
    
    @PostMapping("/users")
    User createUser(@RequestBody User user);
}

// 使用
@Autowired
private UserServiceClient userClient;

User u = userClient.getUser("123");   // 像本地调用

1.2 集成生态

组件 作用
Ribbon / LoadBalancer 客户端负载均衡
Sentinel / Hystrix 熔断降级
Sleuth / SkyWalking 链路追踪
Nacos / Eureka 服务发现

二、Spring Cloud 全家桶

┌──────────────────────────────────────────┐
│ Spring Cloud 微服务体系                    │
│                                            │
│  服务发现:Nacos / Eureka / Consul         │
│  配置中心:Nacos / Apollo / Config         │
│  网关:    Spring Cloud Gateway            │
│  限流熔断:Sentinel / Hystrix              │
│  RPC:     OpenFeign / Dubbo               │
│  追踪:    Sleuth + Zipkin / SkyWalking    │
│  日志:    ELK / Loki                      │
│  消息:    Stream(抽象 Kafka/RabbitMQ)     │
│  分布式事务:Seata                          │
└──────────────────────────────────────────┘

2.1 Spring Cloud Alibaba(简历主推)

组件 作用
Nacos 服务注册 + 配置中心
Sentinel 限流熔断
Seata 分布式事务
RocketMQ 消息中间件
Dubbo RPC

vs Spring Cloud Netflix(已停更):Alibaba 更新活跃 + 国产生态。

三、面试 30/60 秒

30 秒

“OpenFeign 声明式 HTTP 调用,集成 LoadBalancer / Sentinel / SkyWalking / Nacos。backbone 30+ 微服务同步调用大部分用 Feign + Sentinel 熔断。比 Dubbo 长连接性能略低但 HTTP 易调试。Spring Cloud Alibaba 全家桶——Nacos 注册配置 + Sentinel 限流 + Seata 事务 + RocketMQ + Dubbo,国产生态比 Netflix 活跃。”


第 7 部分 · gRPC

一、核心特性

HTTP/2 + Protobuf + 强类型 + 双向流

4 种调用模式:
  - Unary(一对一)
  - Server Streaming(一对多)
  - Client Streaming(多对一)
  - Bidirectional Streaming(多对多)

跨语言:Java / Go / Python / C++ / ...

二、IDL(.proto 文件)

syntax = "proto3";

service UserService {
    rpc GetUser(GetUserRequest) returns (User);
    rpc StreamUsers(StreamRequest) returns (stream User);
}

message GetUserRequest {
    string id = 1;
}

message User {
    string id = 1;
    string name = 2;
}

三、面试 30 秒

“gRPC 主要在跨语言场景用——VMPoolOperator(Go)跟 Java 控制器之间走 gRPC。HTTP/2 + Protobuf 紧凑高效;支持 4 种流(Unary / Server Stream / Client Stream / Bidi)。优点是性能高 + 跨语言 + 强类型;缺点是调试比 HTTP 麻烦,需要工具(BloomRPC / Postman)。”


第 8 部分 · Hystrix(已淘汰但常被问)

一、核心特性 + 现状

Hystrix(Netflix):
  - 熔断器模式开创者
  - 信号量 / 线程池隔离
  - 命令模式封装
  
现状:
  ❌ Netflix 2018 停止维护
  ✅ Resilience4j 接班(轻量,推荐)
  ✅ Sentinel(国产,推荐)

二、面试 30 秒

“Hystrix 是 Netflix 熔断器开创者,但 2018 年停止维护;现在推荐 Resilience4j(轻量)或 Sentinel(国产 + 集群限流)。我用 Sentinel 多——场景丰富 + 控制台 + 集群限流支持。如果遇到 Hystrix 历史代码迁移,熔断器模式是通用的,API 类似。”


第 9 部分 · ELK / Loki / Jaeger / Zipkin

一、ELK(Elasticsearch + Logstash + Kibana)

Filebeat → Logstash(解析)→ Elasticsearch(存储索引)→ Kibana(可视化)

特点:
  ✅ 完整生态 + 全文搜索强
  ❌ ES 集群运维成本高 + 资源消耗大

二、Loki(轻量替代)

Promtail(类似 Filebeat)→ Loki(只索引 label)→ Grafana

特点:
  ✅ 轻量(日志体走对象存储)
  ✅ 与 Prometheus 同生态
  ❌ 全文搜索弱

三、Jaeger / Zipkin

应用 → Jaeger Agent → Jaeger Collector → ES/Cassandra

特点:
  ✅ 标准协议(OpenTracing/OpenTelemetry)
  ✅ Uber 开源,生态广
  ❌ 拓扑分析弱(SkyWalking 强)

四、面试 30 秒

“可观测三支柱:Metrics / Logs / Traces。

  • Metrics:Prometheus + Grafana(默认栈)
  • Logs:ELK 完整但重 / Loki 轻量适合中小规模(我们 backbone 用 Loki)
  • Traces:SkyWalking 国产 + 字节码增强(我们选这个)/ Jaeger 协议标准生态广

backbone 30+ 微服务接入这套,链路定位 MTTD 30 分钟 → 1-3 分钟。”


第 10 部分 · ASM / Javassist 字节码增强

一、用途场景

场景 工具
APM 埋点(SkyWalking) Byte Buddy / ASM
AOP 切面 CGLib / Javassist
Mock 框架(Mockito) ByteBuddy
动态代理(JDK Proxy) 内置

二、ASM vs Javassist

维度 ASM Javassist
抽象层级 字节码级(Visitor) 类似 Java 源码
学习曲线 陡峭 缓和
性能 极高
应用 SkyWalking / ASM tree Hibernate / 早期 EJB

三、面试 30 秒

“字节码增强主要场景:APM 埋点(SkyWalking 用 ByteBuddy)/ AOP 切面 / Mock 框架。ASM 字节码级 Visitor 模式性能极高但学习陡;Javassist 类似源码缓和但性能略低。我做 SkyWalking 接入主要看 -javaagent 启动时的字节码织入,业务代码 0 改动。深度自己写过 ASM 工具的话工作量大,通常用现成框架。”


第 11 部分 · Service Mesh(Istio / Linkerd)

一、Service Mesh vs Spring Cloud

维度 Spring Cloud(SDK 模式) Service Mesh(Sidecar)
流量治理 业务代码 SDK 内 Envoy Sidecar 接管
mTLS 加密 业务集成 自动
多语言 ❌ Java 限定 ✅ 任意语言
业务侵入 高(SDK 升级影响业务) 低(Sidecar 独立)
资源开销 中(每 Pod +Sidecar)

二、Istio 核心组件

数据面:Envoy Proxy(每 Pod 注入)
控制面:Istiod(配置下发)

能力:
  - 流量治理(路由/灰度/熔断/重试)
  - 可观测(Metrics/Tracing 自动)
  - 安全(mTLS/AuthN/AuthZ)

三、面试 30 秒

“Service Mesh 把流量治理 / mTLS / 可观测下沉到 Sidecar——多语言 + 业务零改动。代价是每 Pod +Sidecar 5-15% CPU + 50-150MB 内存。

Mesh 替代部分中间件能力(注册发现 / LB / 熔断 / mTLS)但不能替代消息队列 / 分布式锁 / 缓存——Mesh 是横切关注点,中间件是业务依赖能力,本质不冲突

我们 Elevate-SaaS 用 Dapr Sidecar 偏中间件抽象,跟 Istio 的网络治理 Sidecar 是不同维度。”


第 12 部分 · 简历剩余零碎名词速答

一、Druid 数据库连接池(再补)

spring:
  datasource:
    druid:
      url: jdbc:mysql://...
      username: root
      password: ***
      
      # 监控大盘
      stat-view-servlet:
        enabled: true
        login-username: admin
        login-password: ***
        url-pattern: /druid/*
      
      # 防 SQL 注入
      filter:
        wall:
          enabled: true
          config:
            select-allow: true
            delete-allow: true
            update-allow: true

二、分布式 ID UidGenerator(百度)

基于 Snowflake:
  - 时间戳秒级(而非毫秒)
  - 序列号 13 位(每秒 8192 ID)
  - 机器位 22 位(400w 机器)
  - 预生成 RingBuffer + Disruptor 思路
  
特点:性能极致 + 时钟回拨容忍

三、SRv6 / Netconf / OpenFlow

SRv6: Segment Routing IPv6
  - 在 IPv6 包头嵌入路径信息(SID 列表)
  - 简化 MPLS 控制面
  - DetNet 多约束场景的核心承载

Netconf: 网络配置协议(RFC 6241)
  - XML 格式 + SSH 传输
  - 比 SNMP 强(支持事务、回滚)

OpenFlow: SDN 控制协议
  - 控制器 → 转发设备
  - 流表下发 + 包处理动作

四、Tableau / 可视化(简历未提但常被问)

“数据可视化我用过 Grafana / Kibana / DataV(阿里),不熟 Tableau。Grafana 是默认监控可视化栈,跟 Prometheus 集成最深;Kibana 是 ELK 日志可视化;DataV 是阿里云的大屏可视化。如果硕磐有特定可视化需求我可以快速学。”


收尾 · 全部组件清单 + 答题速查

已覆盖的 50+ 个组件

第 1 批(核心 4)

  1. Kafka - 消息总线主推
  2. Redis - 缓存主推
  3. ZooKeeper / Curator - 选主主推
  4. Netty - 网关主推

第 2 批(微服务 4)

  1. Caffeine - 本地缓存
  2. Sentinel - 限流熔断
  3. SkyWalking - 链路追踪
  4. Dubbo - RPC

第 3 批(MQ + 注册中心 4)

  1. RabbitMQ - 备选 MQ
  2. RocketMQ - 事务消息
  3. Nacos - 多租户配置
  4. etcd - K8s 后端

第 4 批(数据 + 事务 + ID 4)

  1. MySQL + ShardingSphere - 分库分表
  2. Druid / HikariCP - 连接池
  3. TCC / Saga / 本地消息表 / 事务消息 - 分布式事务
  4. Snowflake / Leaf / UidGenerator - 分布式 ID

第 5 批(其余 + 收尾)

  1. Helm / Kustomize - 部署模板
  2. Argo CD / Flux - GitOps
  3. Dapr Sidecar - 中间件抽象
  4. ZGC / G1 / CMS - GC 选型
  5. Flowable - 工作流
  6. OpenFeign + Spring Cloud Alibaba - 微服务全家桶
  7. gRPC - 跨语言 RPC
  8. Hystrix - 熔断(已淘汰)
  9. ELK / Loki / Jaeger / Zipkin - 可观测
  10. ASM / Javassist - 字节码增强
  11. Istio / Service Mesh - 网络治理
  12. SRv6 / Netconf / OpenFlow - 网络协议

进度提示 · 全部完成 ✅

批次 组件 状态
第 1 批 Kafka / Redis / ZooKeeper / Netty
第 2 批 Caffeine / Sentinel / SkyWalking / Dubbo
第 3 批 RabbitMQ / RocketMQ / Nacos / etcd
第 4 批 MySQL+ShardingSphere / 连接池 / TCC+Saga / Snowflake
第 5 批 Helm / Argo / Dapr / ZGC / Flowable / OpenFeign + 收尾

终极速查 · 被问到任何组件,30 秒套这个模板

1. 一句话定位: "X 是 Y 的解决方案"
2. 我的场景: "我在 [项目] 用它做 [具体事],数据 [带场景限定]"
3. 关键设计: "底层是 [核心机制],参数是 [关键配置]"
4. 踩坑: "踩过 [真实坑],修复 [方案],写进 Code Review"
5. 选型: "vs [备选] 我选它因为 [一句理由]"

核心原则:

  • 用过的 → 直接讲场景 + 数据 + 踩坑
  • 不太熟的 → 承认 + 类比 + 表态(1-2 周补到位)
  • 完全没用过 → 不硬编,主动说”原理懂但生产没深度”

杀手句库:

  1. Kafka:”高吞吐本质是 OS 级零拷贝,不是 JVM 调优”
  2. Redis:”性能不靠多线程,靠纯内存 + epoll + O(1) 数据结构”
  3. ZK:”协调原语之王,但新项目优先 etcd”
  4. Netty:”主从 Reactor + 线程亲和 + 零拷贝 4 层”
  5. Dapr:”Sidecar 不能一刀切,要按客户规模分级”
  6. Operator:”业务逻辑重 Java,系统调用重 Go”
  7. GC:”不要凭经验调参,要用数据”
  8. 限流:”千万级 QPS 必走中心+本地”
  9. 分布式事务:”99% 业务场景本地消息表够用”
  10. 可观测:”Metrics + Logs + Traces 三支柱联动”

评论:


技术文章推送

手机、电脑实用软件分享

微信搜索公众号: AndrewYG的算法世界
wechat 微信公众号:AndrewYG的算法世界

热门文章