You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

15 KiB

14 案例篇 | TCP端到端时延变大怎样判断是哪里出现了问题

你好,我是邵亚方。

如果你是一名互联网从业者那你对下面这个场景应该不会陌生客户端发送请求给服务端服务端将请求处理完后再把响应数据发送回客户端这就是典型的C/SClient/Server架构。对于这种请求-响应式的服务,我们不得不面对的问题是:

  • 如果客户端收到的响应时间变大了,那么这是客户端自身的问题呢,还是因为服务端处理得慢呢,又或者是因为网络有抖动呢?
  • 即使我们已经明确了是服务端或者客户端的问题,那么究竟是应用程序自身引起的问题呢,还是内核导致的问题呢?
  • 而且很多时候,这种问题往往是一天可能最多抖动一两次,我们很难去抓现场信息。

为了更好地处理这类折磨人的问题,我也摸索了一些手段来进行实时追踪,既不会给应用程序和系统带来明显的开销,又可以在出现这些故障时能够把信息给抓取出来,从而帮助我们快速定位出问题所在。

因此,这节课我来分享下我在这方面的一些实践,以及解决过的一些具体案例。

当然这些实践并不仅仅适用于这种C/S架构对于其他应用程序特别是对延迟比较敏感的应用程序同样具备参考意义。比如说

  • 如果我的业务运行在虚拟机里面,那怎么追踪呢?
  • 如果Client和Server之间还有一个Proxy那怎么判断是不是Proxy引起的问题呢

那么我们就先从生产环境中C/S架构的网络抖动案例说起。

如何分析C/S架构中的网络抖动问题

上图就是一个典型的C/S架构Client和Server之间可能经过了很复杂的网络但是对于服务器开发者或者运维人员而言这些中间网络可以理解为是一个黑盒很难去获取这些网络的详细信息更不用说到这些网络设备上去做debug了。所以我在这里把它们都简化为了一个Router路由器然后Client和Server通过这个路由器来相互通信。比如互联网场景中的数据库服务像MySQL、http服务等都是这种架构。而当时给我们提需求来诊断网络抖动问题的也是MySQL业务。因此接下来我们就以MySQL为例来进行具体讲解。

MySQL的业务方反馈说他们的请求偶尔会超时很长但不清楚是什么原因引起了超时对应到上图中就是D点收到响应的时刻和A点发出请求的时刻这个时间差会偶然性地有毛刺。在发生网络问题时使用tcpdump来抓包是常用的分析手段当你不清楚该如何来分析网络问题时那就使用tcpdump先把事故现场保存下来吧。

如果你使用过tcpdump来分析问题那你应该也吐槽过用tcpdump分析问题会很麻烦。如果你熟悉wireshark的话就会相对容易一些了。但是对于大部分人而言呢学习wirkshark的成本也是很高的而且wireshark的命令那么多把每一条命令都记清楚也是件很麻烦的事。

回到我们这个案例中在MySQL发生抖动的时候我们的业务人员也使用了tcpdump来抓包保存了现场但是他们并不清楚该如何分析这些tcpdump信息。在我帮他们分析这些tcpdump信息的时候也很难把tcpdump信息和业务抖动的时刻关联起来。这是因为虽然我们知道业务抖动发生的时刻比如说21:00:00.000这个时刻但是在这一时刻的附近可能会有非常多的TCP数据包很难简单地依赖时间戳把二者关联起来。而且更重要的原因是我们知道TCP是数据流上层业务的一个请求可能会被分为多个TCP包TCP Segment同样地多个请求也可能被合并为一个TCP包。也就是说TCP流是很难和应用数据关联起来的。这就是用tcpdump分析业务请求和响应的难点。

针对tcpdump难以分析应用协议的问题有一个思路是在tcpdump的时候把数据也保存下来然后使用tcpdump再进一步去解析这些应用协议。但是你会发现用这种方式来处理生产环境中的抖动问题是很不现实的因为在生产环境中的 TCP 连接动辄数百上千条我们往往并不清楚抖动发生在哪个TCP连接上。如果把这些TCP流都dump出来data部分即使只dump与应用协议有关的数据这对磁盘I/O也是个负担。那有什么好办法吗

好办法还是需要和应用协议关联起来不过我们可以把这些应用协议做一层抽象从而可以更简单地来解析它们甚至无需解析。对于MySQL而言呢工具tcprstat就是来做这件事的。

tcprstat的大致原理是利用MySQL的request-response特征来简化对协议内容的处理。request-response是指一个请求到达MySQL后MySQL处理完该请求然后回responseClient侧收到response后再去发下一个request然后MySQL收到下一个request并处理。也就是说这种模型是典型的串行方式处理完了一个再去处理下一个。所以tcprstat就可以以数据包到达MySQL Server侧作为起始时间点以MySQL将最后一个数据包发出去作为结束时间点然后这二者的时间差就是RTResponse Time这个过程大致如下图所示

tcprstat会记录request的到达时间点以及request发出去的时间点然后计算出RT并记录到日志中。当时我们把tcprstat部署到MySQL server侧后发现每一个RT值都很小并没有延迟很大的情况所以看起来服务端并没有问题。那么问题是否发生在Client这里呢

在我们想要把tcprstat也部署在Client侧抓取信息时发现它只支持在Server侧部署所以我们对tcprstat做了一些改造让它也可以部署在Client侧。

这种改造并不麻烦因为在Client侧解析的也是MySQL协议只是TCP流的方向跟Server侧相反Client侧是发请求收响应而Server侧是收请求发响应。

在改造完成后我们就开始部署tcprstat来抓取抖动现场了。在业务发生抖动时通过我们抓取到的信息显示Client在收到响应包的时候就已经发生延迟了也就是说问题同样也不是发生在Client侧。这就有些奇怪了既然Client和Server都没有问题难道是网络链路出现了问题

为了明确这一点我们就在业务低峰期使用ping包来检查网络是否存在问题。ping了大概数小时后我们发现ping响应时间忽然变得很大从不到1ms的时间增大到了几十甚至上百ms然后很快又恢复正常。

根据这个信息我们推断某个交换机可能存在拥塞于是就联系交换机管理人员来分析交换机。在交换机管理人员对这个链路上的交换机逐一排查后最终定位到一台接入交换机确实有问题它会偶然地出现排队很长的情况。而之所以MySQL反馈有抖动其他业务没有反馈只是因为这个接入交换机上的其他业务并不关心抖动。在交换机厂商帮忙修复了这个问题后就再也没有出现过这种偶发性的抖动了。

这个结果看似很简单,但是分析过程还是很复杂的。因为一开始我们并不清楚问题发生在哪里,只能一步步去排查,所以这个分析过程也花费了几天的时间。

交换机引起的网络抖动问题只是我们分析过的众多抖动案例之一。除了这类问题外我们还分析过很多抖动是由于Client侧存在问题或者Server侧存在问题。在分析了这么多抖动问题之后我们就开始思考能否针对这类问题来做一个自动化分析系统呢而且我们部署运行tcprstat后也发现它存在一些不足之处主要是它的性能开销略大特别是在TCP连接数较多的情况下它的CPU利用率甚至能够超过10%,这难以满足我们生产环境中长时间运行的需要。

tcprstat会有这么高的CPU开销原因其实与tcpdump是类似的它在旁路采集数据后会拷贝到用户空间来处理这个拷贝以及处理时间就比较消耗CPU。

为了满足生产环境的需求我们在tcprstat的基础上做了一个更加轻量级的分析系统。

如何轻量级地判断抖动发生在哪里?

我们的目标是在10Gb网卡的高并发场景下尽量地降低监控开销最好可以控制在1%以内而且不能给业务带来明显延迟。要想降低CPU开销很多工作就需要在内核里面来完成就跟现在很流行的eBPF这个追踪框架类似内核处理完所有的数据然后将结果返回给用户空间。

能够达到这个目标的方案大致有两种:一种是使用内核模块,另一种是使用轻量级的内核追踪框架。

使用内核模块的缺点是它的安装部署会很不方便特别是在线上内核版本非常多的情况下比如说我们的线上既有CentOS-6的操作系统也有CentOS-7的操作系统每个操作系统又各自有很多小版本以及我们自己发布的版本。统一线上的内核版本是件很麻烦的事这会涉及到很多变更不太现实。所以这种现状也就决定了使用内核模块的方式需要付出比较高的维护成本。而且内核模块的易用性也很差这会导致业务人员和运维人员排斥使用它从而增加它的推广难度。基于这些考虑我们最终选择了基于systemtap这个追踪框架来开发。没有选择eBPF的原因是它对内核版本要求较高而我们线上很多都是CentOS-7的内核。

基于systemtap实现的追踪框架大致如下图所示

它会追踪每一个TCP流TCP流对应到内核里的实现就是一个struct sock实例然后记录TCP流经过A/B/C/D这四个点的时刻依据这几个时间点我们就可以得到下面的结论

  • 如果C-B的时间差较大那就说明Server侧有抖动否则是Client或网络的问题
  • 如果D-A的时间差较小那就说明是Client侧问题否则是Server或者网络的问题。

这样在发生RT抖动时我们就能够区分出抖动是发生在ClientServer还是网络中了这会大大提升分析定位问题的效率。在定位到问题出在哪里后你就可以使用我们在“11讲”、“12讲”和“13讲”里讲到的知识点,再去进一步分析具体的原因是什么了。

在使用systemtap的过程中我们也踩了不少坑在这里也分享给你希望你可以避免

  • systemtap的加载过程是一个开销很大的过程主要是CPU的开销。因为systemtap的加载会编译systemtap脚本这会比较耗时。你可以提前将你的systemtap脚本编译为内核模块然后直接加载该模块来避免CPU开销
  • systemtap有很多开销控制选项你可以设置开销阈值来作为兜底方案以防止异常情况下它占用太多CPU
  • systemtap进程异常退出后可能不会卸载systemtap模块在你发现systemtap进程退出后你需要检查它是否也把对应的内核模块给卸载了。如果没有那你需要手动卸载一下以免产生不必要的问题。

C/S架构是互联网服务中比较典型的场景那针对其他场景我们该如何来分析问题呢接下来我们以虚拟机这种场景为例来看一下。

虚拟机场景下该如何判断抖动是发生在宿主机上还是虚拟机里?

随着云计算的发展越来越多的业务开始部署在云上很多企业或者使用自己定制的私有云或者使用公有云。我们也有很多业务部署在自己的私有云中既有基于KVM的虚拟机也有基于Kubernetes和Docker的容器。以虚拟机为例在Server侧发生抖动的时候业务人员还想进一步知道抖动是发生在Server侧的虚拟机内部还是发生在Server侧的宿主机上。要想实现这个需求我们只需要进一步扩展再增加新的hook点去记录TCP流经过虚拟机的时间点就好了如下图所示

这样我们就可以根据F和E的时间差来判断抖动是否发生在虚拟机内部。针对这个需求我们对tcprstat也做了类似的改造让它可以识别出抖动是否发生在虚拟机内部。这个改造也不复杂tcprstat默认只处理目标地址为本机的数据包不会处理转发包所以我们让它支持混杂模式然后就可以处理转发包了。当然虚拟机的具体网络配置是千差万别的你需要根据你的实际虚拟网络配置来做调整。

总之,希望你可以举一反三,根据你的实际业务场景来做合理的数据分析,而不要局限于我们这节课所列举的这几个具体场景。

课堂总结

我们这堂课以典型的C/S架构为例分析了RT发生抖动时该如何高效地识别出问题发生在哪里。我来总结一下这节课的重点

  • tcpdump是分析网络问题必须要掌握的工具但是用它分析问题并不容易。在你不清楚该如何分析网络问题时你可以先使用tcpdump把现场信息保存下来
  • TCP是数据流如何把TCP流和具体的业务请求/响应数据包关联起来,是分析具体应用问题的前提。你需要结合你的业务模型来做合理的关联;
  • RT抖动问题是很棘手的你需要结合你的业务模型来开发一些高效的问题分析工具。如果你使用的是Redhat或者CentOS那么你可以考虑使用systemtap如果是Ubuntu你可以考虑使用lttng。

课后作业

结合这堂课的第一张图在这张图中请问是否可以用TCP流到达B点的时刻到达Server这台主机的时间减去TCP流经过A点的时刻到达Client这台主机的时间来做为网络耗时为什么

结合我们在“13讲“里讲到的RTT这个往返时延你还可以进一步思考是否可以使用RTT来作为这次网络耗时为什么欢迎你在留言区与我讨论。

感谢你的阅读,如果你认为这节课的内容有收获,也欢迎把它分享给你的朋友,我们下一讲见。