惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Microsoft Security Blog
Microsoft Security Blog
WordPress大学
WordPress大学
Stack Overflow Blog
Stack Overflow Blog
D
DataBreaches.Net
罗磊的独立博客
博客园 - 司徒正美
Last Week in AI
Last Week in AI
The Cloudflare Blog
大猫的无限游戏
大猫的无限游戏
Microsoft Azure Blog
Microsoft Azure Blog
B
Blog RSS Feed
The GitHub Blog
The GitHub Blog
宝玉的分享
宝玉的分享
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
小众软件
小众软件
Jina AI
Jina AI
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Hugging Face - Blog
Hugging Face - Blog
B
Blog
博客园 - 【当耐特】
V
V2EX
Apple Machine Learning Research
Apple Machine Learning Research
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
酷 壳 – CoolShell
酷 壳 – CoolShell

Deep Router

解决Codex Desktop高频TRACE日志写入logs_2.sqlite问题 | Deep Router Hermes Agent 优化:解决GLM-5.2模型429(1305 overloaded) | Deep Router 解决微信图片加载缓慢:从DNS到OSPF路由的问题排查 | Deep Router Windows Alma 太卡? 我有特殊的使用技巧 | Deep Router 使用Pipedream同步RSS至Twitter(X.com) | Deep Router 使用Pipedream同步RSS至BlueSky | Deep Router Dae搭配AdGuard Home使用指南 | Deep Router OpenWRT IPv6防火墙规则:如何放行客户端地址及端口 | Deep Router 国内外DNS服务器推荐列表 | Deep Router 在OpenWRT上使用Neovim | Deep Router ImmoralWRT软件源分享 | Deep Router NieR Automata Ver 1.1a | Deep Router 网关互指情况下,旁路由是否需要开启upnp? | Deep Router 【Horace And Pete】百年酒馆 第一季 1080P 阿里云 | Deep Router
KixDNS:用Rust重塑旁路网关的DNS分流——防污染防泄漏 | Deep ...
Yawatasensei · 2026-06-28 · via Deep Router

家里网络拓扑一直是MikroTik RouterOS做主路由,Debian旁路网关跑sing-box透明代理(以前使用Daed,但是最近发现Daed对anyTLS的支持并不是很好,所以换到了Sing-box)。DNS层之前用的MosDNS,负责国内/海外分流、DoH上游查询和缓存加速。整体跑了挺久,但有两个一直让我不太舒服的点:

第一,MosDNS的YAML配置太重了。 它的sequence → parallel → fallback插件链模型虽然灵活,但想改一条分流规则,你得在几十行YAML里理清插件间的依赖关系。加了AdGuard Home做广告过滤后,MosDNS退化成纯转发器,但配置复杂度一点没降。

第二,Go的GC抖动。 高并发查询时偶发延迟尖刺,p99有时候会飙到300ms以上。日常感知不强,但手机推送、IoT设备的心跳包偶尔会因为 DNS解析慢了半拍而重试。

第三,维护成本。我的分流需求其实很固定——国内域名走阿里/DNSPod DoH,海外域名走境外DoH,命中污染特征就重查。但用MosDNS实现,每种逻辑都要拆成 query_matcher + sequence + forward 插件组合,改一条上游地址要翻好几层缩进。对于"改完即忘、过两个月再看一脸懵"的家用场景,YAML插件链的认知负担太高,整体还是偏重了。

我需要的东西其实很简单:能按GeoSite分流国内/海外,上游走DoH,有个靠谱的缓存,配置别太反人类,性能稳一点。

然后我在GitHub上翻到了KixDNS

notion image

📝 KixDNS是什么

一个用Rust写的高性能非递归DNS转发器。核心特点:

  • Pipeline架构:不是MosDNS那种插件链,而是声明式的管道选择(pipeline_select)+ 两阶段处理(请求匹配 + 响应匹配)
  • 零拷贝UDP处理:基于 BytesMut,最小化内存拷贝
  • moka缓存引擎:Rust生态里最快的并发缓存,支持后台刷新和stale兜底
  • 自适应流控PermitManager 根据上游延迟动态调整并发许可数
  • 热重载配置:改完 pipeline.json 自动生效,不用重启服务
  • GeoSite原生支持:直接读 .dat 格式的geosite数据库做分流

说白了,它把我需要的功能全做了,而且用Rust的方式做——零开销抽象、无 GC、编译期安全。

📝 同类DNS转发器横向对比

在决定换之前,我把KixDNS和之前考虑过的两个方案做了系统对比,分别是之前使用过的MosDNS以及还有不少人在用的SmartDNS。

1. 架构哲学

MosDNS

SmartDNS

KixDNS

语言

Go

C

Rust

配置格式

YAML(插件链)

conf(类 dnsmasq)

JSON(声明式管道)

核心模型

sequenceparallelfallback

多上游测速取最快IP

pipeline_select → 两阶段匹配,也可实现多上游使用最快IP

分流方式

插件组合,需手写逻辑

server-group + domain-rule

GeoSite匹配 + 响应CIDR检测

缓存

内置(可持久化)

内置

moka(后台刷新 + stale 兜底)

上游协议

UDP/TCP/DoT/DoH

UDP/TCP/DoT/DoH/DoQ

UDP/TCP/DoT/DoH/DoQ

热重载

不支持,需重启

不支持,需重启

支持,文件监听自动生效

Web UI

有 Dashboard

2. 适用场景

MosDNS适合那种喜欢"编程式"控制 DNS 每一步流程的用户。它的 sequence 插件本质上是一个迷你的DSL,你可以写出非常复杂的条件分支:if 广告域名 → rejectif 国内域名 → forward_localelse → forward_remote with fast_fallback。代价是配置文件动辄上百行,可读性差,团队协作时交接成本高。

SmartDNS的核心差异化是"测速选 IP"——它会对上游返回的多个A记录做TCP连接测速,把访问最快的IP返回给客户端。这个功能在国内运营商DNS污染 + CDN调度不精准的场景下确实有用。但它的分流逻辑依赖 server-group 和 domain-rule,想实现GeoSite级别的国内外分流需要写很多规则,不如直接读geosite.dat 来得干净。另外C语言的内存管理虽然性能好,但在高并发下的稳定性不如Rust的所有权模型。

KixDNS走的是另一条路:声明式管道。你不需要"编程",只需要"声明"——域名匹配什么条件就走哪个管道,管道里定义上游和响应处理逻辑。配置文件就是一棵JSON树,一目了然(但是写起来太屎了,JSON作为配置文件真的写起来像吃屎。)。它的杀手锏是响应匹配response_matchers):不仅能在请求阶段按域名分流,还能在响应阶段检查返回的 IP 是否命中污染特征(比如 0.0.0.0/32240.0.0.0/4),一旦命中就自动jump到备用管道重查。MosDNS可以做到类似的效果,但是不如Kix的配置简洁。

3. 性能体感

我没有做严格的benchmark(三个软件跑在不同硬件上比不公平),但从生产环境的实际体感来说:

  • KixDNS:日常p50 = 16ms(国内)/ 50ms(海外),p99 = 90ms,内存19MB,零GC抖动
  • MosDNS(之前的生产数据):p50差不多,但p99偶尔飙到300ms+,内存40-60MB
  • SmartDNS(短期测试):测速选IP功能会增加首次解析延迟(多了一次TCP握手测速),缓存命中后和前两者差不多。

📝 KixDNS安装与配置

0. 主机环境

1. 安装KixDNS

从GitHub Releases下载预编译二进制:

2. 配置pipeline.json

pipeline.json是kixdns配置文件的名称,你可以根据自己的需要更改成自己喜欢的名称,例如config.json,但需要注意,后面的systemctl启动及管理脚本使用的是pipeline.json这个名称,所以你也需要进行替换。

以下是我的自用配置,没有DNS泄漏,同时在一定程度上可以避免DNS污染。

配置要点:

缓存策略cache_background_refresh: true 是关键——缓存条目在TTL剩余 15%(cache_refresh_threshold_percent)时会触发后台异步刷新,客户端永远拿到的是新鲜数据,不会因为缓存过期而等待。serve_stale: true 确保即使上游全部超时,也能返回上一次的旧缓存(保留30秒),而不是直接报错。这两个特性组合起来,意味着客户端几乎永远不会经历DNS解析失败。

反污染跳转cn_doh 管道的 response_matchers 列了6个CIDR,覆盖了国内DNS投毒的典型特征 IP:0.0.0.0/32(黑洞)、240.0.0.0/4(保留地址段)、以及几个已知的污染节点。一旦国内DoH返回的IP命中这些CIDR,KixDNS会自动 jump_to_pipeline: global_doh,用境外DoH重查。这个机制比MosDNS手写fallback逻辑优雅得多——它是声明式的,你只需要列出"什么样的答案是脏的",而不是写"如果脏了怎么办"的控制流。

DoH连接池doh_pool_size: 32 意味着KixDNS会维护32个到每个上游DoH服务器的复用连接。DNS查询包只有几十字节,但DoH跑在HTTPS上,如果没有连接复用,每次查询都要走TLS握手(~100ms+)。连接池把这个开销摊薄到接近零。

自适应流控flow_control 的三个参数定义了一个动态窗口:初始500个并发许可,根据上游延迟在100-800之间自动伸缩。上游响应快就放大窗口提高吞吐,上游变慢就收缩窗口防止雪崩。这个机制让KixDNS在上游波动时不会因为排队过多请求而加剧延迟。

upstream:你需要替换global_doh 中的upstream 为你自己使用的国外DOH服务,例如Google、Cloudflare、NextDNS、Adguard DNS等等。我个人推荐NovaXNS

3. systemd 服务

LimitNOFILE=65536 是必须的——DoH连接池 + UDP pool + TCP pool加起来,默认的1024文件描述符限制根本不够用。

4. 接入sing-box

sing-box的DNS配置指向KixDNS:

注意这里用的是 "type": "tcp" 而不是 "udp"——这是一个踩坑后的结论,下面详细说。

5. 完整DNS链路

6. 踩坑:sing-box的UDP DNS黑洞

部署完KixDNS后过了一天,手机部分页面打不开、加载很慢。第一反应是查KixDNS日志:

KixDNS完全健康。问题出在上游—sing-box:

sing-box的DNS配置是 type: "udp" 指向 127.0.0.1:5353,KixDNS也确实回了,但sing-box那边27%的查询 context deadline exceeded(10秒超时)。

翻GitHub找到了根因——Issue #1918:sing-box在Linux旁路网关 + strict_route: true + TUN system 栈的环境下,DNS server使用UDP协议时,会持有一个长期固定源端口的UDP socket。这个socket对应的conntrack flow会逐渐变成stale/blackhole状态——包发出去了,回包却被TUN的strict_route规则吞掉。

这是sing-box的已知bug,上游至今未修。解决方法很简单:把sing-boxDNS server 的 type 从 "udp" 改成 "tcp"。KixDNS同时绑了UDP和TCP(bind_tcp: "0.0.0.0:5353"),零成本切换。

改完后做了个压力测试——80个并发TCP DNS查询:

实际峰值才7 QPS,TCP的322QPS 承载能力是实际负载的46倍。本地环回的TCP握手开销在 0.1ms级别,在50-85ms的DoH上游延迟面前完全可以忽略。

运行状态

换到KixDNS + TCP模式后稳定运行,几个关键指标:

🤗 经验总结

  1. sing-box旁路网关 + strict_route的UDP DNS有坑。如果你遇到间歇性DNS超时,先检查sing-box的DNS server type,改成TCP可能直接解决。参考Issue #1918
  1. DNS分层缓存不是多余的。AdGuard Home做第一层缓存(高频域名 + 广告过滤),KixDNS 做第二层(分流 + DoH + stale 兜底)。两层配合下,即使KixDNS的上游全部挂了,AdGuard Home的stale缓存还能扛一阵。
  1. 选DNS工具别只看功能列表。MosDNS功能最全,但配置复杂度也是最高的。KixDNS功能精简,但每一个功能都做到了生产级。对于家庭网络,稳定和简单比功能丰富更重要。
  1. Rust 不是万能的,但在网络基础设施场景下确实有优势。无GC意味着延迟可预测,所有权模型意味着内存安全,编译期检查意味着配置错误在启动时就暴露而不是运行时炸。

对大多数旁路网关用户来说,DNS转发器的核心价值就三件事:分流准、解析快、配置简单。在这三个维度上,KixDNS是我目前用过的最优解。

📎 参考文章

有关KixDNS安装或者使用上的问题,欢迎您在底部评论区留言,一起交流~ 版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!