12
2022
12
08:31:48

为什么 wireguard-go 高尚而 boringtun 孬种



推荐点击下面图片,通过本站淘宝优惠价购买:

image.png


https://github.com/WireGuard/wireguard-go


https://git.zx2c4.com/wireguard-go


WireGuard 项目 宣布了Windows 下 VPN 协议的内核模式实现 WireGuardNT。WireGuard 在 Windows 下的早期实现是一个用 Go 语言开发的用户空间实现 wireguard-go。它在当时需要关联一个虚拟网络设备,WireGuard 作者 Jason Donenfeld 不满意 OpenVPN 项目提供的虚拟网络接口 tap-windows,因此从头实现了他自己的 Wintun。Wintun 相对于 tap-windows 而言是一大改进,OpenVPN 项目之后自己也实现了对 Wintun 的支持。但 wireguard-go 仍然需要持续的在内核空间和用户空间之间上下文切换。为了移除这一性能瓶颈,虚拟网卡和加密等整个堆栈都需要移到内核。测量 显示,内核模式实现 WireGuardNT 比用户空间实现 wireguard-go 要快 10-25%。


几个月前了解到有 boringtun 这么一个项目:https://github.com/cloudflare/boringtun

它是一个基于 rust 的 wireguard 用户态实现,典型卷司 (cloudflare) 的卷材,而且该社区半死不活。

在此之前,为了在不支持 wireguard 之 Linux 内核实现的平台上运行 wireguard,专门提供了一个退而求其次的 wireguard-go :https://github.com/WireGuard/wireguard-go

它是一个 golang 实现。

说实话,golang 作为控制面的专职打手,它根本不适合做数据面,golang 实现 wireguard 一定不怎么样,事实上也确实不怎么样。我的意思不是说它的吞吐性能差,而是说它占用了太多的系统资源,比如 CPU 利用率非常高。这可能是 golang runtime 的开销,图方便,总得交点钱。

对比一下 wireguard-go 和 boringtun,实测结论是后者单流吞吐稍好,但不很。boringtun 的优势更多在于它基于 rust,一个时髦的语言,适合卷。当然了,golang 也时髦,但稍早些,热度过去了。无论 rust 还是 golang,性能都不如 C,但直到现在都没有一个 wireguard 的 C 实现,这意味着性能是最后才要考虑的事。

说回主题。

我稍微改了一下 wireguard-go,就吊打 boringtun 了,因为 wireguard-go 的代码结构实在是太好了,这和我前几个月的认知相悖,我承认当时是受到了强势误导。

wireguard-go 采用了流水线结构:吞吐优化札记,而 boringtun 没有,boringtun 从 ReadFromTun,到 Encrypt,直到 WriteToSocket,全部一气呵成,反过来也一样。这显然是一种落后的结构。

流水线结构可以充分利用多 CPU 资源,golang 透明化的 goroutine 正是干这事的老手,虽然这无疑会造成一些 runtime 开销,但 boringtun 却是彻彻底底的老样式。

话说到此,正好跟朋友聊天,Run To Complete 模型岂不是大家都推崇的?之前我还真不知道 RTC 模型,老练了,哈哈。但是我并不认为 RTC 在应对大并发时是高尚的。不然为什么汽车生产要用流水线呢?在我理解,流水线的本质是“让所有资源都同时动起来”,对应到代码,就是一段时间内,保持“不运行的代码行数”最低。

我并不认为维护 C style 就是守旧,没到那高度,编程语言最终目标是实现功能,而不是编程语言本身。

我估计 boringtun 是被迫老样式的,可能是 rust 逼的。rust 实现流水线结构可能过于困难,特别是对于 C 基础的程序员而言,但 golang 却保持了 C 的风格,我觉得 golang 是高尚的。

有趣的一个点, boringtun 竟然没有实现 UpdatePeer 这个 wireguard 的常规操作,据说是作者用 rust 实现起来过于困难,我在想为什么作者一开始不选择 C++ 呢,或者说 carbon?或者说,既然已经有了 wireguard-go,为什么非要 yet another 呢?如果发现了 wireguard-go 的问题,修正它肯定比 yet another 高尚。

大并发场景,非流水线结构将很难应对,系统资源得不到有效利用,总吞吐注定上不去。流水线典型的好处是满载情况下,总吞吐是单线吞吐的段数倍,我不否认 rust 实现流水线的可能性,但至少 boringtun 没有。

说说大并发改造。

无论 wireguard-go 还是 boringtun,正规的应对大并发场景的策略都是创建多个实例,当年的 Open虚拟专网 也是这般建议,但创建多个实例无论怎么看都不像是程序员的把式,不改代码就算没有技术含量,所以无论是 2013 年修改 Open虚拟专网 ,还是如今面对 wireguard,我还是一招鲜,吃遍天。修改起来很容易,就是把一个换成多个,比如把 tun 网卡换成多队列,把 UDP 换成多个 reuseport。

当真的动手修改时,我发现 rust 完全改不动… 我虽然不懂编程,但大致还是懂一些的,同样都是初学,我既然可以改 golang,为什么不能改 rust,我不能艰难地且违心不情愿地否定自己,我只能说 rust 太垃圾了,因为它没能让我学会,注意,不是我没能学会 rust,而是 rust 没能让我学会。

好了,说说怎么修改。

就跟种地农夫一样,真正的庄稼把式,是鄙视拖拉机的。明明可以创建多个实例,我却傻逼一样修改代码,只因为我是个程序员?好吧,我在 2013 年修改了 Open虚拟专网 ,全网搜基本都是我写的,在将近 10 年后,我用同样的方法修改了 wireguard-go,虽然不高尚,但管用,一招鲜,吃遍天。但实际上,读者要明白,真管用的方法就是创建多个实例。

关于 rust 如何卷,我不多说,至于 carbon, 我明天(已经是今日)准备写个 hello world。

关于 wireguard-go,我已经 fork 了自己的分支 GitHub - marywangran/wireguard-go: Mirror only. Official repository is at https://git.zx2c4.com/wireguard-go ,我会试着把它做的更好,用程序员的方式,而不是搭建多个实例的方式,试图单靠一个进程打满带宽。

我并不赞同把源码修成多线程,企图用单进程处理并发,但在工程上,这比 SRE 的工作更酷。

wireguard-go 高尚在于它做到了它该做的(CPU 利用率高,受 runtime 所限),boringtun 孬种在于它仅仅是跪舔了时髦的 rust 而已。

wireguard-go 本身就是一个退而求其次的兜底,没想到兜底方案都被卷,boringtun 是一个基于 rust 的实现,半死不活的社区让我觉得作者也仅仅是玩玩。wireguard 出场,难道大家不该多关注下协议吗?是吗?

浙江温州皮鞋湿,下雨进水不会胖。




本文链接:https://www.hqyman.cn/post/3296.html 非本站原创文章欢迎转载,原创文章需保留本站地址!

分享到:





休息一下,本站随机推荐观看栏目:


« 上一篇 下一篇 »

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

您的IP地址是: