C++ 当通过TCP发送消息的速率增加时,为什么请求-响应消息对的延迟会减少? 简介

C++ 当通过TCP发送消息的速率增加时,为什么请求-响应消息对的延迟会减少? 简介,c++,networking,tcp,network-programming,low-latency,C++,Networking,Tcp,Network Programming,Low Latency,我有一个客户端和服务器通过TCP连接进行通信的设置,我经历了奇怪的延迟行为,我无法理解 上下文 客户端向服务器发送请求消息,服务器向客户端发送响应消息。 我将延迟定义为从发送请求消息到接收响应消息的时间。我可以以不同的速率发送请求消息(限制请求的频率),但是我在任何时候都最多有一条未完成的请求消息。即,没有并发/重叠的请求-响应消息对 我以三种方式实现了请求和响应消息的发送:第一种是使用我自己的序列化方法直接在TCP套接字上发送,第二种是使用gRPC通过使用HTTP2的RPC进行通信,第三种是使

我有一个客户端和服务器通过TCP连接进行通信的设置,我经历了奇怪的延迟行为,我无法理解

上下文 客户端向服务器发送请求消息,服务器向客户端发送响应消息。 我将延迟定义为从发送请求消息到接收响应消息的时间。我可以以不同的速率发送请求消息(限制请求的频率),但是我在任何时候都最多有一条未完成的请求消息。即,没有并发/重叠的请求-响应消息对

我以三种方式实现了请求和响应消息的发送:第一种是使用我自己的序列化方法直接在TCP套接字上发送,第二种是使用gRPC通过使用HTTP2的RPC进行通信,第三种是使用Apache Thrift(一种类似于gRPC的RPC框架)。 gRPC依次在4种不同的客户机/服务器类型中实现,对于Thrift,我有3种不同的客户机/服务器类型

在所有解决方案中,当增加请求消息的发送速率时,我都会经历延迟的减少(在gRPC和Thrift中,请求-响应对通过RPC方法进行通信)。 如果根本不限制请求速率,而是在收到响应后立即发送新请求,则可以观察到最佳延迟。 使用std::chrono::staid_clock原语测量延迟。 我不知道是什么原因造成的。在开始真正的测试之前,我确保通过发送10k请求消息来预热TCP连接(通过TCP慢速启动阶段)

我如何实现限制和测量延迟(在客户端ofc上):

双倍费率;
性病:死亡率;
自动间隔=标准::计时::微秒(1000000)/速率;
//这里有预热阶段,但不包括在本代码中。
自动总时间=标准时间:微秒(0);
自动iter_时间=开始时间;
int i=0;
对于(i=0;i<10000;i++){//发送10k请求。
iter_time=std::chrono::staid_clock::now();
请求类型请求(“abcdefghijklmnopqrstuvxyz”);
响应类型响应;
自动启动=标准::时钟::稳定时钟::现在();
sendRequest(request);//根据gRPC/Thrift/“TCP”的不同,这些选项看起来有所不同
接管人回应(和回应);
自动结束=标准::时钟::稳定时钟::现在();
auto dur=std::chrono::duration\U cast(结束-开始);
总纬度+=dur;
std::this_thread::sleep_until(iter_time+interval);//限制发送。。
}
//平均延迟:总延迟/i
我使用docker compose在单独的docker容器中运行客户机/服务器,并在kubernetes集群中运行它们。在这两种情况下,我都经历了相同的行为。我在想也许我的节流/时间测量代码正在做一些我不知道/不理解的事情

TCP套接字在所有情况下都设置为TCP_NODELAY。 服务器是单线程/多线程非阻塞/阻塞的,有各种不同的变体,客户端是一些同步的,一些异步的等等。因此有很多变体,但是它们的行为都是相同的


有什么想法可以解释这种行为的原因吗?

现在我认为延迟问题不在网络堆栈中,而是您生成和接收消息的速率

您的测试代码似乎没有任何实时保证,这也需要在容器中设置。这意味着您的“for loop”并非每次都以相同的速度运行。OS调度器可以停止它以运行其他进程(这就是进程共享CPU的方式)。这种行为在集装箱化机制中会变得更加复杂


虽然TCP中存在可能导致延迟变化的机制(如@DNT所述),但我认为您不会看到它们。特别是如果服务器和客户端是本地的。这就是为什么在查看TCP堆栈之前,我会先排除消息生成和接收的速率

我的2美分:从您的描述来看,似乎您达到了一个拥塞窗口大小,其中没有发生拥塞事件(尚未发生),或者通过重复快速重传进行恢复很快,因为您等待每个响应,或者由于窗口大小限制因素而达到平衡。这也会以类似的方式影响远程端的发送方。发送速度越慢,每个响应的远程拥塞窗口大小线性增长越慢,因为您已经过了缓慢的\u开始积极增长的阶段。@DNT感谢您的输入!根据您首先写的内容,关于拥塞窗口的大小;你的意思是,当发送频率较低的请求时,与发送频率较高的请求相比,我的拥塞窗口会变慢,因此较慢的请求受ACK的影响更大?Rasmus Johanson:如果不进行实际测量并检查所有本地环境因素,很难说。拥塞窗口在慢速启动后线性增长,当发生拥塞或丢失事件时,该速率停止,并通过堆栈内的算法(如快速重传)进行纠正。随着窗口的增长,可以发送更多的数据,直到达到大小限制或发生不良事件为止,在这种情况下,我们将指数减少到初始大小,这取决于实现默认值和参数。Liam Kelly所指出的也可能是您所描述的情况的一个因素B:出于某种原因,因此不想在您的姓名前添加at标志:)@DNT感谢您的时间和投入:)我参加了一些数据通信和计算机网络课程,但实际经验有限:)谢谢您的投入。速率如何影响
start()
end()
之间的调度,而(1){start();send();receive();end();sleep_to_keep_rate()}
如果我以较慢的速率运行,操作系统是否会降低线程的优先级,这意味着我大部分时间都在睡眠。在这种情况下,繁忙的等待循环是否会更好地使高速率och低速率案例成为可能