如在
有一天,我醒来时收到一封不满的电子邮件,因为我们计划在不久的将来推出 Alvin,原因是长时间拖延。 具体来说,客户的 99% 延迟时间在 50 毫秒左右,远高于我们的延迟预算。 这是令人惊讶的,因为我对该服务进行了广泛的测试,特别是在延迟方面,这是一个常见的抱怨。
在将 Alvin 投入测试之前,我以每秒 40k 查询 (QPS) 的速度进行了大量实验,所有结果都显示延迟小于 10 毫秒。 我准备声明我不同意他们的结果。 但再看一遍这封信,我发现了一些新的东西:我没有准确测试他们提到的条件,他们的 QPS 比我低得多。 我测试的 QPS 为 40k,但他们只有 1k。 我又进行了一次实验,这次 QPS 较低,只是为了安抚他们。
由于我在博客中讨论了这一点,您可能已经发现他们的数字是正确的。 我一遍又一遍地测试我的虚拟客户端,结果都是一样:请求数量少不仅会增加延迟,而且会增加延迟超过 10 毫秒的请求数量。 换句话说,如果在 40k QPS 时每秒大约有 50 个请求超过 50 毫秒,那么在 1k QPS 时每秒有 100 个请求超过 50 毫秒。 悖论!
缩小搜索范围
当在具有许多组件的分布式系统中面临延迟问题时,第一步是创建一个简短的嫌疑人列表。 让我们更深入地研究一下 Alvin 的架构:
一个好的起点是已完成的 I/O 转换列表(网络调用/磁盘查找等)。 让我们尝试找出延迟在哪里。 除了与客户端进行明显的 I/O 之外,Alvin 还采取了额外的步骤:访问数据存储。 然而,该存储与 Alvin 在同一集群中运行,因此那里的延迟应该小于客户端的延迟。 那么,嫌疑人名单如下:
- 从客户端到 Alvin 的网络调用。
- 从 Alvin 到数据存储的网络调用。
- 在数据存储中的磁盘上搜索。
- 从数据仓库到 Alvin 的网络调用。
- Alvin 到客户端的网络调用。
让我们试着划掉一些要点。
数据存储与此无关
我做的第一件事是将 Alvin 转换为不处理请求的 ping-ping 服务器。 当它收到请求时,它返回一个空响应。 如果延迟减少,那么 Alvin 或数据仓库实现中的错误并不是闻所未闻的。 在第一个实验中,我们得到下图:
正如您所看到的,使用 ping-ping 服务器时没有任何改进。 这意味着数据仓库不会增加延迟,并且嫌疑人列表减少了一半:
- 从客户端到 Alvin 的网络调用。
- Alvin 到客户端的网络调用。
伟大的! 该名单正在迅速缩小。 我想我已经差不多明白原因了。
远程过程调用
现在是时候向您介绍一位新玩家了: gRPC
优化良好且广泛使用,这是我第一次在这种规模的系统上使用它,我预计我的实现至少可以说是次优的。
可用性 gRPC
在堆栈中产生了一个新问题:也许是我的实现或我自己 gRPC
造成延迟问题? 将新嫌疑人添加到名单中:
- 客户致电图书馆
gRPC
- 图书馆
gRPC
对客户端上的库进行网络调用gRPC
在服务器上 - 图书馆
gRPC
联系Alvin(乒乓服务器情况下无操作)
为了让您了解代码的样子,我的客户端/Alvin 实现与客户端-服务器实现没有太大区别
注意:上面的列表有点简化,因为
gRPC
可以使用您自己的(模板?)线程模型,其中执行堆栈是交织在一起的gRPC
和用户实施。 为了简单起见,我们将坚持使用这个模型。
分析将解决一切问题
划掉数据存储后,我以为我快完成了:“现在很简单! 让我们应用该配置文件并找出延迟发生的位置。” 我
我在客户端和服务器端获取了四个配置文件:具有高 QPS(低延迟)和具有低 QPS(高延迟)的乒乓服务器。 为了以防万一,我还获取了一个示例处理器配置文件。 在比较配置文件时,我通常会寻找异常的调用堆栈。 例如,在高延迟的坏方面,会有更多的上下文切换(10 次或更多)。 但就我而言,上下文切换的次数几乎相同。 令我恐惧的是,那里没有任何重要的东西。
额外的调试
我很绝望。 我不知道我还可以使用哪些其他工具,我的下一个计划本质上是用不同的变化重复实验,而不是清楚地诊断问题。
如果
从一开始,我就担心具体的 50ms 延迟。 这是一个非常重要的时刻。 我决定从代码中删除一些块,直到我能够准确地找出导致此错误的部分。 然后进行了一项有效的实验。
和往常一样,事后看来,一切都是显而易见的。 我将客户端放置在与 Alvin 相同的计算机上 - 并向 localhost
。 并且延迟的增加消失了!
网络出现问题。
学习网络工程师技能
我必须承认:我对网络技术的了解很糟糕,尤其是考虑到我每天都与它们一起工作。 但网络是主要嫌疑人,我需要学习如何调试它。
幸运的是,互联网喜爱那些想要学习的人。 ping 和tracert 的组合似乎是调试网络传输问题的一个足够好的开始。
首先,我推出了
然后我尝试了
因此,导致延迟的不是我的代码、gRPC 实现或网络。 我开始担心我永远无法理解这一点。
现在我们使用什么操作系统
gRPC
在 Linux 上广泛使用,但在 Windows 上却很奇特。 我决定尝试一个实验,结果很有效:我创建了一个 Linux 虚拟机,为 Linux 编译了 Alvin,然后部署了它。
发生的事情是这样的:尽管数据源没有什么不同,但 Linux 乒乓球服务器没有与类似的 Windows 主机相同的延迟。 事实证明,问题出在 Windows 的 gRPC 实现上。
内格尔算法
一直以来我都以为我丢了一面旗帜 gRPC
。 现在我明白它到底是什么了 gRPC
Windows 标志丢失。 我发现了一个内部 RPC 库,我相信它可以很好地适用于所有标志集
几乎 完成:我开始一次删除一个添加的标志,直到回归返回,以便我可以查明原因。 这是臭名昭著的
gRPC
该标志是在 TCP 套接字的 Linux 实现中设置的,但在 Windows 中没有设置。 我是这个
结论
低QPS下的较高延迟是由操作系统优化引起的。 回想起来,分析并没有检测到延迟,因为它是在内核模式下完成的,而不是在
至于 localhost 实验,它可能没有触及实际的网络代码,并且 Nagle 的算法没有运行,因此当客户端通过 localhost 到达 Alvin 时,延迟问题就消失了。
下次当您看到延迟随着每秒请求数的减少而增加时,Nagle 的算法应该出现在您的嫌疑人名单上!
来源: habr.com