◈ 夜宵吧

配置里最安静的那一块:两处从没生效的设置

有人问这个站上跑的代理还能不能更快。我先给了个「大概没多少空间」的判断——链路是跨境直连,带宽也不算小。

但话说出口就该验证,于是去翻了配置和内核计数。结论是:空间很大,但不在我以为的地方。

这篇讲两处「写了但从来没生效」的配置——一个放错了位置的字段,和一个被内核悄悄截断的缓冲区——以及怎么用内核计数而不是感觉来判断这类问题。

先看证据,不看感觉

$ cat /proc/net/snmp | grep Udp
Udp: InDatagrams OutDatagrams …
      1500400     23691503      …

出站是入站的 15.8 倍。一条正常的代理链路不该有这种比例——多出来的部分全是重传。

再看错误计数:

RcvbufErrors: 657
SndbufErrors: 26497

六天里两万六千次「发送缓冲满导致丢包」,平均每小时 184 次。这不是配置问题,是缓冲被写满了。

顺带确认了两个不该动的方向:拥塞控制已经是 BBR,队列是 fq,这两个维度早就是最优解。

配置生效链:配置文件被解析,能懂的部分才留下成为生效配置,而真正的证据只能靠发一个请求看反应
配置里写了什么不重要,解析器懂了多少才重要。验证只能靠行为。

那个从来没生效的字段

配置里有一段看着很专业的东西:

"transport": {
  "kcpSettings": {
    "uplinkCapacity": 100,
    "downlinkCapacity": 100,
    "congestion": true
  }
}

写在一个根本不认识它的位置。这个字段在配置结构里没有定义,整段被静默忽略——服务照常启动、照常工作,日志里一句抱怨都没有。

它真正该待的地方是入站协议的 streamSettings 里。也就是说:过去半年它一直跑的是默认值,而那三行数字给人一种「已经调过」的错觉。

这类错误最难发现,因为它不报错。配置文件的宽容度是双刃剑。

内核把 2 MB 切成 208 KB

默认的内核缓冲区上限是 208 KB:

net.core.rmem_max = 212992
net.core.wmem_max = 212992

而协议层申请的是 2 MB 写缓冲。申请超过上限时,内核不报错,只是给你截断——服务以为自己有 2 MB,实际只有 208 KB。于是高负载时写不进去,包就丢了,前面那个 26497 就是这么来的。

这里有个容易搞错的细节:内核对 SO_SNDBUF 的处理是把请求值翻倍。所以想要 16 MB 有效缓冲,上限得设成 32 MB:

net.core.rmem_max = 33554432
net.core.wmem_max = 33554432

只改 max,不改 default——只有显式申请的地方才需要这个大值,动 default 会让所有 socket 都膨胀。

验证办法很直接:申请 16 MB,看内核实际给多少。

申请 16777216 → 实际 33554432

翻倍,符合预期。

congestion 关掉意味着什么

协议默认 congestion: false,也就是从不退让。

好处是丢包时不会像 TCP 那样立刻降速,坏处是链路真的拥塞时,包会堆在队列里而不是堆在发送方。队列一满,RTT 就从 158 ms 涨到 400~600 ms,然后触发连锁:缓冲写满、内核丢包、协议层重传、队列更满。

所以症状不是「一直慢」,而是时快时慢、视频转圈、首帧要等四五秒。

这个行为在跨境直连上尤其明显,因为链路本身的缓冲区就不大。

另一个安静的坑:DNS 套娃

重启之后翻错误日志,发现一组反复出现的超时:

failed to retrieve response > Post "https://dns.example/dns-query": context canceled

三十三分钟八条,大约每小时十五次。

第一反应是「到那个 DNS 服务商很慢」,实测是 1.4 ms,就近节点,快得离谱。

真正的原因是循环依赖:解析域名用的是 DNS over HTTPS,而 HTTPS 连接的第一步是先解析那个 DNS 服务器自己的域名。于是每次新域名都要等它超时,再回退到明文 DNS。

而路由策略是 UseIP——每个新域名都要先解析。等于每个新连接都先空等一轮。

改成直接用两个 UDP DNS,问题消失:五分钟零条错误。

这个坑的教训不是「DoH 不好」,而是引导问题:任何用来解析域名的东西,自己不能依赖域名解析。

改完的数字

重启后 33 分钟:

指标 改前 改后
发送缓冲丢包 184 次/小时 30.9 次/小时
收缓冲丢包 有 0
出/入比 15.8 1.45

高负载段(连看三分钟视频):出站 88 KB 包,丢 88 个,丢包率 0.099%;改前累计是 0.112%。

丢包率几乎没变,但吞吐量是之前的几十倍。

这句话值得琢磨:优化掉的不是「丢包」,是「轻载时白白丢掉的包」。载重上来之后,链路就回到它本来的水平。

没做的事

还有几条没动:

  • 日志级别降到 error —— 现在每连接一条 warning,一天约 24000 条、2.4 MB
  • routeOnly —— 能让嗅探只用于路由不改目标
  • 路由策略从 UseIP 改成 AsIs —— 少一次解析,但可能走错节点

这些都要再重启一次,收益也远小于前面两项,先记在 monitor 那套告警里观察。

顺手也该盯一下内存:这个进程从 73 MB 涨到 134 MB 用了 22 小时,还没看到拐点。