Skip to content

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 是什么? ​

在同一个数据库本地事务中:

  1. 更新业务数据。
  2. 插入 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 数量。

继续阅读