FWDD.NET · 技术文档站

范文多多

聚焦后端工程实践:语言教程、功能实现、面试题与运维脚本。每篇都从真实踩坑场景出发,给出可直接复用的代码与取舍依据。

fork 后写时复制页表在内存共享与 exec 之间的状态演变

这道题在面试里出现的频率很高,但真正能讲清楚的人不多。多数人背下来的答案是「fork 之后子进程和父进程共享物理页,页表项被标成只读,任何一方写入就触发缺页,内核复制一份新的物理页」。这段答案本身没错,可它只覆盖了从 fork 返回、到某一方第一次写入之间的那一小段。面试官一旦追问「exec 的时候这些页怎么办」「如果子进程还没写就 exec,父进程的页会不

用 limit_req_zone 按 cookie 中的租户标识分桶限流配置

多租户 SaaS 里最常见的线上事故不是全站被打挂,而是某一个租户刷爆了共享的 API 网关配额,把其他租户一起拖下水。用 limit_req 做全局限流很容易,limit_req_zone $binary_remote_addr zone=api:10m rate=20r/s; 一行就完事;但要做到「按租户分桶、每桶独立计数」,难点在于取 key 的方式:

sync.Pool 中的对象被 GC 清空后 Put 新对象的复用边界

面试里问 sync.Pool,多数人能背出「临时对象复用、减轻 GC 压力」,但真正能区分候选人的是细节:GC 到底怎么清空 Pool?清空之后马上 Put 进去的对象,会不会在下一轮 GC 又被清掉?为什么必须调用 runtime.GC() 两次才能观察到一个对象从 Pool 中彻底消失?Put 里的对象什么时候会被「私吞」而不是进入共享队列? 本文从 G

用 while read 配合 IFS= 读取含空格路径清单换行安全的写法

批量处理文件清单是运维脚本的日常:备份目录列表、待清理日志清单、批量重命名映射表。很多人第一次踩坑都长一个样——清单里明明有 a b/c.log 这种带空格的路径,脚本跑完发现只处理了 a,后半截 b/c.log 被当成了另一个变量或者直接丢掉。更糟的是循环体里一执行 ssh、ffmpeg、read 之类会吃掉标准输入的命令,循环莫名其妙只跑了第一行就结束了

用 EXPIRE NX 配合 ZADD GT 实现带过期语义的滑动分数榜单

排行榜是 Redis 最经典的用法之一:用 ZSET 存成员与分数,ZREVRANGE 取前 N 名,ZINCRBY 累加积分。但只要业务从「累计积分榜」滑向「热度榜 / 互动分榜」,问题立刻出现——成员分数需要被反复更新,而更新又要遵循「只能涨不能跌」或「窗口内有效」的规则。纯 ZSET 方案在这里会暴露三个具体缺陷:分数更新的并发覆盖、过期语义缺失导致僵

let-else 在解构嵌套 Result 时的提前返回与借用冲突规避

在 Rust 里解析配置、处理网络响应、读取嵌套 JSON 时,最常见的写法是 match 里套 match,层层缩进之后,真正的业务逻辑被推到屏幕右侧。Rust 1.65 稳定了 let-else,它把「匹配成功就绑定变量、失败就立刻离开当前控制流」这段样板代码压缩成一行。但很多人把它当成语法糖直接套到嵌套 Result 上,随后撞上两类硬伤:一是 els

用 Ansible uri 模块轮询健康检查端点:until 重试与 failed_when 分级判定

滚动发布或批量重启之后,很多团队会用一个 sleep 任务加一次 curl 探测来「确认服务起来了」。这种写法在测试环境勉强能用,到了生产环境就会暴露三个问题:探测太早,服务还在初始化,任务直接失败;探测太晚,流水线白白空转几十秒;服务确实挂了却没有明确的重试策略,失败点分散在多个 task 里,很难定位到底是谁把服务拖垮的。Ansible 的 uri 模块