v2ray 的流量统计功能会使裸协议的 ReadV 和 WriteV 同时失效的说明
RPRX opened this issue · comments
这个问题是研究 ReadV 时发现的,感谢 @badO1a5A90 的多次测试。
裸协议指 over TCP,如任意门、Socks、VLESS、VMess(可带加密)等,且不带 HTTP 伪装等额外的东西。
--
是否用 ReadV,取决于断言
Lines 61 to 71 in 2952492
是否用 WriteV(WriteTo 内有断言)
v2ray-core/common/buf/writer.go
Line 50 in 2952492
--
对于刚刚定义的“裸协议”,这里的断言会成功,就可以用到 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
是的,这项研究破了很多性能迷案,非常值得认真看完,有助于加深对很多方面的理解。
比如之前有不少测试表明 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)。
备忘:有空时魔改出性能优化版 TLS。
需要说明,即使有 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