v2fly / v2ray-core

A platform for building proxies to bypass network restrictions.

Home Page:https://v2fly.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

v2ray 的流量统计功能会使裸协议的 ReadV 和 WriteV 同时失效的说明

RPRX opened this issue · comments

commented

这个问题是研究 ReadV 时发现的,感谢 @badO1a5A90 的多次测试。

裸协议指 over TCP,如任意门、Socks、VLESS、VMess(可带加密)等,且不带 HTTP 伪装等额外的东西。

--

是否用 ReadV,取决于断言

_, isFile := reader.(*os.File)
if !isFile && useReadv {
if sc, ok := reader.(syscall.Conn); ok {
rawConn, err := sc.SyscallConn()
if err != nil {
newError("failed to get sysconn").Base(err).WriteToLog()
} else {
return NewReadVReader(reader, rawConn)
}
}
}

是否用 WriteV(WriteTo 内有断言)

n, err := nb.WriteTo(w.Writer)

--

对于刚刚定义的“裸协议”,这里的断言会成功,就可以用到 ReadV 和 WriteV。

现在存在的问题是,流量统计等功能普遍是通过套一层 Conn 来实现的,这也会导致断言失败,从而失去这两项优化。

以客户端下载(上传同理)的场景举例,配置为 任意门 inbound + VLESS outbound:

开启了 outbound 的流量统计时,VLESS 读取服务端返回的数据时就无法 ReadV,性能大打折扣
开启了 inbound 的流量统计时,任意门 往回写数据时就无法 WriteV,性能大打折扣

根据多次测试的数据,这两个“性能大打折扣”不会叠加,而是 取瓶颈,其中失去 ReadV 比失去 WriteV 更不利。

(顺便一提,流量统计的相关文档有些模糊,存在非预期行为,需要完善。API 的也需要补充)

解决方案:流量统计的数据收集放到 buf.Copy 等处,不套一层 Conn。

--

其它说明:

对于非裸协议,比如加个 HTTP 伪装(理论上可以解决)、WS、TLS 等,大概率没有这两项优化,XTLS Direct Mode 是特例。
对于 PROXY protocol,回落的是我手写的,无此问题。而入站的 acceptProxyProtocol 会套一层 Conn,但很可能本来就有 WS。

尚未测试 v2 的常规 ReadV 是否拒绝了 DS,且它现在只对桌面平台开放,详见 #373 (comment)

最后,做这种测试时,“选定数据流向”及“检查配置没错”很重要。

这确实是一段迷案.因为各种协议组合在开启各种姿势的统计时,表现非常不一致,历时良久终于破案.

补充一些测试数据和结论如下:

  • 在客户端开启各种姿势的统计.
  • 数据流向为服务端流向客户端
  • 客户端inbound为任意门
  • outbound为各种组合,摘录典型组合如下:
outbound 无统计 inbound&outbound=true only outbound=true only inbound=true inbound&outbound=false
VLESS over TCP 1604 385 387 682 1622
XTLS direct 1565 719 1488 673 1507
vmess over TCP(aes) 587 287 288 331 576
VLESS over TCP,TLS 284 281 260 250 288

一些结论为:

  • 在数据流向为服务器-->客户端的前提下
    • only outbound=true时,主要考虑ounbound失去readV,性能受到影响
    • only inbound=true时,主要考虑inbound失去writeV,性能受到影响
    • inbound&outbound=true时,考虑两项影响,取其瓶颈
  • 不同的协议本身性能不一样,因此readV和writeV的收益不一样,失去时性能影响也不一致
  • inbound都为任意门,因此only inbound=true,失去writeV时,性能折损比例较为接近,大约都是50%.
    • 尤其outbound为VLESS over TCP和XTLS direct时,因为他们本身性能也接近
  • XTLS direct 因为保持了readV,所以only outbound=true时不受影响
  • VLESS over TCP,TLS本身没有readV和writeV的加持,所以几乎不受影响
  • 在之前的v2ray测试中,XTLS direct有无readV的数据为(1511:390),也可印证VLESS over TCP开启only outbound=true即失去readV时的数据(1604:387).(前提两个协议本身性能接近).

尚未测试 v2 的常规 ReadV 是否拒绝了 DS,且它现在只对桌面平台开放,详见 [#373 (comment)]

且待缓缓...再行验证.....

依数据看来readV和writeV对性能影响很大,尤其readV,是很值得保持的优化.

顺便:之前有说VMess over TCP+ TLS 不如 VMess(AES) over TCP,也算破案了,也是因为readV/writeV

commented

是的,这项研究破了很多性能迷案,非常值得认真看完,有助于加深对很多方面的理解。

比如之前有不少测试表明 v2 的 TCP TLS AES 不如 TCP VMess AES,其实是因为后者有 ReadV & WriteV 加持,其中 ReadV 更重要。
(当然,即使后者只是多套了一层 HTTP 伪装或 WS,大概率也会失去这两项优化,性能大打折扣)

如果你在用非裸协议,则开对应出/入站的流量统计的影响很小,否则影响很大(如桌面平台开裸协议出/入站的流量统计)。

特别地,对于 VLESS XTLS Direct Mode 客户端,特殊实现了 ReadV 和流量统计数据收集,开 outbound 的流量统计几乎没有影响。
(这也是 v2rayNG 的默认做法,但 v2ray-core v4.32.1 只特殊实现了 ReadV,下个版本才有流量统计数据收集)

重点是,现在我们知道了这些原理,以及优化的方向,以后就能更好地针对性能。

而对于 v2ray 整体框架的性能问题,我和 @badO1a5A90 已经确定了另一处关键(此处不指 pipe)。

commented

备忘:有空时魔改出性能优化版 TLS。

commented

需要说明,即使有 ReadV 和 WriteV,仍未实现 zero-copy,即数据仍要拷贝一次,经过 v2ray 的内存,也产生了 CPU 切换的开销。

现在的 Go 语言中,如果直接 io.Copy() 两个 socket,实际上会调用到 splice 或 sendfile,从而无需经过用户态内存。

但对于 v2ray,出入站是分离且透明的,数据靠自己的 buffer 来传输,拿不到另一边的原始 socket,就暂时无法实现 zero-copy。

This issue is stale because it has been open 120 days with no activity. Remove stale label or comment or this will be closed in 5 days