此翻译是使用机器学习生成的,可能不是100%准确。 查看英文版本

NTCP 讨论

来自2007年3月关于NTCP与SSU传输协议的历史讨论

以下是2007年3月关于NTCP的讨论。该内容未更新以反映当前的实现。有关当前的NTCP规范,请参阅NTCP2页面

NTCP vs. SSU 讨论,2007年3月

NTCP 问题

(改编自 zzz 和 cervantes 之间的 IRC 讨论)

为什么 NTCP 比 SSU 更受青睐,NTCP 不是有更高的开销和延迟吗?它具有更好的可靠性。

通过NTCP传输流式库不会遇到经典的TCP-over-TCP问题吗?如果我们为流式库发起的流量提供一个真正简单的UDP传输会怎么样?我认为SSU本来就是所谓的真正简单的UDP传输 - 但事实证明它太不可靠了。

zzz 的"NTCP 被认为有害"分析

发布到新的 Syndie,2007-03-25。这篇文章是为了激发讨论而发布的,不要太当真。

摘要: NTCP 比 SSU 具有更高的延迟和开销,并且在与流媒体库一起使用时更容易崩溃。但是,流量路由会优先选择 NTCP 而不是 SSU,这目前是硬编码的。

讨论

我们目前有两种传输方式,NTCP 和 SSU。按照当前的实现,NTCP 的"出价"比 SSU 更低,因此它是首选的,除非对某个对等节点存在已建立的 SSU 连接但没有已建立的 NTCP 连接的情况。

SSU 与 NTCP 类似,都实现了确认、超时和重传机制。不过 SSU 是 I2P 代码,对超时有严格的约束,并提供往返时间、重传等方面的可用统计信息。NTCP 基于 Java NIO TCP,这是一个黑盒,大概实现了 RFC 标准,包括非常长的最大超时时间。

I2P 内的大部分流量都源自 streaming-lib(HTTP、IRC、Bittorrent),这是我们对 TCP 的实现。由于较低级别的传输通常是 NTCP(因为出价较低),系统会遇到众所周知且令人担忧的 TCP-over-TCP 问题 http://sites.inka.de/~W1011/devel/tcp-tcp.html ,即 TCP 的高层和低层同时进行重传,导致性能崩溃。

与上述链接中描述的 PPP over SSH 场景不同,我们在较低层有多个跳跃,每个跳跃都由 NTCP 链路覆盖。因此,每个 NTCP 延迟通常比上层流库延迟要小得多。这降低了崩溃的可能性。

此外,当底层TCP受到严格限制,具有较低的超时时间和重传次数时,相比于上层协议,连接崩溃的概率会降低。

0.28版本将流媒体库的最大超时时间从10秒增加到45秒,这大大改善了情况。SSU的最大超时时间是3秒。NTCP的最大超时时间据推测至少是60秒,这是RFC的建议值。没有办法更改NTCP参数或监控性能。NTCP层的崩溃是[编辑:文本丢失]。也许像tcpdump这样的外部工具会有所帮助。

然而,运行.28版本时,i2psnark报告的上游速度通常不会保持在高水平。它经常会下降到3-4 KBps,然后再爬升回来。这表明仍然存在崩溃现象。

SSU 也更高效。NTCP 有更高的开销,可能还有更高的往返时间。当使用 NTCP 时,(tunnel 输出)/(i2psnark 数据输出)的比率至少是 3.5 : 1。在运行一个修改代码以偏好 SSU 的实验中(配置选项 i2np.udp.alwaysPreferred 在当前代码中没有效果),比率降低到大约 3 : 1,表明效率更好。

根据流式传输库统计报告,情况得到了很大改善 - 生存期窗口大小从 6.3 增加到 7.5,RTT 从 11.5 秒降低到 10 秒,每次确认的发送次数从 1.11 降低到 1.07。

这种方法非常有效,这令人惊讶,因为我们只是改变了出站消息总共3到5跳中第一跳的传输方式。

由于正常变化,对出站 i2psnark 速度的影响并不明确。同时在实验中,入站 NTCP 被禁用了。对 i2psnark 入站速度的影响也不明确。

提案

  1. 1A) 这很简单 - 我们应该翻转竞标优先级,使得SSU在所有流量中都被优先选择,如果 我们能在不引起各种其他问题的情况下做到这一点。这将修复 i2np.udp.alwaysPreferred配置选项,使其能够正常工作(无论是true 还是false)。

  2. 1B) 1A) 的替代方案,不那么容易 - 如果我们能够在不对匿名性目标产生不利影响的情况下标记流量,我们 应该识别 streaming-lib 生成的流量,并让 SSU 为该流量生成低出价。 此标签必须随消息通过每一跳传递,以便转发 router 也遵循 SSU 偏好。

  3. 2) 进一步限制SSU(将最大重传次数从当前的10次减少)可能是明智的,以减少崩溃的可能性。

  4. 3) 我们需要进一步研究在流媒体库下使用半可靠协议的利弊。单跳重传是有益的和重大胜利,还是比无用更糟糕? 我们可以开发新的 SUU(安全不可靠 UDP),但可能不值得。如果我们完全不希望流媒体库流量进行任何重传,我们也许可以在 SSU 中添加一种无需确认的消息类型。严格限制的重传是否可取?

  5. 4) 0.28版本中的优先级发送代码仅适用于NTCP。到目前为止,我的测试还没有显示SSU优先级有多大用处,因为消息排队时间不够长,优先级无法发挥作用。但还需要更多测试。

  6. 5) 新的流媒体库45秒的最大超时时间可能仍然太低。 TCP RFC 规定是60秒。它可能不应该比底层 NTCP 最大超时时间(大概是60秒)更短。

jrandom 的回应

发布到新的 Syndie,2007-03-27

总的来说,我对此实验持开放态度,但请记住为什么首先要有NTCP——SSU在拥塞崩溃中失败了。NTCP"就是能工作",虽然在正常的单跳网络中可以处理2-10%的重传率,但在2跳tunnel中这会给我们带来40%的重传率。如果你加入一些我们在NTCP实施之前看到的SSU重传率测量值(10-30+%),那会给我们带来83%的重传率。也许那些重传率是由低至10秒的超时造成的,但增加那么多会咬我们一口(记住,乘以5你就得到了一半的旅程)。

与TCP不同,我们无法从tunnel获得反馈来了解消息是否成功传输 - 没有tunnel级别的确认。我们确实有端到端ACK,但只针对少量消息(每当我们分发新的session tag时) - 在我的router发送的1,553,591条客户端消息中,我们只尝试对其中的145,207条进行ACK。其他消息可能已经悄无声息地失败,也可能完全成功了。

我不太相信TCP-over-TCP论证对我们适用,特别是考虑到我们数据传输所经过的各种不同路径。当然,在I2P上进行的测量可能会改变我的看法。

NTCP 最大超时时间据推测至少为 60 秒,这是 RFC 的推荐值。没有办法更改 NTCP 参数或监控性能。

确实如此,但网络连接只有在真正出现严重问题时才会达到那种水平 - TCP的重传超时通常是几十或几百毫秒的量级。正如foofighter指出的,他们的TCP协议栈有20多年的经验和bug修复,加上价值数十亿美元的行业在优化硬件和软件,以便根据他们的实际需求表现良好。

NTCP 具有更高的开销,可能还有更高的往返时间。当使用 NTCP 时,(tunnel 输出)/(i2psnark 数据输出)的比例至少为 3.5 : 1。运行一个实验,修改代码以优先使用 SSU(配置选项 i2np.udp.alwaysPreferred 在当前代码中没有效果),比例降低到大约 3 : 1,表明效率更好。

这是非常有趣的数据,不过更多是关于router拥塞而非带宽效率的问题 - 你需要比较 3.5*$n*$NTCPRetransmissionPct ./. 3.0*$n*$SSURetransmissionPct。这个数据点表明router中存在某些因素导致正在传输的消息出现过多的本地队列积压。

生存期窗口大小从 6.3 增加到 7.5,RTT 从 11.5 秒降低到 10 秒,每个 ACK 的发送次数从 1.11 降低到 1.07。

请记住,每个ACK的发送数只是一个样本,而不是完整计数(因为我们不会尝试对每次发送都进行ACK)。这也不是随机样本,而是更重地采样非活动期间或活动突发的启动阶段——持续负载不需要很多ACK。

该范围内的窗口大小仍然过低,无法获得AIMD的真正好处,而且仍然太低无法传输单个32KB的BT数据块(将下限提高到10或12可以解决这个问题)。

不过,wsize 统计看起来很有希望 - 这个数据维持了多长时间?

实际上,为了测试目的,你可能需要查看 StreamSinkClient/StreamSinkServer 甚至是 apps/ministreaming/java/src/net/i2p/client/streaming/ 中的 TestSwarm - StreamSinkClient 是一个 CLI 应用程序,它将选定的文件发送到选定的目的地,而 StreamSinkServer 创建一个目的地并写出发送给它的任何数据(显示大小和传输时间)。TestSwarm 结合了两者 - 向其连接的任何对象发送随机数据洪流。这应该为你提供测量流媒体库持续吞吐量容量的工具,而不是 BT 限制/发送。

1A) 这很简单 - > 我们应该翻转竞价优先级,使得 SSU 对所有流量都是首选,如果我们能在不造成各种其他问题的情况下做到这一点。这将修复 i2np.udp.alwaysPreferred 配置选项,使其正常工作(无论设置为 true 还是 false)。

无论如何,遵循 i2np.udp.alwaysPreferred 都是一个好主意 - 请随时提交该更改。不过在切换偏好设置之前,让我们先收集更多数据,因为添加 NTCP 是为了解决由 SSU 造成的拥塞崩溃问题。

1B) 1A的替代方案,不太容易 - > 如果我们可以在不对匿名性目标产生不利影响的情况下标记流量,我们 > 应该识别streaming-lib生成的流量 > 并让SSU为该流量生成低出价。这个标记将必须随着 > 消息通过每一跳传递 > 以便转发router也遵循SSU偏好。

在实际应用中,存在三种类型的流量——tunnel构建/测试、netDb查询/响应和流媒体库流量。网络的设计使得区分这三种流量变得非常困难。

2) 进一步限制SSU(将当前超过10次的最大重传次数减少)可能是明智的,以降低崩溃的可能性。

到了10次重传,我们已经陷入困境了,我同意。从传输层角度来说,一次或者两次重传是合理的,但如果对方太拥塞而无法及时发送ACK(即使实现了SACK/NACK功能),我们也无能为力。

在我看来,要真正解决核心问题,我们需要解决为什么router会变得如此拥堵而无法及时ACK(根据我的发现,这是由于CPU竞争导致的)。也许我们可以调整router处理中的一些事项,使已存在tunnel的传输比解密新tunnel请求具有更高的CPU优先级?不过我们必须小心避免饥饿现象。

3) 我们需要进一步研究在流媒体库下使用半可靠协议的利弊。单跳重传是否有益且是一个重大胜利,还是它们比无用更糟?我们可以创建一个新的SUU(安全不可靠UDP),但可能不值得。如果我们完全不希望流媒体库流量有任何重传,我们也许可以在SSU中添加一个无需ACK的消息类型。严格限制的重传是否可取?

值得研究一下 - 如果我们直接禁用 SSU 的重传会怎么样?这可能会导致更高的流媒体库重发率,但也许不会。

4) .28版本中的优先级发送代码仅适用于NTCP。到目前为止,我的测试还没有显示SSU优先级有太大用处,因为消息排队时间不够长,优先级无法发挥作用。但还需要更多测试。

有 UDPTransport.PRIORITY_LIMITS 和 UDPTransport.PRIORITY_WEIGHT(由 TimedWeightedPriorityMessageQueue 执行),但目前权重几乎都相等,所以没有效果。当然可以调整这个(但正如你提到的,如果没有排队,这就无关紧要了)。

5) 新的流媒体库最大超时时间45秒可能仍然太短。TCP RFC规定是60秒。它可能不应该短于底层NTCP最大超时时间(大概是60秒)。

不过那45秒是流库的最大重传超时时间,而不是流超时时间。TCP在实践中的重传超时时间要小几个数量级,尽管确实在通过裸线或卫星传输的链路上可能达到60秒 ;) 如果我们将流库重传超时时间增加到比如75秒,在网页加载之前我们都能去喝杯啤酒了(特别是假设传输可靠性低于98%)。这就是我们偏好NTCP的原因之一。

zzz的回复

发布于新 Syndie,2007-03-31

在10次重传的情况下,我们已经陷入困境了,我同意这一点。从传输层的角度来看,一次或者两次重传是合理的,但如果对方太拥塞以至于无法及时发送ACK(即使有已实现的SACK/NACK能力),我们也无能为力。

在我看来,要真正解决核心问题,我们需要解决router为什么会如此拥塞以至于无法及时ACK的问题(据我发现,这是由于CPU争用造成的)。也许我们可以在router的处理过程中调整一些优先级,让已有tunnel的传输比解密新tunnel请求具有更高的CPU优先级?不过我们必须小心避免饥饿问题。

我主要的统计收集技术之一是启用 net.i2p.client.streaming.ConnectionPacketHandler=DEBUG,然后观察 RTT 时间和窗口大小的变化。概括来说,通常可以看到 3 种类型的连接:约 4 秒 RTT、约 10 秒 RTT 和约 30 秒 RTT。目标是减少 30 秒 RTT 的连接。如果 CPU 争用是原因,那么也许一些调整可以解决它。

将 SSU 最大重传次数从 10 降低实际上只是在黑暗中摸索,因为我们没有足够的数据来判断是否出现了崩溃、TCP-over-TCP 问题或其他情况,所以需要更多数据。

值得研究一下 - 如果我们直接禁用SSU的重传会怎么样?这可能会导致流媒体库的重发率大幅提高,但也许不会。

我不理解的是,如果你能详细说明的话,SSU重传对于非streaming-lib流量有什么好处。我们需要隧道消息(例如)使用半可靠传输吗,还是它们可以使用不可靠或某种程度上可靠的传输(例如最多1或2次重传)?换句话说,为什么要半可靠性?

(但是正如你提到的,如果没有排队,这就无关紧要了)。

我为UDP实现了优先级发送,但它的触发频率比NTCP2端的代码少了大约10万倍。也许这是进一步调查的线索或提示 - 我不理解为什么NTCP2会更频繁地出现积压,但这也许能提示NTCP2性能较差的原因。

jrandom 回答的问题

发布到新 Syndie,2007-03-31

在实现 NTCP 之前我们观察到的 SSU 重传率 > (10-30+%) > > router 本身能够测量这个吗?如果可以,能否基于测量的性能来选择传输协议?(即如果到某个对等节点的 SSU 连接丢失了过多的消息,在向该对等节点发送时优先使用 NTCP)

是的,它目前使用该统计数据作为一种简单的MTU检测方法(如果重传率很高,就使用小包大小,但如果重传率很低,就使用大包大小)。我们在首次引入NTCP(以及首次从原始TCP传输方式转移时)尝试了一些方法,这些方法会优先选择SSU但容易让该传输方式对某个节点失败,导致它回退到NTCP。然而,在这方面肯定还有更多可以做的,尽管情况会很快变得复杂(如何/何时调整/重置出价,是否在多个节点间共享这些偏好,是否在与同一节点的多个会话间共享偏好(以及持续多长时间)等等)。

foofighter 的回复

发布到新的 Syndie,2007-03-26

如果我理解正确的话,支持TCP(总的来说,包括新旧版本)的主要原因是你不需要担心编写一个好的TCP协议栈。这并不是不可能做对的…只是现有的TCP协议栈已经有了20年的领先优势。

据我所知,关于TCP与UDP偏好选择背后并没有太多深层理论,除了以下考虑因素:

  • 仅支持TCP的网络非常依赖于可达节点(那些能够通过其NAT转发传入连接的节点)
  • 即使可达节点稀少,让它们具备高容量在某种程度上也能缓解拓扑稀缺问题
  • UDP允许"NAT打洞",这让人们能够"某种程度上伪可达"(在introducer的帮助下),否则他们只能建立出站连接
  • “旧的"TCP传输实现需要大量线程,这是性能杀手,而"新的"TCP传输在少量线程下表现良好
  • A类router在UDP饱和时崩溃。B类router在TCP饱和时崩溃。
  • “感觉上”(指有一些迹象但没有科学数据或质量统计)A类比B类部署更广泛
  • 某些网络传输非DNS UDP数据报的质量极其糟糕,但对TCP流的处理还算凑合

在这种背景下,少量多样化的传输协议(按需而定,但不要过多)在任何情况下都是明智的。哪个应该作为主要传输协议,取决于它们的性能表现。当我尝试用UDP充分利用线路容量时,我在线路上看到了一些糟糕的情况。数据包丢失率达到了35%的水平。

我们当然可以尝试调整UDP与TCP的优先级,但我建议在这方面要谨慎。我强烈建议不要一次性做出过于激进的改变,否则可能会破坏系统功能。

zzz 的回复(给 foofighter)

发布到新 Syndie,2007-03-27

据我所知,在选择TCP还是UDP方面,除了以下考虑因素外,并没有太多深入的理论依据:

这些都是有效的问题。但是你是在孤立地考虑这两种协议,而不是考虑什么传输协议最适合特定的高层协议(即是否使用流式库)。

我的意思是你必须考虑到流式传输库。

因此要么为所有人调整偏好设置,要么对流媒体库流量进行区别对待。

这就是我的提案1B)所讨论的内容 - 对streaming-lib流量和非streaming-lib流量(例如tunnel构建消息)采用不同的偏好设置。

在这种背景下,少量多样化的传输协议(根据需要而定,但不要过多)在任何情况下都显得明智。哪种应该成为主要传输协议,取决于它们的性能表现。当我尝试用UDP充分利用我的线路容量时,我在线路上看到了一些糟糕的情况。数据包丢失率达到了35%的水平。

同意。新的.28版本可能改善了UDP数据包丢失的情况,也可能没有。

一个重要的要点是 - 传输代码确实会记住传输的失败情况。所以如果 UDP 是首选传输方式,它会首先尝试 UDP,但如果对某个特定目标失败了,下次尝试连接该目标时就会尝试 NTCP 而不是再次尝试 UDP。

我们绝对可以尝试调整UDP与TCP的优先级,但我强烈建议谨慎行事。我建议不要一次性做出太激进的改变,否则可能会破坏某些功能。

我们有四个调节旋钮 - 四个出价值(SSU 和 NTCP,分别对应已连接和未连接的情况)。例如,我们可以设置为只有在两种传输方式都已连接时才优先选择 SSU 而非 NTCP,但如果两种传输方式都未连接,则优先尝试 NTCP。

另一种逐步实施的方法是仅转移流媒体库流量(1B提案),但这可能很困难且可能涉及匿名性问题,我不确定。或者也许只对第一个出站跳转的流量进行转移(即不将标志传播到下一个router),这只能给你部分好处,但可能更匿名且更容易实现。

讨论结果

… 以及同一时间段(2007年)的其他相关变更:

  • 在 0.6.1.28 版本中实现了对 streaming lib 参数的重大调优,大大提高了出站性能
  • 在 0.6.1.28 版本中实现了 NTCP 的优先级发送
  • zzz 实现了 SSU 的优先级发送,但从未提交
  • 在 0.6.1.29 版本中实现了高级传输竞价控制 i2np.udp.preferred
  • NTCP 的推回机制在 0.6.1.30 版本中实现,在 0.6.1.31 版本中因匿名性担忧而禁用,在 0.6.1.32 版本中经过改进以解决这些担忧后重新启用
  • zzz 的提案 1-5 都尚未实现

Was this page helpful?