Ember Moth's Blog

为世界上所有的美好而战!

TL;DR — 代理节点端的热路径是”每秒几十万次的小操作”:封一帧、加密一块、排队一个上传分片、发一个 UDP 报文。这类路径上每次操作多一次堆分配,单看 ns/op 几乎没有变化,但会以 GC assist、堆扫描和尾延迟抖动的形式在高负载下集体兑现。这篇记录一轮把 SMux、VLESS Encryption、XHTTP、AnyTLS、Hysteria2 等热路径改到 0 allocs/op 的工作:先定六条原则,再用五个可复用的模式落地,每个模式都配 wire 格式不变的 round-trip 测试与前后基准。最后一节讲零分配买不到的东西:当 profile 里排第一的是 write syscall 时,该做的是写合并,而我们否决了两种看起来更聪明的源侧方案。


阅读全文 »

TL;DR — 代理软件的路由规则是一张有序表:逐条检查,第一条命中的规则决定出口。这个语义天然导致”规则越多、未命中越慢”,因为一个不命中任何规则的请求要把整张表扫完,而带地理类别的规则每条都是几万个域名或网段。这篇文章记录一次不改变任何语义的重新设计:利用”最早规则赢”对匹配维度可分配这一性质,把所有规则的同类选择器合并进三个静态索引(冻结哈希表、一个 Aho-Corasick 自动机、一棵前缀树),每个索引只回答”本维度最早命中的规则是哪一条”,跨维度取最小即得全局答案。结果是未命中延迟从微秒级降到几十纳秒,并且不再随规则数增长;等价性由与朴素线性实现的百万次差分保证。


阅读全文 »

TL;DR — 上一篇讲了移动端为什么把 TCP 交还给内核。桌面端我们仍然用 smoltcp 跑用户态 TCP 栈,因为桌面没有 Extension 那样的内存墙,也不值得为省内存让每个载荷多穿一次 TUN。这条路径的骨架来自 clash-rs 的 netstack,社区已经替我们修过一批坑;这篇记录的是在此之上我们自己踩到的六个:一个 Listen 状态的 socket 在 SYN 送达前就被回收、一个形式上是 UB 的 SPSC 环、一个在 tx 队列满时自旋的 poll 循环、一个能终止整条隧道的 UDP 分片包、一个藏在 TxToken 里的 panic,以及每次读都清零 64 KiB 的接收路径。每个坑都给出症状、根因、修法和守住它的测试。


阅读全文 »

TL;DR — 移动端隧道进程的内存墙让用户态 TCP 栈(smoltcp)成了负担:每条连接要背几百 KiB 的固定缓冲,还要自己处理重传、拥塞和窗口。system netstack 换了一条路:不实现 TCP,而是把 TUN 里读到的 TCP 包原地做源 NAT,改写后写回 TUN,让内核把它当成发给本机 listener 的连接;accept 出来的普通 socket 再按 NAT 表里恢复的原始五元组交给分发器。代价是每个 TCP 载荷要穿越 TUN 两次,第一版因此比 smoltcp 慢 40%,靠 GRO/GSO 批量 I/O 追回来;收益是每条连接的用户态内存从 1 MiB 降到一个约 100 字节的 NAT 表项。这篇文章记录设计、那些只有真机才会教你的坑,以及实测数字。


阅读全文 »

TL;DR — 这次迁移不是“把 import 从 gin 改成 hertz”这么简单,而是一次分层完成的渐进式重构:先抽离 transport 层,再做 Gin 兼容层,最后把热点路由和关键中间件原生化到 Hertz。中间我们甚至短暂用 Fiber 验证过 fallback 方案。最重要的收获不是“换了框架”,而是沉淀出一套对存量 Go 服务可复制的迁移方法。


阅读全文 »

TL;DR — 为已有项目新增第二种数据库支持,工作量远大于”加个驱动依赖”。
本文记录 ppanel-server 从 MySQL-only 到同时支持 MySQL + PostgreSQL 的完整改造:重构配置抽象层、拆分两套迁移 DDL、修复 model 层 20+ 个 MySQL 专属写法、顺带优化搜索索引。
涉及 2 个 PR、6 次提交、约 40 个文件改动。最有价值的发现是:引入异构数据库测试,意外暴露了一个 MySQL 下潜伏了半年的隐性 bug。


阅读全文 »
0%