Skip to content

毕业项目:dnsproxi-go ​

DNS UDP 代理:wire 解析 → 转发 → 缓存 → 逐出 → TinyLFU 准入 → 主动老化。 对照同构的 Rust 版(本机 rs/dnsproxi)。

它在哪条弧上 ​

  • ② 服务搭建的终点读数:P6 notes 讲架构与逐级构建(D1→D7)。
  • ③ 可观测性的落地现场:P17 指标、P18 日志都长在这个服务上。

真机压测读数(同机,dnsq 压测器) ​

场景Go qpsGo P50Go P99Rust qpsRust P50Rust P99
A 无缓存 keyspace=100010 9241.25ms4.5ms12 4390.92ms5.1ms
B 缓存 on keyspace=10009 3361.28ms6.8ms12 1560.65ms6.9ms
B2 缓存 on keyspace=1009 9401.26ms5.6ms19 4220.63ms2.0ms
C 准入 on keyspace=500010 2881.09ms6.9ms15 0370.95ms2.4ms
D +主动老化10 3811.15ms6.1ms13 5960.97ms4.1ms

三个读数(详细分析见仓库 dnsproxi-go/LESSON.md):

  1. 总量级同档:Go ~10k qps vs Rust ~13-19k。瓶颈在 per-query socket 生命周期,不在语言运行时。
  2. 命中路径差距最大(B2,2×):Go 版 RWMutex + 两次拷贝 vs Rust 版 Arc 读锁 + 一次拷贝。省一次拷贝是现成优化点,留作练习。
  3. 所有档位零丢包:差异全是代理自身吞吐/延迟,不是网络抖动。

指标与日志(③ 阶段的落地形态) ​

sh
cd dnsproxi-go
DNS_LISTEN=:15353 DNS_CACHE=1 DNS_LOG_LEVEL=debug go run .
# 另一个终端:
curl -s localhost:9153/metrics | grep dnsproxi
指标label说明
queries_totalqtype×outcome量与错误率、命中率;qname 高基数不进 label
upstream_duration_seconds无只量成功转发的上游往返
upstream_errors_totalreason(timeout/network/refused)死上游→refused(ICMP 立回),黑洞→timeout(挂满 deadline)
in_flight_queries无worker pool 模式下顶到 N 即背压点
cache_entries无PutFull 涨、expirer 落,双更新点

日志:JSON 结构化,每查询一个 req_id 由 ctxHandler 自动注入(P16 手艺在独立 module 的重写),DNS_LOG_LEVEL 可调级别。

跑测试 ​

sh
cd dnsproxi-go && go vet ./... && go test -race -count=1 ./...