性能比拼: TCP vs UDP(重大改进)

本内容是对知名性能评测博主 Anton Putra TCP vs UDP Performance: HUGE Improvement! 内容的翻译与整理, 有适当删减, 相关指标和结论以原作为准

在本期视频中,我们将再次对比 TCP 和 UDP 协议,重点关注延迟(latency)和吞吐量(throughput)。在我进行另一个基准测试时,我发现了几个可以改进之前测试的方法,因此我决定与大家分享这些内容。


TCP 协议是如何工作的?

首先,让我们了解一下 TCP 协议的工作方式。

客户端和服务器之间需要建立连接,这通过 三次握手 来完成。

  1. 首先,发送方发送一个 "同步"(SYN)数据包给接收方,表示客户端希望发起连接,并提供一个随机的序列号,这个序列号将在后续数据传输中使用。
  2. 然后,接收方回应一个 "同步-确认"(SYN-ACK)数据包,表示"收到!这是我的序列号,并且我确认你的序列号。"
  3. 最后,发送方回复一个 "确认"(ACK)数据包,序列号加 1,并确认:"很好,我们同步了,可以通信了。"

我们可以使用 Wireshark 工具进行观察。为了简化,我们可以监听本地回环地址(localhost),并设置抓包过滤器,只捕捉端口为 8080 的数据包(无论是源端口还是目标端口)。

此刻我们还没有任何数据包,所以我将先启动服务器,然后发送一个 JSON 负载。

现在我们可以看到 TCP 的三次握手过程了:包括 SYN、SYN-ACK 和最终的 ACK 数据包,表明一个 TCP 连接已经建立。

我运行的服务器监听的是 8080 端口,但当你创建客户端时,它会随机选择一个端口来创建 TCP 套接字,并连接到服务器。

你还可以看到服务器向客户端发送了一个 JSON 消息,而客户端在接收到该消息后,会返回一个仅包含头部的空数据包,表示它已经收到了消息。


测试

为了进行测试,我使用了 AWS 平台。在这次测试中,我为参与测试的每个应用程序分别使用了一台 m7a.medium 实例

我使用 C++ 和原始套接字(raw sockets) 实现了 TCP 和 UDP 消息的发送与接收。

相关的源代码已经发布在我的公共 GitHub 仓库中

我将从与上一次测试完全相同的设置开始,整个测试分为两个阶段:

  • 第一阶段 ,我们只发送 16,000 条消息。你会发现,UDP 的延迟更低---这在意料之中,因为 UDP 不需要建立连接,也不需要确认接收。
  • 第二阶段,我们尝试使用 TCP 和 UDP 各自发送尽可能多的数据。你会注意到,由于 TCP 拥有更好的拥塞控制机制,它能实现更高的吞吐量。

最终测试结果非常值得注意:

  • TCP 的吞吐量可达约 620,000 条消息/秒
  • UDP 的吞吐量则只有 310,000 条消息/秒

不过,在最大负载下,你也会发现延迟差异变得更明显。UDP 在需要低延迟的场景下表现更好,而 TCP 在你更关注吞吐量和可靠性时表现更优秀。


第一个改进

我原以为使用 CMake 构建项目时,默认会生成一个经过优化的、可用于生产环境的二进制文件。

但我错了。

所以在下一轮测试中,我将使用 release 模式重新编译相同的源代码。

测试仍然分为两个阶段:

  • 第一阶段基本与之前相同,可能会看到 稍微降低的 CPU 使用率,但没有显著变化。
  • 第二阶段中,TCP 的吞吐量提升到了 1,000,000 条消息/秒,令人惊讶的是,UDP 的吞吐量反而没有增加,不过延迟有所降低。

因此,即使是简单地将应用程序编译为 release 模式,也能同时提升两个协议的表现。

这是一个愚蠢的错误,我没有任何借口。不过它确实展示了:通过正确编译应用程序可以获得更高的性能。


第二个改进

最后一个改进其实涉及到 监控(monitoring)

我知道给应用程序添加指标会降低性能,但我以前从未实际测量过影响到底有多大。

所以,我使用 Prometheus 的直方图(histogram) 来测量客户端发送和接收的 UDP 与 TCP 消息的延迟。

很多人在生产环境中也会这么做,但如果你测量的是类似原始 TCP 或 UDP 数据包这种层级的内容,那么这些测量行为本身就会影响性能。

因此,在下一次测试中,我继续使用 Prometheus 的计数器(counter) 来计算接收到的消息速率,但我关闭了 histogram。这样你在最终测试中将看不到延迟的统计。这是第一阶段。

在第二阶段,我们可以看到:

  • TCP 达到了 1,100,000 次请求/秒 ,比上一个测试提升了 10%
  • UDP 也有相同程度的提升,现在约为 420,000 条消息/秒

这个测试说明,仅仅是为高性能应用程序添加监控指标,就可能导致性能下降 10% 或更多 。所以一定要非常小心 --- 每一次函数调用和性能测量,都会降低程序的运行效率。

这在例如 交易系统 这样的场景中尤其重要,因为每一微秒都至关重要。


在下一期视频中,我将对比 WebSockets 与 HTTP 或 REST API 的表现。但在此之前,你可以先查看我关于 数据库、缓存以及不同编程语言 的其他基准测试。


相关推荐
在下木子生12 分钟前
SpringBoot条件装配注解
java·spring boot·后端
huan9934 分钟前
Obsidian 插件篇 - 插件汇总简介
后端
周Echo周37 分钟前
5、vim编辑和shell编程【超详细】
java·linux·c++·后端·编辑器·vim
AronTing42 分钟前
03-深入解析 Spring AOP 原理及源码
后端
逻辑重构鬼才44 分钟前
AES+RSA实现前后端加密通信:全方位安全解决方案
后端
卤蛋七号1 小时前
JavaSE高级(一)
后端
Java中文社群1 小时前
SpringAI用嵌入模型操作向量数据库!
后端·aigc·openai
暴力袋鼠哥1 小时前
基于Flask的跨境电商头程预警分析系统
后端·python·flask
一只爱撸猫的程序猿2 小时前
防止外部API服务不可用拖垮系统的解决方案
spring boot·后端·程序员
白露与泡影2 小时前
SpringBoot 最大连接数及最大并发数是多少?
spring boot·后端·firefox