Go 后端面试复盘:语言、并发与服务实践 · 25. 稳定性与分布式系统至26. 安全编码
前置:Go 系统学习,并完成一个HTTP 工程练习。本页用于面试前查漏补缺,不按章节替代语言课程。
25. 稳定性与分布式系统
25.1 超时、重试、限流、熔断、降级分别解决什么?
| 机制 | 目标 |
|---|---|
| 超时 | 限制单次等待时间,释放资源 |
| 重试 | 恢复少量瞬时失败 |
| 限流 | 限制进入系统的负载 |
| 熔断 | 下游持续失败时快速失败,避免继续施压 |
| 降级 | 舍弃非核心能力,保护核心链路 |
它们需要组合,单独使用可能产生副作用。例如超时后立即无上限重试会放大故障。
25.2 常见限流算法有哪些?
固定窗口:
- 实现简单。
- 窗口边界可能瞬间通过两倍流量。
滑动窗口:
- 更平滑。
- 维护成本更高。
漏桶:
- 按固定速率流出。
- 适合整形,突发会排队或丢弃。
令牌桶:
- 按速率生成 token。
- 允许一定突发。
- 工程中非常常见。
25.3 熔断器的状态是什么?
查看图表代码
- Closed:正常放行并统计。
- Open:快速失败。
- Half-Open:允许少量探测请求。
阈值应考虑最小请求量、时间窗口、错误类型和慢调用,避免低流量下偶发失败导致误熔断。
25.4 什么是重试风暴?
下游变慢或失败后,上游多个层级同时重试,流量成倍增加,使下游更难恢复。
治理:
- 只在一层统一重试。
- 总超时预算。
- 指数退避和 jitter。
- 重试配额。
- 熔断和限流。
- 仅重试幂等瞬时错误。
25.5 什么是幂等?
同一个操作执行一次和执行多次,对系统最终结果的影响相同。
实现方式:
- 请求幂等键。
- 唯一索引。
- 状态机条件更新。
- 去重表。
- 消费消息记录。
- 操作本身天然幂等,例如设置值而非累加。
“接口使用 POST”不代表一定不幂等;幂等是业务语义。
25.6 分布式事务有哪些方案?
- 强一致事务协议:2PC 等,协调成本和可用性取舍较大。
- Saga:长事务拆成多个本地事务和补偿操作。
- TCC:Try、Confirm、Cancel,业务侵入较高。
- 事务消息/Outbox:本地事务写业务数据和事件,再可靠投递。
- 最终一致性 + 对账修复。
选择取决于一致性要求、业务补偿能力、吞吐和故障模型。
25.7 Outbox Pattern 是什么?
在同一个数据库本地事务中:
- 更新业务数据。
- 插入 outbox 事件。
后台发布器读取未投递事件发送到消息系统,成功后标记。消费者仍要幂等,因为投递通常是至少一次。
它解决“数据库提交成功但消息发送失败”之间的原子性缺口。
25.8 消息队列的 At-most-once、At-least-once、Exactly-once 是什么?
- At-most-once:最多一次,可能丢,不重复。
- At-least-once:至少一次,不轻易丢,但可能重复。
- Exactly-once:在明确边界和条件下恰好一次,成本高,端到端业务通常仍需幂等。
实际系统更常采用至少一次投递 + 幂等消费。
25.9 如何设计消息消费者?
- 手动确认时机正确。
- 幂等。
- 有限重试。
- 指数退避。
- 死信队列。
- 监控积压和消费延迟。
- 单条消息大小限制。
- poison message 隔离。
- 处理顺序要求和分区键。
- 优雅关闭时停止拉取并等待在途消息。
25.10 CAP 怎么理解?
在发生网络分区时,分布式系统必须在一致性和可用性之间做选择:
- C:所有节点看到一致数据。
- A:每个请求都能得到非错误响应。
- P:网络分区存在时系统仍能运行。
现实网络中 P 无法忽略,因此系统针对不同操作选择偏 C 或偏 A。CAP 不是平时任何情况下“只能三选二”的简单口号。
25.11 如何防止雪崩?
- 入口限流。
- 有界队列和并发。
- 超时预算。
- 熔断降级。
- 缓存和请求合并。
- 重试退避。
- 隔离舱:不同依赖或业务使用独立的连接池、并发额度、队列,避免一个故障耗尽所有共享资源。
- 提前容量规划和压测。
- readiness 摘除故障实例。
- 关键链路与非关键链路资源隔离。
26. 安全编码
26.1 Go 服务常见安全风险有哪些?
- SQL 注入。
- 命令注入。
- 路径穿越。
- SSRF。
- 不安全反序列化或超大输入。
- 身份认证与权限校验缺失。
- 敏感信息日志泄露。
- 弱随机数生成 token。
- TLS 校验被关闭。
- 依赖漏洞。
- 无超时、无大小限制导致 DoS。
26.2 如何防止命令注入?
优先不调用 shell。使用参数数组执行固定程序:
go
cmd := exec.CommandContext(ctx, "convert", inputPath, outputPath)不要把用户输入拼接到 sh -c 字符串。即使使用参数数组,也要对文件路径、选项和资源消耗做白名单与限制。
26.3 如何防止路径穿越?
- 不直接拼接用户输入。
- 清理路径。
- 使用安全基目录。
- 计算绝对路径后验证仍在基目录内。
- 注意符号链接和 TOCTOU。
- 更好的是让用户传资源 ID,而不是任意路径。
只判断字符串中是否包含 .. 不够。
26.4 SSRF 如何防护?
- URL scheme 白名单,只允许 http/https。
- 解析主机并检查解析后的所有 IP。
- 阻止 loopback、私网、链路本地、云元数据地址。
- 重定向后重新校验目标。
- 限制端口。
- 使用出站代理/防火墙。
- 设置超时、响应大小上限。
- 防 DNS rebinding,连接目标与校验结果要一致。
26.5 随机 token 应使用什么?
使用 crypto/rand:
go
b := make([]byte, 32)
if _, err := rand.Read(b); err != nil {
return "", err
}
token := base64.RawURLEncoding.EncodeToString(b)math/rand 适合模拟和普通随机选择,不适合密码、验证码、会话 token。
26.6 密码如何存储?
不要明文保存,不要用普通快速哈希。使用专门的密码哈希算法,例如 bcrypt、scrypt、Argon2,并使用独立 salt 和合理成本参数。
认证系统还应考虑:
- 登录限速。
- MFA。
- 密码重置 token 生命周期。
- 会话撤销。
- 凭证轮换。
- 防用户枚举。
26.7 TLS 有什么常见错误?
- 设置
InsecureSkipVerify: true。 - 不验证主机名。
- 使用过旧协议或弱套件。
- 证书到期未监控。
- 私钥权限不当。
- 内部服务误以为“内网就安全”。
测试环境临时跳过验证的代码不能进入生产。
26.8 如何限制请求资源?
- Header 大小。
- Body 大小。
- JSON/Proto 消息大小。
- 上传文件大小。
- 解压后大小,防压缩炸弹。
- 单请求处理时间。
- 并发数。
- 分页上限。
- 正则或解析复杂度。
- 批量 ID 数量。
继续阅读
- 排错
- 排错
- 排错