分布式中间件QQ
-
date_range 23/01/2023 21:37
点击量:次infosort网随云动label
技术组件 × 场景 全覆盖速查手册
用途:面试被问”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 分钟):
- Kafka(简历最重)
- Redis + Redisson
- ZooKeeper(选主 / 脑裂防御)
- Netty(零拷贝 / 主从 EventLoop)
- G1 GC + Arthas 排查
- CRD 五件套
- Disruptor(含水分认知)
🔵 次熟(每个 3 分钟):
- Sentinel(千万级架构)
- ShardingSphere(分库分表)
- SkyWalking(链路追踪)
- JOSDK + controller-runtime 选型
- Saga / TCC / 本地消息表
- Caffeine(防 Map 无上限坑)
- 连接池配置(Druid / Hikari / Jedis)
- Helm + Kustomize
⚪ 速读(每个 1-2 分钟,知道场景就行):
- RabbitMQ / RocketMQ
- Nacos / Consul / etcd
- Dubbo
- ZGC / CMS
- Argo CD / Flux
- ELK / Loki / Jaeger
- Snowflake / Leaf
- Dapr Sidecar
- 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 大角色:
- Producer — 生产消息
- Broker — 存储消息(集群中多台)
- Controller — Broker 之一,管元数据(KRaft 后由 Raft Leader 担任)
- Topic / Partition — 逻辑分类 + 物理分片
- Consumer — 消费消息
- 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 个关键词:
- 顺序追加 + PageCache + mmap + sendfile(高吞吐 4 件套)
- ISR 多数派(副本一致性)
- PID + Sequence(单会话幂等)
- Static Membership + CooperativeStickyAssignor(防 Rebalance 风暴)
- 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(哨兵)
职责:
- 监控 Master 健康
- Master 失联自动 failover
- 客户端订阅 Sentinel 获取 Master 地址变更
部署: 3 或 5 个 Sentinel,多数派达成共识
failover 流程:
- Sentinel 主观下线(单 Sentinel 心跳超时)
- 多数派客观下线(quorum)
- 选 Leader Sentinel 执行 failover
- 在 Slave 中选新 Master(优先级 / 复制偏移量 / runid)
- 通知其他 Slave 复制新 Master
- 通知客户端
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 个关键词:
- SDS / ziplist / skiplist + hashtable(数据结构底层)
- RDB fork + AOF everysec(持久化)
- PSYNC + repl_backlog(增量同步)
- BloomFilter / 互斥锁 / 随机 TTL(三大问题防御)
- 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 混存导致环形等待
修复:
- 所有锁按全局有序 ID 加锁(数学保证不死锁)
- ThreadMXBean 死锁检测告警(每 10s 一次)
- 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)
- ✅ Kafka - 消息总线主推
- ✅ Redis - 缓存主推
- ✅ ZooKeeper / Curator - 选主主推
- ✅ Netty - 网关主推
第 2 批(微服务 4)
- ✅ Caffeine - 本地缓存
- ✅ Sentinel - 限流熔断
- ✅ SkyWalking - 链路追踪
- ✅ Dubbo - RPC
第 3 批(MQ + 注册中心 4)
- ✅ RabbitMQ - 备选 MQ
- ✅ RocketMQ - 事务消息
- ✅ Nacos - 多租户配置
- ✅ etcd - K8s 后端
第 4 批(数据 + 事务 + ID 4)
- ✅ MySQL + ShardingSphere - 分库分表
- ✅ Druid / HikariCP - 连接池
- ✅ TCC / Saga / 本地消息表 / 事务消息 - 分布式事务
- ✅ Snowflake / Leaf / UidGenerator - 分布式 ID
第 5 批(其余 + 收尾)
- ✅ Helm / Kustomize - 部署模板
- ✅ Argo CD / Flux - GitOps
- ✅ Dapr Sidecar - 中间件抽象
- ✅ ZGC / G1 / CMS - GC 选型
- ✅ Flowable - 工作流
- ✅ OpenFeign + Spring Cloud Alibaba - 微服务全家桶
- ✅ gRPC - 跨语言 RPC
- ✅ Hystrix - 熔断(已淘汰)
- ✅ ELK / Loki / Jaeger / Zipkin - 可观测
- ✅ ASM / Javassist - 字节码增强
- ✅ Istio / Service Mesh - 网络治理
- ✅ 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 周补到位)
- 完全没用过 → 不硬编,主动说”原理懂但生产没深度”
杀手句库:
- Kafka:”高吞吐本质是 OS 级零拷贝,不是 JVM 调优”
- Redis:”性能不靠多线程,靠纯内存 + epoll + O(1) 数据结构”
- ZK:”协调原语之王,但新项目优先 etcd”
- Netty:”主从 Reactor + 线程亲和 + 零拷贝 4 层”
- Dapr:”Sidecar 不能一刀切,要按客户规模分级”
- Operator:”业务逻辑重 Java,系统调用重 Go”
- GC:”不要凭经验调参,要用数据”
- 限流:”千万级 QPS 必走中心+本地”
- 分布式事务:”99% 业务场景本地消息表够用”
- 可观测:”Metrics + Logs + Traces 三支柱联动”
评论:
技术文章推送
手机、电脑实用软件分享
微信公众号:AndrewYG的算法世界