- SignalDesk3小时前
开端 家里那台小米 AC2100 ,刷了 ImmortalWrt ,安稳地当了几年的上网工具。一开始我是想让其运行 xray 工具的,因为我的服务器上的代理工具使用的 VLESS 协议,使用 xray 是目前流行的解决方案。但是我将其安装上之后,路由器直接卡死了,我一开始以为是 xray 本身有问题,于是又试了一圈其他的 VLESS 协议的程序,无一例外全部卡死,而且卡死后路由器必须得重启才能使用。 当时查了一下,如果想使用代理软件的话,目前资源最低的就是 trojan 协议,于是我把 go-trojan 安装到路由器中,果然能正常运行了。我以为这就是完美方案了,没有想到这只是趟坑的序幕,接下来几天,我发现会时不时的上不了网,一开始我还以为是运营商的问题,没有在意,然后出的问题多了,我就忍不了了,开始把怀疑对象转到了我的路由器身上。 我首先 ping 了一下路由器网关,果然 ping 值不对劲,看来是路由器负载过高了,但是要找到问题,必须得确定是哪个进程导致的。于是我直接 SSH 进入路由器后台,使用 top 命令就盯着它看。当时 ai 也没有这么智能,只能人工来观测。 经过我的不懈努力,终于抓住了一个 CPU 飙升的瞬间。 通过图片,貌似是系统的网络卡住了,cpu 的 io 比较高,同时 sys 也比较高。trojan-go 虽然也占用 CPU ,但是不像是主因,然后我就丈二的金刚摸不着头脑了。又过了几天实在忍无可忍了,想到这些天也就增加了一个 trojan-go ,干脆死马当活马医,先把它下掉。没有想到拿掉它后,路由器不卡了! 又过了些天,我还是不死心,一定要将我花 40 元大洋买的路由器利用上,在网上又查了一下,找了一篇关于 golang 在 mips 设备上的兼容性的 github issue 描述 Abnormal memory usage on MIPS platform with GOMIPS=softfloat · Issue #39174 · golang/go 。原来 golang GC 时也需要耗费一定的资源,但是 mips 硬件资源低,会导致系统 CPU 使用量飙升。看样子跟我的症状比较吻合,于是按照 issue 中的解决思路,使用 ulimit -v 32768 命令限制 golang 虚拟内存大小,不让它 GC 大块的内存。同时设置环境变量 GOMAXPROCS=2 ,约束其 CPU 使用的核心数上限。 经过上述一番操作后,没有想到之前路由器的 CPU 飙升的问题就得以解决了! 升级 虽然我的路由看上去已经是完美的方案了,但是它毕竟是 trojan 协议,手里面的 VLESS 协议的服务它是用不上的。现在已经进入了氛围编程阶段,以前耗费大量时间才能完成的事情,现在可能一两天就能搞定了。我何不使用氛围编程复刻一个轻量的 VLESS 协议的客户端呢?说干就干,反正不是我干。 我想要的东西其实很窄。协议只要 REALITY 加 VLESS 加 Vision ,这是我服务端已经在用的。入口只要 SOCKS5 和透明 REDIRECT 。内存要在启动的时候就定死,之后不涨,宁可拒绝连接也不要去申请新内存。单线程,一个 epoll 循环就够,反正只有一个核。 用 Zig 写 Zig 对于嵌入式来说是一门完美的语言,没有 runtime 、没有 GC ,打出来的包个头小,启动后占用内存低。而且其对于低端 mips 设备的支持完整,编程语言的安全性也比 C 要强很多。总之是解决当前场景的最优方案。 我给它起名 zray ,然后就开始写了。 由于是氛围编程,这里就不透露技术细节了。但是有一点需要注意由于牵扯到网络加密相关的技术,如果你直接使用 Claude Code 工具化,它会拒绝编写代码,认为你是在做有风险的事情,你通过修改提示词并不能瞒过他,所以这种事情还是要交给第三方 Agent 来做。 写完后,交叉编译出来版本,让 Agent 直接 ssh 到路由器上测试即可。 最终成功还是喜人的,编译出来的文件只有 800KB ,是 trojan-go ( 13MB )的十六分之一,比起 34MB 的 xray 更是小了将近两个数量级。运行起来的话,虚拟内存只有 7MB ,比起 18 MB 的 trojan-go 来说也只有其一半不到。关键是我要求其使用连接池和上游服务进行通信,总体内存是可控的。 测一下速 速度也不拖后腿。在路由器上经隧道拉 50 MB 文件,HTTPS 流量能跑到 9 到 10 MB/s ,zray 只占一个核的 17% 左右;而同一个文件不走隧道直接从 WAN 下载反而只有 1.4 MB/s ,慢的是运营商的国际出口。HTTP 流量就差远了,被 CPU 卡在 1.5 MB/s 。同一条隧道,为什么差这么多?这要从 Vision 的设计说起。 zray 和服务器之间是一条 TLS 1.3 连接,可以叫它"外层 TLS"。默认情况下,客户端发出的每个字节都要经这层 AES-GCM 加密后才进隧道,服务器解密后再转发。桌面和手机的 CPU 都有专门的 AES 指令,一条指令完成一轮运算,加密 1 GB 数据不费什么力气;可 MT7621 这颗 MIPS 核没有这种指令,只能用普通整数运算一步步"软算",实测这颗核的极限就是 1.5 MB/s 左右,此时 zray 一个线程被占到 98%。 Vision 的办法是避开二次加密。你访问的是 HTTPS 网站时,数据本身已经是浏览器和网站之间的 TLS 加密流了,再套一层外层 TLS 等于在加密密文,纯属浪费。所以 Vision 只让 TLS 握手那几个包走外层 TLS ,等它识别到内层握手完成、后面全是加密后的应用数据,就通知服务器切到"直拷",之后的数据原样复制进隧道,不再经过 AES-GCM 。这时 zray 只是在两个 socket 之间搬运字节,CPU 占用 17%,速度完全由链路决定。 而明文 HTTP 没有这个待遇。它本身没有加密,要是也直拷进隧道,等于把明文直接暴露在公网上,协议不允许这么做。所以 HTTP 流量必须一直留在外层 TLS 里,每个字节都要软算 AES-GCM ,速度就被 CPU 钉死。好在实际上网时绝大多数流量都是 HTTPS ,影响不大,但它解释了为什么 Vision 这种"握手后直拷"的设计在低端设备上格外有意义。 这里还踩了一个坑:刚部署完实测只有 1 MB/s ,查了半天 zray 的代码一无所获,抓包才发现是路由器内核给 TCP 接收缓冲设的默认上限只有 930 KB ( 128 MB 内存的机器就这么算的),再乘上 190 ms 的 RTT ,单条流的理论上限就是 1 MB/s 左右。在 /etc/sysctl.d/ 里把 net.ipv4.tcp_rmem 的上限调到 4 MB ,速度直接翻了几倍。跟程序没关系,但这种坑不踩一次是不会知道的。 代码放在 GitHub , yunnysunny/zray ,MIT 协议。README 有中文版。预编译的二进制覆盖 mipsel 、mips 、aarch64 、armv7 、x86_64 ,都是静态 musl ,scp 到路由器就能
- 情报分类:服务器与云资源
- 分类依据:内容涉及服务器、云资源或网络线路
- 信息来源:服务器 / V2EX
- 发布时间:2026/10/20 23:00:12
- 暂无回复