vault backup: 2026-08-09 19:06:40
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
---
|
||||
tags: [test/review, architecture, arch/cicd]
|
||||
create time: 2026-08-09 12:00
|
||||
---
|
||||
|
||||
# K8s 发布与可观测性体系 — 测试题
|
||||
|
||||
## 概述
|
||||
本测试覆盖 RollingUpdate 策略详解、蓝绿部署 vs Canary 发布、HPA/VPA 自动扩缩容、Prometheus 指标采集链路、Grafana Dashboard 数据源以及日志收集方案。共 10 道题目(6 选择 + 3 填空 + 1 简答)。
|
||||
|
||||
---
|
||||
|
||||
## 一、选择题(6道,由浅入深)
|
||||
|
||||
> **难度阶梯**: Q1-Q2 基础概念 → Q3-Q4 核心原理 → Q5-Q6 深入应用/边界场景
|
||||
|
||||
### Q1(基础)— 考察定义层面
|
||||
|
||||
K8s Deployment 的 RollingUpdate 策略中,`maxUnavailable=0` 和 `maxSurge=1` 的含义是:
|
||||
|
||||
A. 更新期间最多有 0 个新 Pod 同时运行
|
||||
B. 逐台升级,保证零停机——每次只启动 1 个新 Pod,旧 Pod 终止后再启动下一个
|
||||
C. 所有 Pod 同时替换,然后检查是否可用
|
||||
D. 先删除全部旧 Pod 再创建新 Pod
|
||||
|
||||
### Q2(基础)→
|
||||
|
||||
Canary 发布相比蓝绿部署的主要优势是什么?
|
||||
|
||||
A. 回滚速度更快
|
||||
B. 资源消耗更低
|
||||
C. 风险最低——只有小部分用户先体验新版本
|
||||
D. 实现复杂度更低
|
||||
|
||||
### Q3(进阶)— 核心原理
|
||||
|
||||
HPA(Horizontal Pod Autoscaler)扩容的基本计算公式是:
|
||||
|
||||
A. `targetReplicas = currentReplicas * desiredMetricValue / currentMetricValue`
|
||||
B. `targetReplicas = ceil(currentReplicas * currentMetricValue / desiredMetricValue)`
|
||||
C. `targetReplicas = currentReplicas + (currentMetricValue - desiredMetricValue)`
|
||||
D. `targetReplicas = floor(currentReplicas * 2)`
|
||||
|
||||
### Q4(进阶)— 比较/辨析
|
||||
|
||||
以下关于 Prometheus Pull 模式的说法哪个是正确的?
|
||||
|
||||
A. Push 模式下失联的服务会立即被标记为 down
|
||||
B. Pull 模式下失联的 service 会自动被忽略(不再收到数据即标记为 down)
|
||||
C. Prometheus 不支持自定义业务指标
|
||||
D. Prometheus 的采集间隔是实时的,没有延迟
|
||||
|
||||
### Q5(深入)— 场景推理
|
||||
|
||||
某秒杀系统需要在流量洪峰到来前自动扩容。以下方案最合理的是:
|
||||
|
||||
A. 完全依赖 HPA,等 CPU 使用率上升后触发扩容
|
||||
B. 提前设置 minReplicas >= 预期峰值的 80%,配合 HPA 动态调整上限
|
||||
C. 使用 VPA Auto 模式代替 HPA
|
||||
D. 手动扩容并在活动结束后手动缩容
|
||||
|
||||
### Q6(深入)— 源码级/边界场景
|
||||
|
||||
关于 Prometheus 的四种核心指标类型,以下匹配哪个是错误的?
|
||||
|
||||
A. Counter — 单调递增计数器,适合画 Rate 曲线
|
||||
B. Gauge — 可上可下的仪表值,如活跃连接数
|
||||
C. Histogram — 分桶统计,自动生成 count + sum + bucket
|
||||
D. Summary — 服务端计算分位数(P50/P99),客户端不做计算
|
||||
|
||||
---
|
||||
|
||||
## 二、填空题(3道)
|
||||
|
||||
### F1 — 填空1
|
||||
|
||||
K8s 中新 Pod 必须通过 _____ Probe 才会被加入 Service 的 Endpoint 列表。如果失败,即使新 Pod 已 Running 也不会接收流量。livenessProbe 用于检测进程存活并可能触发重启;而 _____ Probe 用于决定"这个 Pod 是否可以接受流量"。
|
||||
|
||||
> **提示**: 两个空填同一种类型的探针名称。
|
||||
|
||||
### F2 — 填空2
|
||||
|
||||
Fluentd/Filebeat DaemonSet 采集日志后的两种典型存储方案:**ELK 栈**建立_____索引(搜索能力强但存储成本高);**Loki**不建立全文索引,只索引_____(Labels),存储成本极低。
|
||||
|
||||
> **提示**: ELK 的核心搜索机制和 Loki 的区别。
|
||||
|
||||
### F3 — 填空3
|
||||
|
||||
VPA(Vertical Pod Autoscaler)的 Offline 模式行为是_____——只推荐建议值而不直接应用,人工审核后手动调整。这种模式适合_____阶段。
|
||||
|
||||
> **提示**: 原文 VPA 表格中的描述。
|
||||
|
||||
---
|
||||
|
||||
## 三、简答题(1道)
|
||||
|
||||
### S1
|
||||
|
||||
面试官问:"HPA 能基于自定义业务指标(如 Kafka 消费 lag、QPS)进行扩缩容吗?如何做到?"请给出完整回答,包括必要的组件链和技术路径。
|
||||
|
||||
> **答题框架提示**:
|
||||
> 1. 明确回答能否
|
||||
> 2. 解释 Custom Metrics API 的工作原理
|
||||
> 3. 列出关键组件及其职责
|
||||
> 4. 结合实际场景举例
|
||||
|
||||
---
|
||||
|
||||
## 参考答案与解析
|
||||
|
||||
### 选择题答案
|
||||
|
||||
| 题号 | 正确答案 | 解析 |
|
||||
|------|---------|------|
|
||||
| Q1 | B | maxSurge=1 表示更新过程中允许超出目标副本数 1 个,maxUnavailable=0 表示不允许任何不可用实例。逐台升级确保零停机,但速度慢。生产环境高可用场景推荐此配置。 |
|
||||
| Q2 | C | Canary 只有少量流量进入新版本(如 10%),大部分用户还在稳定版。这样风险最低——有问题也只影响一小部分用户。蓝绿需要双倍资源,且一旦切到新版本就是全部流量都有问题。 |
|
||||
| Q3 | B | `ceil(current * current_metric / desired_metric)`。例如当前 5 副本,CPU 91%,目标 70% → ceil(5 × 91 / 70) = ceil(6.5) = 7。注意分子是当前值、分母是目标值。 |
|
||||
| Q4 | B | A 错:Push 模式下挂了停止发数据但采集器不知道不存在了;C 错:通过 Custom Metrics API + adapter 可以读任意业务指标;D 错:采集间隔 15~60 秒,有分钟级延迟。 |
|
||||
| Q5 | B | HPA 不是实时的(依赖 metrics-server 15~60 秒采样),等 CPU 上升再扩容已经来不及。minReplicas 预热的做法可以应对已知高峰。VPA 不适合因为它是垂直扩缩(改资源配额需重建 Pod),不能快速应对流量变化。 |
|
||||
| Q6 | D | D 错误——Summary 的分位数计算在**客户端**完成(SDK 内部算 P50/P99),服务器端不做计算但有额外开销。Histogram 是在服务器端做分桶统计。原文原话:Summary = 客户端计算分位数。 |
|
||||
|
||||
### 填空题答案
|
||||
|
||||
| 题号 | 答案 | 解析 |
|
||||
|------|------|------|
|
||||
| F1 | `readiness`;`readiness` | Readiness Probe 决定 Pod 是否能接收流量(加入 Endpoint)。livenessProbe 检测进程是否存活。二者职责不同:Ready = 可服务,Live = 没挂。 |
|
||||
| F2 | `全文`;`Labels` | ELK 对每个字段建 inverted index 支持全文检索,但 JSON 结构化索引非常占空间。Loki 只对 Labels 建索引,查询时先用 label 过滤再检索内容,存储成本低几个数量级。 |
|
||||
| F3 | `只推荐不应用`;`微服务刚上线` | 刚上线阶段运维难以精确预估资源需求,可以让 VPA 学习实际使用模式后给出最优建议。Offline 模式不会自动修改 Pod Spec,避免误配导致业务中断。 |
|
||||
|
||||
### 简答题参考答案
|
||||
|
||||
S1:**参考答案要点**:
|
||||
1. **明确回答**:可以。HPA 不仅支持 CPU/内存指标,还支持自定义业务指标。
|
||||
2. **Custom Metrics API**:HPA 通过 Kubernetes Custom Metrics API 获取任意指标值,不限于 K8s 原生指标。
|
||||
3. **关键组件链**:Prometheus adapter(或 custom-metrics-adapter)负责将 Prometheus 中的数据暴露给 Custom Metrics API。流程:App 暴露 /metrics → Prometheus scrape → adapter 查询 Prometheus → HPA 读取指标 → 计算目标副本数。
|
||||
4. **实际例子**:根据 Kafka consumer lag 扩缩容——当 lag > 阈值时 HPA 增加 Pod 数加快消费;lag < 阈值时缩容节省资源。也可以根据 QPS、错误率等业务指标触发。
|
||||
|
||||
**评分标准**:答出任意 2 个要点即可得满分;完全正确需覆盖全部要点。
|
||||
|
||||
## 关联笔记
|
||||
- [[负载均衡算法]]
|
||||
- [[限流熔断降级]]
|
||||
@@ -0,0 +1,142 @@
|
||||
---
|
||||
tags: [test/review, architecture, arch/distributed, redis-distributed-lock]
|
||||
create time: 2026-08-09 12:00
|
||||
---
|
||||
|
||||
# Redis 分布式锁 — 测试题
|
||||
|
||||
## 概述
|
||||
本测试覆盖 Redis 分布式锁的核心原理,包括 SETNX 原子性陷阱、Redlock 多实例容错、Redisson 看门狗续期、可重入锁 hash key 设计以及常见死锁场景分析。共 10 道题目(6 选择 + 3 填空 + 1 简答)。
|
||||
|
||||
---
|
||||
|
||||
## 一、选择题(6道,由浅入深)
|
||||
|
||||
> **难度阶梯**: Q1-Q2 基础概念 → Q3-Q4 核心原理 → Q5-Q6 深入应用/边界场景
|
||||
|
||||
### Q1(基础)— 考察定义层面
|
||||
|
||||
在 Redis 中尝试获取分布式锁时,以下哪段代码存在**最严重**的缺陷?
|
||||
|
||||
A. `redis.set("lock", uuid, "NX")`
|
||||
B. `redis.set("lock", uuid, "NX", "EX", 30)`
|
||||
C. `redis.setnx("lock", uuid); redis.expire("lock", 30)`
|
||||
D. Lua 脚本:`if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) end`
|
||||
|
||||
### Q2(基础)→
|
||||
|
||||
使用 SET NX EX 获取锁后,客户端在持有锁期间宕机了。当锁过期时间到达时,Redis 会自动删除该 key。但如果在业务执行完成之前锁就过期了,会出现什么问题?
|
||||
|
||||
A. Redis 会阻塞等待业务完成后再删除 key
|
||||
B. 其他客户端可能获取到同一把锁,导致数据竞争
|
||||
C. 自动触发 Redlock 选举新的持有者
|
||||
D. Lua 脚本检测到异常,自动延长锁有效期
|
||||
|
||||
### Q3(进阶)— 核心原理
|
||||
|
||||
Redisson 的看门狗(Watch Dog)机制在什么条件下才会启动?
|
||||
|
||||
A. 每次调用 `lock.lock()` 时都会启动
|
||||
B. 只在传入 leaseTime 参数时启动
|
||||
C. 只有在加锁时**不指定** leaseTime 参数时才启动
|
||||
D. 看门狗需要手动通过 `startWatchDog()` 命令启用
|
||||
|
||||
### Q4(进阶)— 比较/辨析
|
||||
|
||||
Redisson 实现可重入锁的数据结构是 Hash 类型,其 key 中存储的内容是什么?
|
||||
|
||||
A. client_id 和整数计数
|
||||
B. threadID 和重入次数
|
||||
C. UUID 和时间戳
|
||||
D. hostname 和端口号
|
||||
|
||||
### Q5(深入)— 场景推理
|
||||
|
||||
在一个包含 5 个独立 Redis 实例的 Redlock 部署中,客户端依次向所有 5 个实例发送 SET lock uuid NX EX 30。其中实例 R1、R3、R5 返回 OK,R2 返回 nil,R4 返回 OK。总耗时 T = 12ms。请问以下判断正确的是:
|
||||
|
||||
A. 加锁失败,因为只拿到了 3/5,未达到多数派
|
||||
B. 加锁成功,有效 TTL 为 30s
|
||||
C. 加锁成功,有效 TTL 约为 19.988s
|
||||
D. 加锁成功,有效 TTL 为 20ms
|
||||
|
||||
### Q6(深入)— 源码级/边界场景
|
||||
|
||||
关于解锁操作,为什么必须用 Lua 脚本保证"检查 value + 删除"的原子性?下面哪个场景最能说明不加原子性的危险?
|
||||
|
||||
A. 客户端 A 读取到 key 的 value 是自己的 clientId,但在执行 DEL 之前,锁已过期且客户端 B 重新获得了锁并设置了新的 value。此时 A 的 DEL 会把 B 的锁删掉。
|
||||
B. Redis 主从切换时主节点未同步 DEL 命令导致从节点仍有锁
|
||||
C. 客户端网络抖动导致 DEL 超时,锁永远不会被释放
|
||||
D. SET NX 在高并发下存在多个客户端同时返回 OK 的情况
|
||||
|
||||
---
|
||||
|
||||
## 二、填空题(3道)
|
||||
|
||||
### F1 — 填空1
|
||||
|
||||
Redlock 算法要求获得至少 _____ 个节点的 OK 响应才能判定加锁成功(假设部署了 5 个实例)。有效 TTL 的计算公式是:_____ − T(T 为总耗时)。
|
||||
|
||||
> **提示**: 回忆 Redlock 的核心规则中关于 N/2+1 的部分。
|
||||
|
||||
### F2 — 填空2
|
||||
|
||||
Redisson 看门狗的后台任务每 _____ 秒检查一次锁是否仍持有,每次续期到默认 _____ 秒。
|
||||
|
||||
> **提示**: 原文中有一个表格列出了看门狗的行为细节。
|
||||
|
||||
### F3 — 填空3
|
||||
|
||||
解锁 Lua 脚本的核心逻辑:只有当 `redis.call("get", key)` 返回的值等于传入的 _____ 时,才执行删除操作。这是为了防止误删其他客户端持有的锁。
|
||||
|
||||
> **提示**: 考虑竞态场景中谁有资格删除这把锁。
|
||||
|
||||
---
|
||||
|
||||
## 三、简答题(1道)
|
||||
|
||||
### S1
|
||||
|
||||
某金融系统需要在用户扣款接口上做防重复提交。面试官问你:"为什么不依赖 Redis 分布式锁来保证幂等,而要在数据库层面再加一层唯一约束+CAS?"请结合 Redis 分布式锁的局限性,给出你的完整回答思路。
|
||||
|
||||
> **答题框架提示**:
|
||||
> 1. 从锁本身的可靠性局限谈起(clock drift、主从切换)
|
||||
> 2. 说明即使锁正确工作也无法替代的业务语义需求
|
||||
> 3. 提出"双重保障"架构建议
|
||||
|
||||
---
|
||||
|
||||
## 参考答案与解析
|
||||
|
||||
### 选择题答案
|
||||
|
||||
| 题号 | 正确答案 | 解析 |
|
||||
|------|---------|------|
|
||||
| Q1 | A | A 选项只用了 SET NX 而没有设置过期时间(EX/PX),客户端宕机后锁永久不释放——死锁。B 是正确写法(原子 SET NX EX),C 虽然非原子但有 expire 兜底,D 是正确的解锁 Lua 脚本而非加锁。 |
|
||||
| Q2 | B | 锁自动释放后,其他客户端可以获取到同一把锁,而此时原客户端仍在执行业务逻辑,两个客户端同时对同一资源做写操作,必然产生数据竞争。 |
|
||||
| Q3 | C | 看门狗只在**不指定** leaseTime 时启动。如果明确传入了 leaseTime,Redisson 认为你已经知道业务执行时间上限,无需续期。 |
|
||||
| Q4 | B | Redisson 使用 Hash 存储 `{threadID}: count`,threadID 标识是哪个线程获得了锁,count 记录重入次数。unlock 时递减计数,归零才真正删除 key。 |
|
||||
| Q5 | C | 5 个实例中获得 4 个 OK,4 >= 5/2+1=3,满足多数派所以加锁成功。有效 TTL = 30s - 0.012s ≈ 29.988s。 |
|
||||
| Q6 | A | 这是经典的"误删他人锁"场景:没有 value 校验就直接 DEL,任何读到旧 value 的客户端都可以删除当前持有者的锁。Lua 脚本将 get + del 打包成原子操作是唯一解。 |
|
||||
|
||||
### 填空题答案
|
||||
|
||||
| 题号 | 答案 | 解析 |
|
||||
|------|------|------|
|
||||
| F1 | `3`;`30s`(或"设定值") | Redlock 要求 ≥ N/2+1 = 3 个实例 OK。有效 TTL = 设定过期时间 − 实际耗时,确保即使部分节点因网络延迟稍晚收到 DEL 也不会误判。 |
|
||||
| F2 | `10`;`30` | 每 10 秒检查一次,续期到默认 30 秒。这意味着只要业务不超过 30 秒且不连续错过 3 次续期,锁就不会意外释放。 |
|
||||
| F3 | `clientId`(或"客户端标识值") | 只有 value 匹配证明当前客户端确实是锁的持有者,才允许删除。否则可能是锁过期后被新客户端重新获取的场景。 |
|
||||
|
||||
### 简答题参考答案
|
||||
|
||||
S1:**参考答案要点**:
|
||||
1. **时钟漂移风险**:NTP 同步异常可能导致系统时钟回拨,使得一个实例已释放锁而客户端仍持旧锁,Redlock 也不能完全规避此问题。
|
||||
2. **主从切换丢锁**:Redis 单实例在主从切换时,主节点上的锁可能还未复制到从节点就宕机了,从节点成为新主后锁丢失。
|
||||
3. **业务语义不足**:分布式锁只能保证互斥访问,不能保证"之前是否已经成功执行过"——如果第一次请求成功但网络超时导致客户端重试,锁无法区分这两次请求。
|
||||
4. **数据库兜底的不可替代性**:唯一索引 + CAS 是最终防线,即使锁层面全部失效,数据库层面仍然能保证不会重复扣款。
|
||||
5. **最佳实践**:应采用"锁 + 数据库二次校验"的双重保障策略,锁提升性能减少冲突,数据库保证最终一致性。
|
||||
|
||||
**评分标准**:答出任意 2 个要点即可得满分;完全正确需覆盖全部要点。
|
||||
|
||||
## 关联笔记
|
||||
- [[ZooKeeper 分布式锁]]
|
||||
- [[限流熔断降级]]
|
||||
@@ -0,0 +1,146 @@
|
||||
---
|
||||
tags: [test/review, architecture, arch/distributed, zookeeper-distributed-lock]
|
||||
create time: 2026-08-09 12:00
|
||||
---
|
||||
|
||||
# ZooKeeper 分布式锁 — 测试题
|
||||
|
||||
## 概述
|
||||
本测试覆盖 ZooKeeper 分布式锁的核心机制,包括临时顺序节点创建、Watch 一次性触发、公平锁实现原理、ZAB 协议 CP 保证以及与 Redis 锁的系统级对比。共 10 道题目(6 选择 + 3 填空 + 1 简答)。
|
||||
|
||||
---
|
||||
|
||||
## 一、选择题(6道,由浅入深)
|
||||
|
||||
> **难度阶梯**: Q1-Q2 基础概念 → Q3-Q4 核心原理 → Q5-Q6 深入应用/边界场景
|
||||
|
||||
### Q1(基础)— 考察定义层面
|
||||
|
||||
ZK 分布式锁中创建节点的类型是什么,使得客户端断开连接后能自动清理锁?
|
||||
|
||||
A. PERSISTENT — 持久节点,需手动删除
|
||||
B. EPHEMERAL_SEQUENTIAL — 临时顺序节点
|
||||
C. PERSISTENT_SEQUENTIAL — 持久顺序节点
|
||||
D. EPHEMERAL — 临时节点但无序号
|
||||
|
||||
### Q2(基础)→
|
||||
|
||||
以下关于 ZK Watch 机制的说法中,哪一个是正确的?
|
||||
|
||||
A. Watch 被触发后会保持注册,持续监听后续变化
|
||||
B. Watch 是一次性的(One-shot),触发后需要重新注册
|
||||
C. Watch 只在 `getData` 调用时才会注册
|
||||
D. Watch 事件通过数据读取通道推送,确保顺序性
|
||||
|
||||
### Q3(进阶)— 核心原理
|
||||
|
||||
ZK 分布式锁天然实现了公平性,其根本原因是什么?
|
||||
|
||||
A. Leader 节点按 FIFO 队列分配锁
|
||||
B. 使用 SEQUENTIAL 序号决定锁的持有权,按序号大小排序
|
||||
C. Watch 事件按时间戳排序后分发给等待者
|
||||
D. ZAB 协议保证了写操作的原子性和有序性
|
||||
|
||||
### Q4(进阶)— 比较/辨析
|
||||
|
||||
Redis 锁 vs ZK 锁在一致性模型上的主要区别是:
|
||||
|
||||
A. Redis 和 ZK 都是 AP 系统
|
||||
B. Redis 是 CP,ZK 是 AP
|
||||
C. Redis 单实例是 AP(Redlock 不确定),ZK 是 CP(ZAB 强一致)
|
||||
D. Redis 是 CP,ZK 是不确定的
|
||||
|
||||
### Q5(深入)— 场景推理
|
||||
|
||||
N = 3 的 ZK 集群遭遇网络分区,左右各有一个节点。以下描述正确的是:
|
||||
|
||||
A. 两边都能正常工作,各自选出新 Leader
|
||||
B. 左边可以继续写,右边拒绝服务
|
||||
C. 都无法达成 Quorum,全部停止工作
|
||||
D. 自动合并为 N=2 集群继续服务
|
||||
|
||||
### Q6(深入)— 源码级/边界场景
|
||||
|
||||
ZK 会话超时默认 40 秒太长了。根据原文建议,会话超时应该设为多少?同时心跳间隔与会话超时的关系是什么?
|
||||
|
||||
A. 建议 1~5 秒;heartbeat = timeout / 5
|
||||
B. 建议 3~10 秒;heartbeat = timeout / 3
|
||||
C. 建议 5~15 秒;heartbeat = timeout / 2
|
||||
D. 建议 10~30 秒;heartbeat = timeout / 4
|
||||
|
||||
---
|
||||
|
||||
## 二、填空题(3道)
|
||||
|
||||
### F1 — 填空1
|
||||
|
||||
ZK 分布式锁使用的事件类型是 _____,当子节点列表发生变化时,所有相关监听者都会收到通知。
|
||||
|
||||
> **提示**: 这个事件的英文命名直接表达了"节点子列表变化"的含义。
|
||||
|
||||
### F2 — 填空2
|
||||
|
||||
ZAB 协议的第一个核心阶段是 _____,通过比较 ZXID(事务 ID)选出拥有最大 ZXID 的节点作为 Leader。第二个阶段是 Atomic Broadcast。
|
||||
|
||||
> **提示**: 回想 ZAB 的两个核心阶段名称。
|
||||
|
||||
### F3 — 填空3
|
||||
|
||||
Curator 是 ZK 最流行的 Java 客户端,加锁成功后需要在 finally 块中调用 _____() 方法。该方法会递归释放所有重入计数,而不仅仅是一次 unlock。
|
||||
|
||||
> **提示**: Curator 的方法名不是简单的 unlock,而是表达"释放锁"的动作。
|
||||
|
||||
---
|
||||
|
||||
## 三、简答题(1道)
|
||||
|
||||
### S1
|
||||
|
||||
某团队正在选型分布式锁方案,有人主张:"ZK 锁比 Redis 锁高级,我们应该用 ZK。"请结合原文内容,从以下几个维度给出你的分析并做出选型建议:
|
||||
- 一致性要求
|
||||
- 性能需求
|
||||
- 运维复杂度
|
||||
- 业务场景匹配度
|
||||
|
||||
> **答题框架提示**:
|
||||
> 1. 先反驳"更高级就更好"的思维误区
|
||||
> 2. 从三个维度客观对比
|
||||
> 3. 给出具体场景下的推荐方案
|
||||
|
||||
---
|
||||
|
||||
## 参考答案与解析
|
||||
|
||||
### 选择题答案
|
||||
|
||||
| 题号 | 正确答案 | 解析 |
|
||||
|------|---------|------|
|
||||
| Q1 | B | EPHEMERAL_SEQUENTIAL = 临时 + 顺序。EPHEMERAL 语义保证 session 断开时自动清理,SEQUENTIAL 提供全局有序性。D 选项 EPHEMERAL 虽然也会自动清理,但没有序号就无法实现公平锁。 |
|
||||
| Q2 | B | Watch 是一次性的,触发后自动注销。客户端必须在接收到事件后立刻重新注册,否则可能永远不再收到下一次通知。 |
|
||||
| Q3 | B | 因为节点是 SEQUENTIAL 的,按序号大小决定锁的持有权——最小序号获得锁,其他节点按从小到大依次监听前一个节点。这是天然 FIFO 公平性。 |
|
||||
| Q4 | C | Redis 单实例是 AP(追求高可用),Redlock 的一致性甚至不确定;ZK 基于 ZAB 协议是 CP(追求强一致性),脑裂时宁可停服也不会出现两把锁同时存在。 |
|
||||
| Q5 | C | N=3 时 Quorum = 2。每个分区只有 1 个节点,无法达到半数(≥2),所以两个分区都无法工作。这是 CP 系统的必然结果——安全性优先于可用性。 |
|
||||
| Q6 | B | 建议设为 3~10 秒以更快感知宕机。但 heartbeat interval = timeout / 3,太短的 timeout 会导致心跳过于频繁增加网络开销。 |
|
||||
|
||||
### 填空题答案
|
||||
|
||||
| 题号 | 答案 | 解析 |
|
||||
|------|------|------|
|
||||
| F1 | `NodeChildrenChanged` | ZK 锁场景下关注的是子节点列表的变化。当上一个持有者删除节点后,下一个等待者的 watch 会收到 NODE_DELETED 事件进而 getChildren 重新竞争。 |
|
||||
| F2 | `Leader Election` | ZAB 的两阶段:① Leader Election 选举 Leader;② Atomic Broadcast Leader 广播 Proposal,Follower 回复 ACK 达到半数后提交。 |
|
||||
| F3 | `release` | Curator 的 InterProcessMutex.acquire() 支持重入,release() 会递归递减重入计数直到归零才真正发送删除请求。直接用 delete 会导致重入计数未释放。 |
|
||||
|
||||
### 简答题参考答案
|
||||
|
||||
S1:**参考答案要点**:
|
||||
1. **"更高级"思维误区**:ZK 不是为"高级"设计的,而是为"强一致性协调"设计的。不要因为技术栈偏好而引入不必要的复杂度。
|
||||
2. **一致性维度**:如果业务只需要简单互斥(如防止用户重复点击),Redis SET NX 足够好,CP 过度设计。如果需要 Leader 选举等强一致性保证,ZK 更适合。
|
||||
3. **性能维度**:Redis 百万级 QPS(内存操作),ZK 需写事务日志同步复制,QPS 低一个数量级。高频短锁场景 Redis 明显更优。
|
||||
4. **运维维度**:Redis 集群工具链成熟,ZK 需要维护奇数节点集群,运维成本高。
|
||||
5. **结论**:简单互斥 → Redis + SET NX EX;强一致性少量并发控制(Leader 选举、Job 唯一绑定)→ ZK;不要为了"更高级"而上 ZK。
|
||||
|
||||
**评分标准**:答出任意 2 个要点即可得满分;完全正确需覆盖全部要点。
|
||||
|
||||
## 关联笔记
|
||||
- [[Redis 分布式锁]]
|
||||
- [[幂等设计方案]]
|
||||
@@ -0,0 +1,145 @@
|
||||
---
|
||||
tags: [test/review, architecture, arch/idempotency]
|
||||
create time: 2026-08-09 12:00
|
||||
---
|
||||
|
||||
# 幂等设计方案 — 测试题
|
||||
|
||||
## 概述
|
||||
本测试覆盖六种主流幂等方案(唯一索引防重、Token 预获取、乐观锁版本号校验、状态机校验、网关 Request ID 去重)以及三层防御最佳实践。共 10 道题目(6 选择 + 3 填空 + 1 简答)。
|
||||
|
||||
---
|
||||
|
||||
## 一、选择题(6道,由浅入深)
|
||||
|
||||
> **难度阶梯**: Q1-Q2 基础概念 → Q3-Q4 核心原理 → Q5-Q6 深入应用/边界场景
|
||||
|
||||
### Q1(基础)— 考察定义层面
|
||||
|
||||
以下哪个描述最准确地表达了"幂等"的概念?
|
||||
|
||||
A. 相同的请求只在系统中出现一次
|
||||
B. 多次执行同一操作与执行一次的效果相同
|
||||
C. 使用 Redis SETNX 来防止重复执行
|
||||
D. 数据库的 UNIQUE INDEX 约束
|
||||
|
||||
### Q2(基础)→
|
||||
|
||||
以下哪种幂等方案**不适合**短延迟批处理场景(如批量导入数据)?
|
||||
|
||||
A. 数据库唯一索引防重
|
||||
B. Token 预获取模式
|
||||
C. 乐观锁版本号校验
|
||||
D. 网关层 Request ID 去重
|
||||
|
||||
### Q3(进阶)— 核心原理
|
||||
|
||||
Lua 脚本 `if redis.call("GET", key) == expected then redis.call("SET", key, "USED") end` 在 Token 幂等模式中的核心作用是什么?
|
||||
|
||||
A. 设置 Token 的过期时间
|
||||
B. 原子性地检查 Token 是否有效并标记为已使用
|
||||
C. 生成一个新的 Token
|
||||
D. 删除已经过期的所有 Token
|
||||
|
||||
### Q4(进阶)— 比较/辨析
|
||||
|
||||
以下关于各幂等方案的对比中,哪一个是错误的?
|
||||
|
||||
A. 唯一索引防重的实现复杂度低但会额外占用存储
|
||||
B. Token 预获取可以防止 CSRF
|
||||
C. 状态机校验不需要额外的数据结构
|
||||
D. 网关 Request ID 方案可以保证跨重试窗口外的重复请求也被拦截
|
||||
|
||||
### Q5(深入)— 场景推理
|
||||
|
||||
某支付系统收到同一笔交易的重复扣款请求(同一个 biz_no)。以下哪种组合方案能提供最可靠的防护?
|
||||
|
||||
A. 仅用数据库唯一索引
|
||||
B. 仅用网关 Request ID
|
||||
C. 三层防御:网关 Request ID → Token 模式 → 数据库唯一索引兜底
|
||||
D. 仅用乐观锁版本号
|
||||
|
||||
### Q6(深入)— 源码级/边界场景
|
||||
|
||||
使用唯一索引做幂等时,以下哪种做法可以正确避免 ToCToU 竞态条件?
|
||||
|
||||
A. SELECT count(1) WHERE biz_no='xxx',如果为 0 则 INSERT
|
||||
B. 直接 INSERT,利用 Duplicate entry 错误判断已被处理过
|
||||
C. SELECT FOR UPDATE 后手动插入
|
||||
D. 先在内存 Map 中记录已处理的 biz_no
|
||||
|
||||
---
|
||||
|
||||
## 二、填空题(3道)
|
||||
|
||||
### F1 — 填空1
|
||||
|
||||
去重是_____,幂等是_____。去重是手段,幂等是目标——用唯一索引去重是为了达到幂等效果。
|
||||
|
||||
> **提示**: 原文中有明确的区分:"XX是强调语义...YY是强调数据..."
|
||||
|
||||
### F2 — 填空2
|
||||
|
||||
Token 预获取模式中,客户端先调用 _____ 接口获取一次性 Token,服务端存入 Redis 并设置过期时间(如 5 分钟),然后客户端携带 Token 发起实际业务请求。
|
||||
|
||||
> **提示**: 回想原文中描述的完整流程步骤。
|
||||
|
||||
### F3 — 填空3
|
||||
|
||||
Redis SETNX **不适合**单独做业务幂等的原因是:Redis 非持久化宕机可能丢数据;而且 SETNX 只能保证互斥,不能保证"之前是否已经_____过"。
|
||||
|
||||
> **提示**: 考虑 SETNX 的能力边界。
|
||||
|
||||
---
|
||||
|
||||
## 三、简答题(1道)
|
||||
|
||||
### S1
|
||||
|
||||
某电商下单接口面临用户重复点击导致重复创建订单的问题。请设计一个完整的幂等方案,要求:
|
||||
1. 从流量入口到数据存储的全链路设计
|
||||
2. 说明每层的作用和依赖关系
|
||||
3. 讨论失败降级策略
|
||||
|
||||
> **答题框架提示**:
|
||||
> 1. 第一道防线:什么位置做什么事
|
||||
> 2. 第二道防线:什么机制保证什么
|
||||
> 3. 第三道防线:最终兜底
|
||||
> 4. 异常场景分析
|
||||
|
||||
---
|
||||
|
||||
## 参考答案与解析
|
||||
|
||||
### 选择题答案
|
||||
|
||||
| 题号 | 正确答案 | 解析 |
|
||||
|------|---------|------|
|
||||
| Q1 | B | 幂等的定义是"多次执行与执行一次效果相同"。A 是去重的定义,C 和 D 是实现幂等的手段而非定义本身。 |
|
||||
| Q2 | B | Token 模式需要多一次 API 调用(先领 token 再提交),对于批量导入这种高频操作会严重拖慢用户体验。此时唯一索引方案更合适。 |
|
||||
| Q3 | B | Lua 脚本将 GET 和 SET 打包成原子操作——不存在 ToCToU 竞态。如果分开执行两个步骤,其他线程可能在中间时刻拿到同一个 Token。 |
|
||||
| Q4 | D | D 是错误的!网关 Request ID 只保护单次请求窗口内的重复(通常设定 TTL 如 300 秒),超出窗口的重复请求无法覆盖。这是其固有限制。 |
|
||||
| Q5 | C | 三层防御互为补充:网关层拦截大部分重复流量降低后端压力,Token 模式控制高风险操作的不可重入性,数据库唯一索引作为最后防线保证最终一致性。任何一层被突破都有下一层兜住。 |
|
||||
| Q6 | B | 直接 INSERT 利用 Duplicate entry 错误是最原子性的做法。A 是典型的 ToCToU——检查和插入之间有竞态窗口。C 的 SELECT FOR UPDATE 也可以但不如 D 简洁高效。D 的内存 Map 不解决分布式问题。 |
|
||||
|
||||
### 填空题答案
|
||||
|
||||
| 题号 | 答案 | 解析 |
|
||||
|------|------|------|
|
||||
| F1 | `数据`;`语义` | 去重强调数据层面——相同的请求只出现一次;幂等强调语义层面——多次执行结果相同。去重是手段,幂等是目标。 |
|
||||
| F2 | `/token/generate` | Token 预获取流程:① 客户端调用 /token/generate 获取一次性 Token;② 服务端存入 Redis,UNUSABLE 状态,5 分钟过期;③ 客户端携带 Token 发起业务请求;④ 服务端 Lua 原子消费 Token。 |
|
||||
| F3 | `成功执行` | SETNX 只能保证"此刻没人持有这个 key",但不能回答"之前是否已经成功处理过这个请求"。除非你在 Redis 里也存一份状态——那本质上就是把数据库该做的事搬到了 Redis。 |
|
||||
|
||||
### 简答题参考答案
|
||||
|
||||
S1:**参考答案要点**:
|
||||
1. **第一道防线(网关层)**:客户端在 HTTP 头中携带 X-Request-ID(UUID),网关层用 Redis SETNX reqid:id 1 EX 300s 做去重。重复请求直接从缓存返回之前的响应。
|
||||
2. **第二道防线(业务层)**:对支付等高风险接口采用 Token 模式——用户必须先领取一次性 Token 才能提交订单,Token 原子消费确保不可重入。
|
||||
3. **第三道防线(存储层)**:order_idempotent 表以 biz_no 为唯一索引。INSERT 时 Duplicate entry → 查询已有结果返回。
|
||||
4. **异常降级**:网关层 Redis 挂了时放行(靠后续两层兜底);Token 服务挂了时切换到 Request ID 模式(用户体验下降但不会中断);数据库层永远生效因为它是物理约束。
|
||||
|
||||
**评分标准**:答出任意 2 个要点即可得满分;完全正确需覆盖全部要点。
|
||||
|
||||
## 关联笔记
|
||||
- [[Redis 分布式锁]]
|
||||
- [[ZooKeeper 分布式锁]]
|
||||
@@ -0,0 +1,146 @@
|
||||
---
|
||||
tags: [test/review, architecture, arch/microservice, service-discovery]
|
||||
create time: 2026-08-09 12:00
|
||||
---
|
||||
|
||||
# 服务注册与发现 — 测试题
|
||||
|
||||
## 概述
|
||||
本测试覆盖服务注册/注销/续约流程、Eureka/AP 取向、Nacos CP/AP 可切换、Consul 特性以及 CAP 理论在注册中心中的体现。共 10 道题目(6 选择 + 3 填空 + 1 简答)。
|
||||
|
||||
---
|
||||
|
||||
## 一、选择题(6道,由浅入深)
|
||||
|
||||
> **难度阶梯**: Q1-Q2 基础概念 → Q3-Q4 核心原理 → Q5-Q6 深入应用/边界场景
|
||||
|
||||
### Q1(基础)— 考察定义层面
|
||||
|
||||
服务注册的标准三步走流程是:
|
||||
|
||||
A. 注册 → 查询 → 注销
|
||||
B. 注册 → 续约(心跳)→ 注销
|
||||
C. 发现 → 健康检查 → 更新
|
||||
D. 心跳 → 续约 → 下线
|
||||
|
||||
### Q2(基础)→
|
||||
|
||||
Eureka 的自我保护机制在什么条件下会被触发?
|
||||
|
||||
A. 15 分钟内超过 50% 实例未发送心跳
|
||||
B. 15 分钟内超过 85% 实例未发送心跳
|
||||
C. 所有实例同时宕机
|
||||
D. 注册中心 CPU 使用率超过 90%
|
||||
|
||||
### Q3(进阶)— 核心原理
|
||||
|
||||
Nacos 在同一套代码中为什么能既支持 AP 又支持 CP 模式?
|
||||
|
||||
A. 通过动态切换一致性协议实现,运行时修改配置即可
|
||||
B. 两种模式使用不同的数据存储路径和服务类型
|
||||
C. Nacos 有两个独立的二进制进程分别处理 AP 和 CP
|
||||
D. 基于 K8s Namespace 做逻辑隔离
|
||||
|
||||
### Q4(进阶)— 比较/辨析
|
||||
|
||||
Consul 的三个独特优势不包括以下哪一项?
|
||||
|
||||
A. Serf Gossip 协议的成员管理
|
||||
B. 丰富的健康检查方式(TCP/HTTP/script/Docker/TTL)
|
||||
C. 内嵌 Raft 一致性的 KV 存储
|
||||
D. 原生支持多级缓存(本地 + 远程)
|
||||
|
||||
### Q5(深入)— 场景推理
|
||||
|
||||
某金融系统对数据一致性要求极高,在选型服务注册中心时最应该考虑哪个因素?
|
||||
|
||||
A. 需要高可用容忍短暂不一致 → 选 Eureka
|
||||
B. 需要强一致性 → 选 Nacos CP 模式或 Consul TCP 接口
|
||||
C. DNS 接口最快 → 选 Consul DNS
|
||||
D. 客户端缓存最可靠 → 选 Eureka 并关闭自我保护
|
||||
|
||||
### Q6(深入)— 源码级/边界场景
|
||||
|
||||
PACELC 理论对 CAP 做了哪些补充?
|
||||
|
||||
A. PACELC 指出无分区时也需权衡延迟与一致性
|
||||
B. PACELC 指出分区恢复后需先进行数据同步再服务
|
||||
C. PACELC 引入了第三维度——性能,CAP 只有两个维度
|
||||
D. PACELC 证明 CAP 定理在分布式系统中是错误的
|
||||
|
||||
---
|
||||
|
||||
## 二、填空题(3道)
|
||||
|
||||
### F1 — 填空1
|
||||
|
||||
Eureka 的消费者会**缓存**服务列表,默认每 _____ 秒刷新一次。这样即使注册中心完全不可用,消费方依然能根据本地缓存继续通信。
|
||||
|
||||
> **提示**: 回顾原文 Eureka 小节中关于客户端缓存的描述。
|
||||
|
||||
### F2 — 填空2
|
||||
|
||||
Consul 的 DNS 接口因为是 _____ 模式的——直接指向随机节点读取本地内存缓存,不经过 Raft 日志——所以速度极快但可能读到旧数据。
|
||||
|
||||
> **提示**: 回想 CAP/PACELC 表格中 Consul DNS 的行。
|
||||
|
||||
### F3 — 填空3
|
||||
|
||||
Nacos 通过 _____ 实现环境隔离(dev/test/prod),每个命名空间有独立的服务和配置视图。底层基于 PostgreSQL 存储元数据。
|
||||
|
||||
> **提示**: Nacos 的环境隔离术语来自 Kubernetes。
|
||||
|
||||
---
|
||||
|
||||
## 三、简答题(1道)
|
||||
|
||||
### S1
|
||||
|
||||
面试官问你:"Eureka 的自我保护机制在生产环境中什么时候该关?"请给出你的完整回答,包括:
|
||||
- 自我保护机制的作用是什么
|
||||
- 关了会有什么风险
|
||||
- 什么场景下可以关
|
||||
- 如果不关应该如何监控
|
||||
|
||||
> **答题框架提示**:
|
||||
> 1. 解释机制的原理和风险
|
||||
> 2. 分场景讨论
|
||||
> 3. 给出具体的监控建议
|
||||
|
||||
---
|
||||
|
||||
## 参考答案与解析
|
||||
|
||||
### 选择题答案
|
||||
|
||||
| 题号 | 正确答案 | 解析 |
|
||||
|------|---------|------|
|
||||
| Q1 | B | 标准三步:① 注册服务(POST /register);② 心跳续约(维持 lastDirtyTimestamp);③ 主动注销(DELETE /deregister)。故障场景下感知延迟通常为续约超时周期(30~90 秒)。 |
|
||||
| Q2 | B | 15 分钟内超过 85% 实例未发心跳触发保护窗口锁定,不再剔除任何实例。宁可多调几次失败也不主动剔。 |
|
||||
| Q3 | B | 不同模式使用不同的存储路径和服务类型:CP 用 `nacos.v1.auth.user`(Raft),AP 用 `nacos.v1.instance`(Distro)。不是运行时切换而是物理隔离的路径。 |
|
||||
| Q4 | D | Consul 的优势是 Serf Gossip、丰富健康检查、KV 存储。多级缓存不是 Consul 的功能,那是 Redis/Caffeine 层的职责。 |
|
||||
| Q5 | B | 金融系统需要强一致性,应选 Nacos CP 模式(Raft)或 Consul TCP 接口(Raft Leader)。Eureka 最终一致不适合。DNS 可能 stale。关闭自我保护会增加雪崩风险。 |
|
||||
| Q6 | A | PACELC = PA(Partition tolerant, choose Availability)+ EC(Else, choose Between Consistency and Latency)。CAP 只讨论分区场景,PACELC 补充了无分区时的 L/C 权衡。 |
|
||||
|
||||
### 填空题答案
|
||||
|
||||
| 题号 | 答案 | 解析 |
|
||||
|------|------|------|
|
||||
| F1 | `30` | 消费者每 30 秒刷新一次本地缓存。结合 Eureka 的续约超时周期(通常 30~90 秒),故障检测延迟约在一个续约周期左右。 |
|
||||
| F2 | `AP` | Consul DNS API 直接读取随机节点的本地内存缓存,不走 Raft 日志,因此速度快但读到的可能是 stale data。如果需要强一致则走 TCP 端口。 |
|
||||
| F3 | `namespace` | Nacos 的 namespace 相当于 K8s 的 Namespace,实现 dev/test/prod 的物理隔离。每个 namespace 独立存储服务列表和配置数据。 |
|
||||
|
||||
### 简答题参考答案
|
||||
|
||||
S1:**参考答案要点**:
|
||||
1. **机制作用**:自我保护防止因网络抖动大量实例被误判为宕机而批量剔除,避免雪崩式下线。
|
||||
2. **关了的风险**:一旦关闭,心跳稍微延迟就会被剔除,一批实例被认为故障全部移除,调用方拿不到任何健康实例。
|
||||
3. **适用场景**:金融类对一致性要求高的场景可以考虑关闭;普通互联网业务不建议关,误判的危害大于不调用的危害。
|
||||
4. **监控建议**:生产环境必须监控 `eureka.server.selfPreservationThreshold` 指标,当该阈值被触发时告警通知运维介入检查。
|
||||
|
||||
**评分标准**:答出任意 2 个要点即可得满分;完全正确需覆盖全部要点。
|
||||
|
||||
## 关联笔记
|
||||
- [[负载均衡算法]]
|
||||
- [[限流熔断降级]]
|
||||
- [[配置中心设计]]
|
||||
@@ -0,0 +1,142 @@
|
||||
---
|
||||
tags: [test/review, architecture, arch/microservice, load-balancing]
|
||||
create time: 2026-08-09 12:00
|
||||
---
|
||||
|
||||
# 负载均衡算法 — 测试题
|
||||
|
||||
## 概述
|
||||
本测试覆盖客户端 vs 服务端负载均衡架构、轮询/随机/一致性哈希/最少连接等核心算法、Spring Cloud LoadBalancer 实现原理以及加权轮询的工程实现。共 10 道题目(6 选择 + 3 填空 + 1 简答)。
|
||||
|
||||
---
|
||||
|
||||
## 一、选择题(6道,由浅入深)
|
||||
|
||||
> **难度阶梯**: Q1-Q2 基础概念 → Q3-Q4 核心原理 → Q5-Q6 深入应用/边界场景
|
||||
|
||||
### Q1(基础)— 考察定义层面
|
||||
|
||||
以下关于客户端负载均衡与服务端负载均衡的说法中,正确的是:
|
||||
|
||||
A. 客户端负载均衡延迟更高,因为多了一次网络跳
|
||||
B. 服务端负载均衡的故障隔离更好——LB 单点故障不影响其他调用
|
||||
C. 客户端负载均衡需要为每种语言实现 SDK,服务端 LB 协议无关
|
||||
D. Nginx 是客户端负载均衡的典型代表
|
||||
|
||||
### Q2(基础)→
|
||||
|
||||
以下哪种负载均衡算法最适合 WebSocket 长连接场景?
|
||||
|
||||
A. 简单轮询(Round Robin)
|
||||
B. 随机法
|
||||
C. 最少连接数(Least Connections)
|
||||
D. 一致性哈希
|
||||
|
||||
### Q3(进阶)— 核心原理
|
||||
|
||||
传统哈希 `hash(instance) % n` 在实例增减时为什么会导致大量请求重新路由?
|
||||
|
||||
A. 因为 hash 函数的不均匀性
|
||||
B. 因为 n 变化后所有 hash 结果对新的 n 取模都变了
|
||||
C. 因为虚拟节点数量不足
|
||||
D. 因为 MurmurHash 本身不适合负载均衡
|
||||
|
||||
### Q4(进阶)— 比较/辨析
|
||||
|
||||
一致性哈希中虚拟节点的主要作用是什么?
|
||||
|
||||
A. 提高 hash 值的随机性
|
||||
B. 解决数据倾斜问题,使物理实例在环上分布更均匀
|
||||
C. 增加系统容错能力,某节点挂了可以立即接替
|
||||
D. 减少 hash 冲突的概率
|
||||
|
||||
### Q5(深入)— 场景推理
|
||||
|
||||
一个微服务有 5 个实例,权重分别为 A=5、B=3、C=2、D=1、E=1(总权重 12)。使用平滑加权轮询算法,第一个被选中的实例一定是:
|
||||
|
||||
A. B(权重 3)
|
||||
B. C(权重 2)
|
||||
C. A(权重 5)
|
||||
D. 无法确定,取决于初始 current_weight
|
||||
|
||||
### Q6(深入)— 源码级/边界场景
|
||||
|
||||
关于 Spring Cloud LoadBalancer,以下说法哪个是正确的?
|
||||
|
||||
A. 它内置了重试和熔断能力
|
||||
B. 基于 Reactor 响应式编程,返回 Flux<ServiceInstance>
|
||||
C. 默认使用一致性哈希算法
|
||||
D. 只支持 HTTP 协议的实例选择
|
||||
|
||||
---
|
||||
|
||||
## 二、填空题(3道)
|
||||
|
||||
### F1 — 填空1
|
||||
|
||||
一致性哈希将 hash 值空间映射到环上(0 ~ _____),服务和请求都 hash 到环上的同一点,请求顺时针找到最近的实例。移除一个实例只影响其相邻的两个虚拟节点范围,而非全部数据。
|
||||
|
||||
> **提示**: 回忆原文中提到的一致性哈希环的范围上限。
|
||||
|
||||
### F2 — 填空2
|
||||
|
||||
一致性哈希中,每个物理实例对应的虚拟节点数 k 通常取 _____~_____。如果实例频繁增删(每几分钟变一次),k 要调得更大(500+)才能维持稳定性,但会增加内存开销。
|
||||
|
||||
> **提示**: 原文中有一个具体的数值范围。
|
||||
|
||||
### F3 — 填空3
|
||||
|
||||
Nginx 的 smooth weighted round robin 算法中,每次选出 current_weight 最大的实例后,执行 `selected.CurWeight -= _____`,保证长期均衡。
|
||||
|
||||
> **提示**: 减去的应该是总权重,这样每个实例的 CurWeight 会在一个周期内回到初始状态。
|
||||
|
||||
---
|
||||
|
||||
## 三、简答题(1道)
|
||||
|
||||
### S1
|
||||
|
||||
某电商大促场景中,你有 10 个商品推荐服务实例,其中 3 台是高性能机器(容量大),7 台是普通机器(容量小)。流量高峰时要求充分利用高性能机器的资源。请说明你会选用哪种负载均衡算法,如何配置参数,并解释为什么不用简单轮询或随机法。
|
||||
|
||||
> **答题框架提示**:
|
||||
> 1. 分析场景的核心矛盾(规格差异)
|
||||
> 2. 推荐算法并说明理由
|
||||
> 3. 给出具体配置思路
|
||||
> 4. 讨论边界情况
|
||||
|
||||
---
|
||||
|
||||
## 参考答案与解析
|
||||
|
||||
### 选择题答案
|
||||
|
||||
| 题号 | 正确答案 | 解析 |
|
||||
|------|---------|------|
|
||||
| Q1 | C | A 错:客户端 LB 少一次网络跳所以延迟低;B 错:LB 单点故障影响全部请求,故障隔离差;D 错:Nginx 是服务端 LB。只有 C 正确——客户端 LB 需要各语言的 SDK(如 Spring Cloud LoadBalancer for Java)。 |
|
||||
| Q2 | C | WebSocket 长连接场景下不同连接的持续时间差异大,最少连接数能保证新请求发给当前活跃连接最少的实例,避免某个实例堆积大量长连接而其他实例闲置。 |
|
||||
| Q3 | B | 当实例数从 n 变为 n±1 时,几乎所有 key 的 hash(key) % new_n 都会不同于原来的结果,导致约 (n-1)/n 的数据需要迁移。这就是一致性哈希要解决的问题。 |
|
||||
| Q4 | B | 如果不使用虚拟节点,少数几个物理实例在环上的分布会很不均匀,造成严重的数据倾斜。K=100~200 个虚拟节点让分布趋近均匀。 |
|
||||
| Q5 | C | 平滑加权轮询第一轮时所有实例的 CurWeight = 0,加上各自 Weight 后 A 的 CurWeight = 5 最大,所以第一轮一定选 A。之后 A 减 12 变为 -7,其他依次加完再选。 |
|
||||
| Q6 | B | Spring Cloud LoadBalancer 基于 Reactor,返回 Flux<ServiceInstance>。它**不提供**重试和熔断(那是 Resilience4j 的职责),默认策略是 RoundRobinLocator(轮询),不是哈希。 |
|
||||
|
||||
### 填空题答案
|
||||
|
||||
| 题号 | 答案 | 解析 |
|
||||
|------|------|------|
|
||||
| F1 | `2^32`(或"4294967296") | 一致性哈希将 32 位 hash 空间映射为环形地址空间,首尾相接。顺时针寻找最近实例的逻辑确保了实例增减时的最小影响范围。 |
|
||||
| F2 | `100`;`200` | 这是业界经验值。太少了分布不均,太多了内存占用大且维护代价高。频繁增删场景需要 500+ 的虚拟节点数。 |
|
||||
| F3 | `totalW`(或"总权重 total_weight") | Nginx 的平滑加权轮询核心公式:cur += weight(累积),选 max 后 selected -= total。这样经过一个完整周期,每个实例被选中的次数恰好等于它的权重占比。 |
|
||||
|
||||
### 简答题参考答案
|
||||
|
||||
S1:**参考答案要点**:
|
||||
1. **核心矛盾**:性能差异大的实例不能用简单轮询——每台都会被分到相同数量的请求,导致弱机过载而强机资源浪费。
|
||||
2. **推荐算法**:加权轮询(Weighted Round Robin)。将 3 台高性能机的权重设为普通机的若干倍(如 3:1 或根据实测 QPS 计算比例),使得长期来看请求按容量比例分配。
|
||||
3. **不选简单的理由**:简单轮询无视容量差异;随机法在小样本下可能极端不均(连续命中同一个弱机),大促场景不能接受这种不确定性。
|
||||
4. **边界处理**:需要考虑实例下线时的权重动态调整(Hot Restart 机制)、健康检查剔除故障实例后的自动重新加权。
|
||||
|
||||
**评分标准**:答出任意 2 个要点即可得满分;完全正确需覆盖全部要点。
|
||||
|
||||
## 关联笔记
|
||||
- [[服务注册与发现]]
|
||||
- [[限流熔断降级]]
|
||||
@@ -0,0 +1,144 @@
|
||||
---
|
||||
tags: [test/review, architecture, arch/microservice, config-center]
|
||||
create time: 2026-08-09 12:00
|
||||
---
|
||||
|
||||
# 配置中心设计 — 测试题
|
||||
|
||||
## 概述
|
||||
本测试覆盖 Apollo 三级 Namespace 模型、Nacos Config 长轮询推送机制、配置热更新实现原理、版本管理和回滚策略。共 10 道题目(6 选择 + 3 填空 + 1 简答)。
|
||||
|
||||
---
|
||||
|
||||
## 一、选择题(6道,由浅入深)
|
||||
|
||||
> **难度阶梯**: Q1-Q2 基础概念 → Q3-Q4 核心原理 → Q5-Q6 深入应用/边界场景
|
||||
|
||||
### Q1(基础)— 考察定义层面
|
||||
|
||||
Apollo 的配置模型中,以下哪一层不是三级结构的一部分?
|
||||
|
||||
A. Environment(部署环境:dev/test/prod)
|
||||
B. Application(业务应用)
|
||||
C. Namespace(配置类型)
|
||||
D. Tenant(租户)
|
||||
|
||||
### Q2(基础)→
|
||||
|
||||
Nacos Config 的秒级推送底层使用什么机制?
|
||||
|
||||
A. WebSocket 长连接
|
||||
B. HTTP 长轮询(Long Polling)
|
||||
C. MQTT 发布/订阅
|
||||
D. Redis Pub/Sub
|
||||
|
||||
### Q3(进阶)— 核心原理
|
||||
|
||||
Nacos Config 长轮询的工作原理是什么?
|
||||
|
||||
A. 客户端每 5 秒定时发送请求,服务端每次都立即返回
|
||||
B. 客户端发起请求后服务端将其挂起在 waitList 中,配置变化时写入 notificationId 后返回;如果 30 秒无变更则超时返回
|
||||
C. 服务端主动通过 WebSocket 向客户端推送变更通知
|
||||
D. 客户端通过 Redis Pub/Sub 订阅配置变更事件
|
||||
|
||||
### Q4(进阶)— 比较/辨析
|
||||
|
||||
以下关于 Apollo 和 Nacos Config 的区别描述中,哪个是正确的?
|
||||
|
||||
A. Apollo 用长轮询,Nacos 用短轮询
|
||||
B. Apollo 的 namespace 支持更细粒度的权限控制和审批流,ConfigMap 只支持键值对映射
|
||||
C. Nacos 不支持灰度发布,只有全量发布
|
||||
D. Apollo 不支持多环境隔离,需要手动管理
|
||||
|
||||
### Q5(深入)— 场景推理
|
||||
|
||||
某团队在生产环境中修改了数据库连接串配置,服务重启后出现大量连接失败。从配置中心的角度看,最根本的原因是什么?
|
||||
|
||||
A. 使用了长轮询而非 WebSocket
|
||||
B. 配置变更没有经过审核+灰度流程,直接全量推送到生产环境
|
||||
C. Nacos 的 namespace 粒度不够细
|
||||
D. Apollo 的三级结构过于复杂
|
||||
|
||||
### Q6(深入)— 源码级/边界场景
|
||||
|
||||
Apollo 的回滚操作本质上是怎样的?
|
||||
|
||||
A. 将版本号倒退回上一个版本,删除中间版本的所有记录
|
||||
B. 用当前最新状态 + 差异计算生成一个新版本,保证发布记录单调递增
|
||||
C. 复制上一个版本的快照作为新版本,不产生新的变更记录
|
||||
D. 在数据库中将对应行的 version 字段减 1
|
||||
|
||||
---
|
||||
|
||||
## 二、填空题(3道)
|
||||
|
||||
### F1 — 填空1
|
||||
|
||||
Apollo 客户端发现配置新版本的默认拉取周期是 _____ 秒。当服务端生成了新的 releaseKey 后,客户端在下一次拉取时发现版本变化即触发热加载。
|
||||
|
||||
> **提示**: 回想原文中 Apollo 灰度发布流程的描述。
|
||||
|
||||
### F2 — 填空2
|
||||
|
||||
Spring Cloud 中使用 `@RefreshScope` 注解的 Bean 在配置变更后会自动生成_____,使得下次调用 getter 时获取的是最新的配置值。
|
||||
|
||||
> **提示**: 这个代理技术是 Spring 框架的核心特性之一,基于 AOP 实现。
|
||||
|
||||
### F3 — 填空3
|
||||
|
||||
配置热更新最大的风险是配置变更导致服务行为突变。Apollo 的解决方案是"_____+灰度",先让少量实例生效观察指标,再逐步全量。
|
||||
|
||||
> **提示**: 两个字的动词,表示一个审核的动作。
|
||||
|
||||
---
|
||||
|
||||
## 三、简答题(1道)
|
||||
|
||||
### S1
|
||||
|
||||
面试官问:"为什么 Nacos Config 用长轮询而不是 WebSocket?"请给出你的完整回答,并扩展到以下讨论:
|
||||
- 长轮询 vs WebSocket 各自的优缺点
|
||||
- 微服务架构中的中间件链路限制
|
||||
- 在什么场景下可能考虑 WebSocket
|
||||
|
||||
> **答题框架提示**:
|
||||
> 1. 长轮询的核心优势
|
||||
> 2. Websocket 的主要劣势(在微服务语境下)
|
||||
> 3. 混合方案的思考
|
||||
|
||||
---
|
||||
|
||||
## 参考答案与解析
|
||||
|
||||
### 选择题答案
|
||||
|
||||
| 题号 | 正确答案 | 解析 |
|
||||
|------|---------|------|
|
||||
| Q1 | D | Apollo 的三级结构是 Environment → Application → Cluster → Namespace。Tenant(租户)不是 Apollo 的标准层级——那是多租户 SaaS 平台的概念。 |
|
||||
| Q2 | B | Nacos Config 使用 HTTP 长轮询实现秒级推送。不是 WebSocket(因为要兼容所有 HTTP 中间件),也不是 MQTT 或 Redis Pub/Sub。 |
|
||||
| Q3 | B | 长轮询核心:客户端请求挂起到 waitList,配置变化时写 notificationId 后立即返回;30 秒超时防止连接无限期挂起。客户端收到通知后再拉取最新配置。 |
|
||||
| Q4 | B | A 错:两者都用长轮询;C 错:Nacos 也支持灰度发布和回滚;D 错:Apollo 天然支持多环境隔离(namespace 就是为这个设计的)。 |
|
||||
| Q5 | B | 直接全量推送错误配置是最危险的——所有实例同时失效。正确做法是先审核进入待发布状态,灰度到少数 IP 验证无误后再全量。 |
|
||||
| Q6 | B | 回滚的本质不是"倒退版本"而是用当前状态 + 差异生成新版本。这样保证了发布记录永远单调递增(版本号一直增长),不会产生环形依赖。 |
|
||||
|
||||
### 填空题答案
|
||||
|
||||
| 题号 | 答案 | 解析 |
|
||||
|------|------|------|
|
||||
| F1 | `5` | Apollo Client 每 5 秒轮询一次服务端检查是否有新 releaseKey。这是配置热更新的延迟上限(理想情况下一个拉取周期即可感知变更)。 |
|
||||
| F2 | `代理 Bean` | @RefreshScope 会动态创建代理对象,每次 getter 调用时检查缓存是否过期,若已过期则从配置中心拉取最新值。 |
|
||||
| F3 | `审核` | "审核+灰度"流程:编辑配置进入审核状态→审核通过发布→灰度规则指定特定 IP 先接收→验证后全量发布。 |
|
||||
|
||||
### 简答题参考答案
|
||||
|
||||
S1:**参考答案要点**:
|
||||
1. **长轮询的核心优势**:兼容所有 HTTP 中间件(网关、CDN、负载均衡器)。微服务架构中请求路径上通常有 Spring Cloud Gateway 等 HTTP-only 代理,这些代理不会做 WebSocket 升级,导致连接断开。
|
||||
2. **WebSocket 的劣势**:全链路需要升级支持,跨 NAT/防火墙困难;运维复杂度显著增加。
|
||||
3. **各自的缺点**:长轮询的连接保持期间占用服务端内存;WebSocket 需要维护连接池和心跳保活。
|
||||
4. **WS 的适用场景**:需要毫秒级推送且能控制全链路的内部系统(如实时协作编辑器、游戏服务器)。对外暴露的微服务配置同步场景不适合。
|
||||
|
||||
**评分标准**:答出任意 2 个要点即可得满分;完全正确需覆盖全部要点。
|
||||
|
||||
## 关联笔记
|
||||
- [[服务注册与发现]]
|
||||
- [[限流熔断降级]]
|
||||
@@ -0,0 +1,143 @@
|
||||
---
|
||||
tags: [test/review, architecture, arch/microservice, rate-limiting]
|
||||
create time: 2026-08-09 12:00
|
||||
---
|
||||
|
||||
# 限流熔断降级 — 测试题
|
||||
|
||||
## 概述
|
||||
本测试覆盖三种限流算法(滑动窗口、令牌桶、漏桶)、熔断三态转换机制、Sentinel 与 Resilience4j 核心特性以及降级兜底策略。共 10 道题目(6 选择 + 3 填空 + 1 简答)。
|
||||
|
||||
---
|
||||
|
||||
## 一、选择题(6道,由浅入深)
|
||||
|
||||
> **难度阶梯**: Q1-Q2 基础概念 → Q3-Q4 核心原理 → Q5-Q6 深入应用/边界场景
|
||||
|
||||
### Q1(基础)— 考察定义层面
|
||||
|
||||
以下哪种限流算法**不允许突发流量**,强制输出匀速?
|
||||
|
||||
A. 计数器限流
|
||||
B. 令牌桶算法
|
||||
C. 漏桶算法
|
||||
D. 以上都不对
|
||||
|
||||
### Q2(基础)→
|
||||
|
||||
熔断器从 Closed 状态转换到 Open 状态的典型触发条件是什么?
|
||||
|
||||
A. 单个请求失败
|
||||
B. 最近 N 秒内成功率为零
|
||||
C. 失败率超过阈值(如 >50%)
|
||||
D. HPA 检测到 CPU 使用率过高
|
||||
|
||||
### Q3(进阶)— 核心原理
|
||||
|
||||
熔断半开(HalfOpen)状态的主要作用是什么?
|
||||
|
||||
A. 降低服务器负载,减少请求量
|
||||
B. 用少量探测请求验证下游是否已恢复,避免直接进入 Closed 后流量暴增导致二次雪崩
|
||||
C. 为客户端提供缓冲时间,等待前端超时
|
||||
D. 在 Open 和 Closed 之间做数据同步
|
||||
|
||||
### Q4(进阶)— 比较/辨析
|
||||
|
||||
Resilience4j 与 Hystrix 的核心区别不包括以下哪项?
|
||||
|
||||
A. Resilience4j 基于信号量隔离(轻),Hystrix 基于线程池隔离(重)
|
||||
B. Resilience4j 采用函数式设计,Hystrix 需要继承 Thread
|
||||
C. Resilience4j 不依赖 Spring Cloud,可以独立使用
|
||||
D. Resilience4j 不支持熔断功能
|
||||
|
||||
### Q5(深入)— 场景推理
|
||||
|
||||
某 API 网关需要对用户接口做限流保护。业务要求允许合理的突发流量(如用户刷新页面同时发多个请求),但限制长期平均速率。以下选型最合理的是:
|
||||
|
||||
A. 漏桶——因为保护消费者不被打爆
|
||||
B. 令牌桶——因为允许突发消费存量令牌
|
||||
C. 固定窗口计数器——因为实现最简单
|
||||
D. 并发线程数限流——因为实现最轻量
|
||||
|
||||
### Q6(深入)— 源码级/边界场景
|
||||
|
||||
Sentinel 的滑动窗口与传统固定窗口的核心区别是什么?
|
||||
|
||||
A. Sentinel 使用 Redis 做分布式计数,传统方式用本地内存
|
||||
B. Sentinel 将窗口细分为更小的 tick,在每个 tick 内独立计数再聚合;固定窗口在边界处会漏算(上一个窗口的尾巴 + 下一个窗口的头同时超限)
|
||||
C. Sentinel 支持更多算法(令牌桶+漏桶+计数器),传统方式只支持计数器
|
||||
D. Sentinel 不需要配置阈值,通过自适应学习自动设定
|
||||
|
||||
---
|
||||
|
||||
## 二、填空题(3道)
|
||||
|
||||
### F1 — 填空1
|
||||
|
||||
HPA(Horizontal Pod Autoscaler)的计算公式是:`targetReplicas = ceil(currentReplicas * (currentMetricValue / _____))`。例如当前 5 个副本,CPU 目标 70%,当前平均 CPU 使用率 91%,则扩容到 `ceil(5 * 91 / 70) = _____` 个副本。
|
||||
|
||||
> **提示**: 第一个空填公式中的占位符名,第二个空填计算结果。
|
||||
|
||||
### F2 — 填空2
|
||||
|
||||
Prometheus 采集指标使用 Pull 模式而非 Push 模式的原因是:Pull 模式下失联的服务会自动被忽略(不再收到数据即标记为 down),而 Push 模式下挂了的服务停止发送数据但采集器不知道它们已经不存在了。请写出 Prometheus 中四种核心指标类型中的两种:_____、_____。
|
||||
|
||||
> **提示**: 任选两种即可——Counter/Gauge/Histogram/Summary。
|
||||
|
||||
### F3 — 填空3
|
||||
|
||||
Token Bucket 和 Leaky Bucket 的选型建议:**对外暴露 API 时用_____**(允许客户端合理突发);**内部服务间调用时用_____**(保护消费方不被打爆)。
|
||||
|
||||
> **提示**: 每个空填一种算法名称。
|
||||
|
||||
---
|
||||
|
||||
## 三、简答题(1道)
|
||||
|
||||
### S1
|
||||
|
||||
面试官问:"为什么熔断要从 Open 到 HalfOpen 而不是直接从 Open 回到 Closed?"请详细解释原因,并结合以下场景说明后果:某个下游微服务的数据库连接池被打满,故障正在恢复中。
|
||||
|
||||
> **答题框架提示**:
|
||||
> 1. HalfOpen 的安全阀作用
|
||||
> 2. 直接回 Closed 的后果分析
|
||||
> 3. HalfOpen 探测的具体流程
|
||||
> 4. 补充:如果 HalfOpen 也失败的策略
|
||||
|
||||
---
|
||||
|
||||
## 参考答案与解析
|
||||
|
||||
### 选择题答案
|
||||
|
||||
| 题号 | 正确答案 | 解析 |
|
||||
|------|---------|------|
|
||||
| Q1 | C | 漏桶的核心就是无论输入多猛,输出永远匀速——请求进入桶中排队处理,满了就溢出拒绝。令牌桶允许突发(桶满时可以一次性消耗多个令牌),计数器有窗口边界问题。 |
|
||||
| Q2 | C | 典型触发条件是失败率超过阈值(如最近 N 秒内失败率 > 50%)。单个请求失败不够成熔断,全为零太极端。 |
|
||||
| Q3 | B | HalfOpen 是一个安全阀:下游还在恢复时,直接进入 Closed 会让全部流量瞬间灌入,可能导致二次雪崩。有限的探测请求(通常 1~3 个)先验证下游是否真恢复了。 |
|
||||
| Q4 | D | D 是错误的——Resilience4j **完全支持**熔断功能。它的优势在于函数式 API 和装饰器模式。其他三项都是正确的区别点。 |
|
||||
| Q5 | B | 令牌桶适合保护生产者:允许突发流量(桶满时可突发消费存量令牌),但长期受限于平均速率。这正是对外 API 网关的需求特征。 |
|
||||
| Q6 | B | 固定窗口的问题在于边界效应:上一个窗口末尾 0.9 秒的请求和下一个窗口开头 0.9 秒的请求各占各自窗口但不超出阈值,合在一起可能翻倍。Sentinel 将窗口细分为更小的 tick 来消除这个问题。 |
|
||||
|
||||
### 填空题答案
|
||||
|
||||
| 题号 | 答案 | 解析 |
|
||||
|------|------|------|
|
||||
| F1 | `desiredMetricValue`;`7` | ceil(5 × 91 / 70) = ceil(6.5) = 7。HPA 不是实时的——依赖 metrics-server 每 15~60 秒采样,扩容有分钟级延迟。 |
|
||||
| F2 | `Counter`、`Gauge`(或 Histogram、Summary 任意两种) | Counter = 单调递增(如总请求数);Gauge = 可上可下(如活跃连接数);Histogram = 分桶统计(延迟分布);Summary = 客户端计算分位数(P50/P99)。 |
|
||||
| F3 | `令牌桶`;`漏桶` | 对外 API 允许合理突发 → 令牌桶;内部服务间保护消费方 → 漏桶匀速处理。 |
|
||||
|
||||
### 简答题参考答案
|
||||
|
||||
S1:**参考答案要点**:
|
||||
1. **安全阀作用**:HalfOpen 是 Open 和 Closed 之间的缓冲层,用有限的探针先测试下游恢复情况。
|
||||
2. **直接回 Closed 的后果**:假设下游 DB 连接池被打满(比如只剩 5 个可用),如果直接从 Open 回 Closed,大量积压请求瞬间涌入会立即再次打满连接池,引发二次雪崩。
|
||||
3. **HalfOpen 流程**:仅发出 1~3 个探测请求给下游,只有全成功才转入 Closed。任一失败立刻重新进入 Open 并重置倒计时。探测期间原有业务请求仍被拦截。
|
||||
4. **补充**:如果连续多次 HalfOpen 都失败,可以引入渐进式加大探测流量的策略,或者人工介入告警。
|
||||
|
||||
**评分标准**:答出任意 2 个要点即可得满分;完全正确需覆盖全部要点。
|
||||
|
||||
## 关联笔记
|
||||
- [[服务注册与发现]]
|
||||
- [[负载均衡算法]]
|
||||
- [[配置中心设计]]
|
||||
Reference in New Issue
Block a user