你好,我是庄振运。
你一定有过在网页或者手机上下载照片的体验,如果数据传输太慢,那你的体验一定十分糟糕。你看,互联网实体之间的数据快速传输对用户体验至关重要。这里涉及到的其实就是网络传输问题。所以,今天我们就通过生产实践中的案例,来探讨一下互联网服务中的数据传输性能。
说到底,网络传输问题其实就分两种:
“数据没有传递”虽然看起来更严重,但是相对“数据传送缓慢”来说,更容易判断和解决。所以,这一讲,我们就重点解决第二种问题。我们一起来看看,为什么网络传送速度会慢,在众多原因中怎么快速诊断出关键问题来,又该如何去解决。
造成网络传输缓慢的原因很多,我们这一讲,就是帮助你快速诊断问题出在哪里:是客户端,是服务器端,还是网络本身?在此基础上,你才能专门针对具体的领域继续分析。
我们先看一下,都有哪些可能的原因会导致数据传输缓慢呢?在宏观上,这种问题的可能原因可以分为三种场景:
也就是说,可能是由于数据发送方过载,而没有向接收方发送数据;也可能是网络通道很慢;又或者是数据接收方的服务器太忙,从而无法从网络缓冲区读取数据。
为了描述方便,我们根据平时客户浏览网页的场景,假设客户端是数据接收方,而服务器端是数据发送方。
进行此类分析诊断时,负责的工程师通常需要快速隔离出上述不同场景,以便他们可以专注于特定场景里面的可疑组件,并对本质原因进行更深入的分析。但这一快速诊断过程会遇到很多难点。
首先,数据传输涉及多个网络实体,包括两台机器(也就是发送者和接收者)和网络路由,这与仅涉及一台机器的常见性能问题形成鲜明对比。
其次,这种诊断涉及多层信息,包括应用程序层和网络传输层。为了找出原因,工程师必须检查各种数据,包括客户端日志、服务器日志、网络统计信息、CPU使用情况等。这些检查需要花费很多时间和精力,并且通常需要性能工程师的经验和专业知识。
更加让人郁闷的是,这些日志往往分散在不同的地方,比如客户端和服务器。为了节省时间和精力,性能工程师迫切需要更智能的工具,以帮助他们快速找出根本原因。
所以今天,我专注于解决这样的一个问题,就是:快速确定应归咎的组件范围和场景(无论是发送方、接收方还是网络本身)。我提出了一种当发生数据传输缓慢的问题时,可以自动隔离原因的解决方案。毕竟,你只有找出了要对“数据传输慢”负责任的那一部分,才可以进行后续分析工作,最终确定真正的问题。
要想快速诊断,我们需要先看看三种问题场景的不同特征。
这个解决方案本质上依靠的是客户端和服务器端的TCP层面的特征。TCP是传输层协议之一,可提供有序且可靠的流字节传输,是当今使用最广泛的传输协议。TCP具有流控制功能,可避免接收方过载。接收方设置专用的接收缓冲区,发送方设置相应的发送缓冲区。数据发送方(服务器)的发送缓冲区和数据接收方(客户端)的接收缓冲区,都可以通过操作系统来监测当前队列大小。
为了能够识别瓶颈,你需要在发送方和接收方的传输层上,收集有关队列大小的信息。有很多收集此类信息的方法。你有两种工具可以使用,分别是Netstat和ss。
Netstat是一个命令行工具,可以显示网络连接和网络协议统计信息。我们主要是用它来观察TCP / IP套接字的发送队列和接收队列的大小。而ss命令,可以显示套接字统计信息,包括显示TCP以及其他类型套接字的统计信息。类似于Netstat,ss还可以显示发送和接收队列大小。
除了相应的工具的介绍,为了帮助你理解,我们还需要先重温一下传输数据时候,应用层和TCP层的交互。
上图显示了任何基于TCP的数据传输中的典型流程。关于系统调用和网络传输的五个步骤如下:
接下来,我们就来看看三种不同场景下的问题特征是什么样的。
我们先看第一个场景,客户端接收数据缓慢的情况。为了重现这一场景,我们做一个实验,让发送端发送一段固定大小的数据给接收方。我们强制接收方,也就是客户端,减慢数据的接收速度。具体做法,就是在应用程序代码的read()调用之前,注入了一定的延迟,这种场景代表了客户端数据接收成为瓶颈的情况。
上图显示的是数据发送方的发送缓冲区,SendQ(Send Queue)的大小变化。开始时候,数据发送调用send(),立刻注满SendQ。随着数据的传输,慢慢变为0。
第二张图是客户端的接收缓冲区,RecvQ(Receive Queue)的大小变化,客户端因为应用程序运行缓慢,所以RecvQ具有一定的积累,这可以由非零值来看出。这些非零值持续了一段时间,随着应用程序不断地读取,最终RecvQ减为0。
对于第二种场景,也就是数据发送方是瓶颈的情况,我们强制发送方(即服务器端),放慢数据的发送速度。具体来说,我们在应用程序代码中,对write()的调用之前注入了一定的延迟,模拟了发送者是瓶颈的情况。
上图显示了服务器端的SendQ的值,你可以看到,SendQ几乎全部是零。这是因为发送端是瓶颈,其他地方不是瓶颈,所以任何SendQ的数据会被很快发送出去。
你可以在图片中看到一个持续时间很短的峰值,这是因为SendQ取样的时候恰好取到数据还没有被传输到网络中的时候。但因为这个峰值持续时间很短,简单的过滤就可以去掉。
接收端的RecvQ显示在下图,你可以看到,因为接收端不是瓶颈,RecvQ是零。
第三个场景,是网络本身是瓶颈造成的数据传输缓慢。我们通过向网络路径注入延迟来创造这一场景,以使TCP仅能以非常低的吞吐量进行传输。
图片中显示了发送端的SendQ值,你可以看到它的值不为零,因为那些数据不能很快地被传送出去。
再来看接收端的RecvQ,如下图。RecvQ全为零,这些零值就代表了快速的数据传递。
通过上面三种场景的分析,尤其是对发送端SendQ和接收端RecvQ的观察,我们不难总结出规律来。正常的数据传输情况下,客户端的接收队列和服务器端的发送队列都应该是零。
反之,如果数据传输缓慢,则有如下几种情况:
为了帮助你加深记忆,我用表格来做了个归纳。
你可以通过这个表格,快速判断问题出现的位置。
根据前面的分析和总结,我们现在提出解决方案。这是一个基于状态转移的方案,需要从客户端和服务器端收集几个关键点的信息。
为了帮助你理解,我们需要先来看看数据请求和传输流程图。就用常见的HTTP协议的Request和Response方式来描述,如下图所示。
当客户端需要下载服务器的数据时,首先在T0发出数据请求;网络将请求发送到服务器后,服务器在T1收到数据。
然后,服务器开始准备数据,数据准备好后,服务器将开始在T2时发回数据。通过一系列write()调用。发送在T4完成。网络传输后,客户端在T3开始接收数据,并在T5完成接收。请注意,尽管其他时间戳是按照严格的顺序,T3和T4的顺序可能会因实际情况而异。具体来说,对于小数据传输时,T4可以先于T3,因为单个write()调用就足够了。对于大数据传输,通常使用T3在T4之前。
接下来,我们来看看基于状态机的解决方案,它是一个针对HTTP数据传输问题的,完整而具体的解决方案。
从上面的过程中,我们可以看到,如果服务器无法接收到数据请求,则数据传递将不会发生,因此不会完成。
我用下图来表示整个状态机。这个状态机展示了整个HTTP数据传输的过程,包括Request和Response。如果数据传递成功,状态机最后会到达状态F。
如上图,数据传递的初始状态为State-S。客户端发出请求后,它将移至状态A;当网络通道完成其工作,并将请求传递到服务器OS时,状态变为B。当服务器准备好数据,并开始发出数据的第一个字节时,状态变为C。
当客户端收到第一个字节后,状态变为D;最后当服务器发出最后一个字节时,状态变为E。或者这两个次序交换,成功进行数据传递的最终状态是State-F。
在整个过程中,如果发生其他的转移,那么就是网络传输有问题了。我们就可以根据发送端的发送队列和接收端的接收队列长度的变化,轻松判断是谁的问题,比如是客户端,服务器或是网路。
今天我们讲述了,互联网服务在传输数据时,如果发生传输速度太慢的问题,怎样才能快速地诊断到底是客户端、服务器端,还是网络的问题。
唐代诗人高适的《燕歌行》有几句诗:“山川萧条极边土,胡骑凭陵杂风雨。战士军前半死生,美人帐下犹歌舞。”说的是前方的战士,在前线出生入死;后方却有人逍遥自在的观赏美人歌舞,醉生梦死。这种冰火两重天的讽刺,部分原因,就是责任没有分清,以至于滥竽充数者可以逍遥自在。
对待数据传输缓慢问题,我们也很希望能快速地搞清责任,分清是哪里的问题,然后才能有针对性地继续分析。我们的解决方案就是根据TCP的Send和Receive队列大小变化,来快速诊断的方案。它能智能而快速地分清问题的大致范围:就是数据发送方、数据接收方,还是网络。
根据你平时的观察,公司业务有没有发生数据传输太慢的问题?如果发生了,你们一般怎么根因呢?如果采用本讲的思路,会不会更加快速地诊断?
欢迎你在留言区分享自己的思考,与我和其他同学一起讨论,也欢迎你把文章分享给自己的朋友。
评论