史上最全的CDN内容分发网络实战技巧(网络优化)

今天来给你们分享下关于 CDN 的东西,以及我本身的一些发现、一些我的的拙见。总共分为 3 个部分:原理、详解、各类坑。前端

 

 

首先说一下 CDN 的基本原理部分,主要分 4 块来描述:CDN 的由来、调度是怎么作的、缓存是什么、关于安全。node

 

 

最初刚有互联网的时候,带宽用量很少、用户少,并不存在什么问题,后来随着发展,逐渐出现了使用量大、访问缓慢的状况。最初 95 年的时候,有两个博士试图经过利用数学的办法来解决动态路由问题,且效果还不错,这即是 Akamai 的前身,也是全球第一个CDN 公司。98 年中国成立了国内第一家 CDN 公司,蓝汛,ChinaCache,很荣幸我曾在这个公司任职,群里也有好多前蓝汛的同事们,也有不少现还在蓝汛的同事。linux

 

 

什么是 CDN?c++

 

这是一个作过 CDN 以后的拓扑图,里面有几个概念须要明确一下:算法

Origin Server: 源站,也就是作 CDN 以前的客户真正的服务器;数据库

User: 访问者,也就是要访问网站的网民;缓存

Edge Server: CDN 的服务器,不单只“边缘服务器”,这个以后细说;安全

s/\(单\)只/\1指/;服务器

Last Mile: 最后一千米,也就是网民到他所访问到的 CDN 服务器之间的路径。cookie

 

咱们平时所使用的DNS服务器,通常称之为LDNS,在解析一个域名的时候,通常有两个状况,一种是域名在DNS上有记录,另外一种状况是没有记录,两种状况的处理流程不同。

 

当你访问163这个域名时,若是LDNS上有缓存记录,那它会直接将IP地址直接给你。若是没有缓存记录,它将会一步步向后面的服务器作请求,而后将全部数据进行汇总交给最终的客户。

 

当你访问163这个地址时,实际上若是自己没有内容的话,它要去后面拿数据,这个过程术语叫递归,它首先会向全球13个根域服务器请求,问com域名在哪,而后根域服务器做出回答,一步步往下,这个过程较复杂,若是你们感兴趣可去查相关资料,在这就不一一赘述。

 

 

DNS调度

 

确定不少人好奇是如何进行调度和进行定位的?

其实也是经过LDNS的具体地址来进行的,好比,看图,假设你是一个广东电信客户,那你所使用的DNS服务器去作递归的时会访问到某一个CDN厂商的GRB,全球的一个调度系统,他就能看到来自于哪一个LDNS。假设若是用户和LDNS使用同一个区域的服务器,他就会间接认为用户也是广东电信的。

 

再举个例子,好比说北京联通的用户,它使用DNS地址,通常自动给它分配的是北京联通的服务器,这个服务器去作递归的时候,调度服务器就会看到这个请求是来自北京联通的LDNS服务器,就会给它分配一个北京联通的服务器地址,而后让来自北京联通的用户直接访问北京联通的服务器地址,这样来实现精准的区域性调度。

 

从这个调度理论上看,咱们能够发现一个问题,就是假设用户所使用的LDNS地址和你是同一个区域,那么这个时候咱们的调度才有多是正确的。可是举个例子来讲,若是你是北京联通的用户,但是使用的是广东电信的LDNS的话,就会让GRB系统误觉得你是广东电信的客户,这样就会错误的调度过去。

 

以前有一次我在小区里上网,因为个人路由器有问题,我设了202.106.0.20的北京联通的DNS服务器地址,后来出差去深圳,访问比较大的网站发现比较慢,通过分析,才发现原来我设的DNS地址是北京联通的,而我在广东和深圳使用的网络都是电信接入的,可是分配给个人是北京联通的地址,那我用电信的线路访问北京联通的地址,势必就会很慢。

 

 

由于刚才讲到的DNS调度机制存在必定问题,因此在某些场合下咱们会使用第二种调度机制,叫HTTP的调度。

 

了解http协议的人知道,在http协议中有一个叫302跳转的功能,它的实现并非说你访问一个URL,而后立刻吐给你想要的数据,而是吐给你一个302返回信令,这个信令头部会告诉你,有一个location目标,这个location就是告诉你下一步将要怎么作,而具体调度是经过location来实现的。

 

即使我所使用的DNS和我不在一个区域,但当我访问http server的时,这个server是由CDN公司提供的。客户访问server的时,虽然说经过DNS方式没法拿到客户的真正IP地址,可是若是你访问的是http server,他必定能直接看到客户的真实IP,利用这种方法能够进行调度的纠偏,能够直接返回给你一个302,而后location里面携带一个真正离你最近的CDN server。

 

这种调度方式,优点是准确,可是也存在弊端,它须要有一次TCP的三次握手建连,他不像DNS那样直接请求一个数据包过去给一个反馈就OK了,他须要一次TCP的三次握手建连。

 

第二个是你如何访问到http的服务器?若是你以前是经过DNS调度过去的,实际上前边的那个DNS也是省不了,在国内是没有办法作anycast的,也就是没有办法来直接访问一个众所周知的大的IP来进行,因此,通常状况下都是经过DNS来进行第一次调度,而后用http来进行第二次纠偏。这种状况下你们能够想象,若是你下载一个大文件,好比说电影,但你访问的是一个页面小元素,好比说这个图片只有几k,那么,实际上你调度的时间就已占用了很大的成分。实际上,这种302调度是一种磨刀不误砍柴工的方案,若是你后面有不少工做要作,好比要下载一个电影时间会很长,那你调度准确,即便花一点时间调度也是值得的。可是若是你后续访问一下就完了,那么你这样调度就没有太大意义。

 

 

除了DNS调度和http的302调度之外,其实还有一种调度方式,叫http DNS调度,它的原理是经过一个正常的http请求,发一个get的请求,而后再请求里面以参数的形式携带一个我要解析的域名,而后服务器那边去经过数据库查询,查询以后又经过http的正常响应,把这个你要请求的IP经过http协议给你,这种协议有一个特色就是必须双端都支持,由于这种模式是非标准的。没有任何一个RFC文档说,你的客户端或者你的操做系统天生就支持这种机制。这有点相似是一种API的这种方式,那若是要实现的话就必须双端都支持。

 

通常,第三种调度的应用场景是在手机的APP端,在APP软件里面,你要访问某些东西颇有可能被运营商劫持等问题,这个劫持问题后面还有很大的篇幅去讲。那为了不这种劫持,可能会用到这种http DNS的调度方式。既然APP的程序都是你本身写的,因此说实现这么简单一个API的借口是很容易的。

 

 

CDN的接入

 

可能会有人问,你讲了这么多DNS和具体CDN的调度有什么关系呢?

 

由于在讲你得到一个具体的DNS域名地址的时,他给你的就是一个IP地址。那在没有CDN以前,他给你的IP地址就是在原来没作CDN时的原始服务器地址。但若是你作过CDN的话,你会发现最终拿到的这个IP地址是CDN的节点,而并非真正的原始服务器。

 

咱们一般说的拿到一个IP地址,这其实是DNS的A记录。DNS里面有不少不一样的记录,好比像A记录负责给你一个IP地址;好比像CNAME记录给你的是一个域名的别名。固然还有不少其余记录,好比TXT的记录、MX记录等等。这个跟CDN无关,这里就不细说了,有兴趣去查一下DNS相关的文档。

 

上图就是一个很明显的CDN介入后的效果图。linux里有一个命令叫dig,它可直接把要访问域名的具体的解析状况列出来。那么,经过这个图可看出,当你要访问www.163.com时,他最终虽给出的是一个IP地址,但实际上,它通过了两次CNAME记录。第一次CNAEM记录就是咱们以前说得CDN的GRB,他拿到了这个数据,就能够间接知道你的这个LOCODNS是从哪里来的,而后间接给你进行一个定位。以这个图为例,他实际上第一跳是跳到网速地址,第二跳是分配了网速的一个平台,这个平台又分开其余的IP给最终的客户。

 

 

Cache 系统—缓存系统

 

除DNS调度之外,在CDN里还有一个很是大的重头戏就是Cache系统,也就是缓存系统。它用于把那些能够缓存住的东西,缓存到CDN的边缘节点,这样当第二我的去访问同一节点,同一具体电影或MP3时就不用再通过CDN链路回到真正的源站去拿数据,而是由边缘节点直接给数据。

 

在Cache系统里囊括了不少的技术,好比,用空间换时间的这种高效的数据结构和算法,多级缓存以热度来区分,前端是SSD后面是机械硬盘等等。不少的细节就不说了,如感兴趣的可以后交流。

 

 

对于Cache系统来讲,有两种不一样的工做状态。第一种工做状态就是所谓的命中(hit),第二种就是没有命中(miss)。若是命中了,直接经过检索找到磁盘或内存上的数据,把这个数据直接吐给客户,而不是从后面去拿数据。这样的话就起到一个很完美的加速效果。

 

 

第二种是在miss时,其实,miss的时候跟hit惟一的区别就是,当我发现个人本机上没有这个资源,我会去个人upstream(上游)去拿数据。拿完这个数据,除了第一时间给客户,同时还会在硬盘上缓存一份。若是这个硬盘空间满了,会经过一系列置换方法,把最老的数据、最冷的数据替换出去。

 

提到了upstream,不是原始服务器,缘由是由于当客户访问到CDN节点的时,他发现上面没有数据,并非直接从原始服务器上去拿,而是通过他的另外一个CDN节点,而后经过middlemell的方式去进行一些数据传输。而后upstream这一层,从原始服务器拿数据,经过一系列的加速手段,快速的把数据投递给咱们的边缘节点,再把这个数据给最终客户。在过程中upstream和downstream这两层都会把数据缓存一份。经过这种树形结构,好比说多个边缘节点,而后汇总到一个或者几个副层结点,这样的话能够逐渐的实现流量的收敛。

 

 

提到Cache的具体技术,我相信这里的不少朋友都是同行业的,有人会说其实这没有什么难的,你只要有网络、有运维人员就能够了。其实我并不这样认为,由于你若是想把它作好的话其实很难,好比,我列出的不少技术你有没有在考虑?

 

举几个例子来讲,你有没有作网卡的的多队列和CPU的亲和性绑定?你有没有作磁盘的调度算法改进?另外,你存储的时候仍是用仍是?等等都是有讲究的。包括内核的调优包括架构和CPU的绑定,CPU的多级缓存的使用,而后你的处理你使用,仍是用标准的的这种机制。再好比说编译的程序时使用的去编译仍是用英特尔的,而后你再作不少的调用。好比说一个很简单的字符串拷贝,那你是用,你仍是用汇编去写,你仍是用什么方式等等不少细节。

 

关于高性能这一块,还有不少的研究,如你们感兴趣的话,能够以后跟我进行进一步的沟通。我想表达的一个观点就是说,看上去作CDN很简单,入门确实也简单,可是要真正想作好很难。

 

 

安全问题

 

在没有作CDN以前你的网站颇有可能会遭受到各类各样的攻击。那么攻击通常分红两种,第一种叫蛮力型攻击,量大的让你的带宽没法抗住最后致使拒绝服务,另一种是技巧性攻击。

 

做为CDN来说,就已经将你的原始服务器的IP进行了隐藏。这样当一个攻击者去访问你的域名的时,实际上访问的并非你真正的服务器。当他访问的是CDN的节点,就没有办法把CDN的节点打倒,换句话说,即便有能力把CDN的好比10g的节点或者是40g的大节点所有打倒,但因为CDN自然的分布式的部署方式,他也很难在同一时间以内迅速的把全国全部CDN的边缘节点全都打瘫。

 

另外,还有一种攻击是针对你的DNS地址的。若是你的GRB瘫了的话,会致使整个调度系统失灵。若是调动系统失灵,即便你的CDN的Cache server仍是可以正常接受请求,但因为流量调度不了。所以,你须要在DNS层作不少防御机制,好比说用高性能的DNS或用分布式的部署方式等等。

 

 

技巧型攻击不须要很大的流量,就能够把你的原针打倒或是让你的网页出现错误的状况。好比说,像注入、挂马甚至说更严重的会直接拖走你的数据库等等。那么做为CDN来讲,有不少厂商实际上已经开始具有这样的技巧性的防御能力了,好比说WAF(Web Application Fierwall),就是应用层防火墙,他能够直接去解析你的请求内容,分析内容是否有恶意性,若有恶意性的话去进行过滤,报警等一系列措施来保证你的原始服务器的安全。

 

详解篇

 

第二部分主要是针对网络层的优化、架构的优化、Cache的选型还有性能分析等等几个方面,对整个CDN的基础原理做很深刻地剖析。

 

 

原始的CDN实际上是Content Delivery Network这三个词的缩写,也就是内容分发网络。但我认为应该是can do something on Network。CDN的理念是加速,因此,咱们就尽一切可能去作各类优化,从一层到七层的优化来实现最终的优化效果。

 

为何说一层是优化,实际上也是硬件,你的服务器选型就是一种优化。你是用ssd,仍是用saker硬盘,你是该用pce卡,仍是应该用ssd。你的CPU应该用至强仍是应该用阿童木的等等,都是须要去斟酌。

 

至于二层,链路层的优化指的就是资源方面。好比机房如何去选择。

 

三层路由层是指你在middlemell这块真正选路的具体的细节,后面会有一个图来具体讲一下。

 

四层是指传输层的优化,咱们通常的业务全都是TCP,因此说这里面就能够明确的说这里是指TCP的优化。还有一个就是七层也是能够优化的。好比说你强行对内容进行压缩,甚至你改变压缩级别去压缩。

 

 

做为CDN来讲,基本上我罗列了一下可能会用到的一些技术,大概10个。好比说就近分布、策略性的缓存、传输的优化、链路层的优化、包括内容的预取、合并回源。而后持久链接池、主动压缩,还有当你原始服务器挂了的话你怎么样可以保证让客户看到数据等不少的细节。

 

 

路径的优化,实际上,咱们能够把它抽象成是一个求最短路径最优解的思路去解决真实的问题。当你从a点到b点须要传输数据的时,每每会通过一个c点,比直接从a到b更快。在互联网里有个三角原理,和地理位置的原理有必定区别的。虽然说有必定的相关性,但仍是有区别的,有可能从a通过c到b会比a直接到b更快。

 

在数据传输的时,须要去考虑不少综合因素,目前为止,包括阿克麦也很难作到彻底系统自动化去作链路选择和切换。在调度的时,不少公司都有专门的团队管流量调度的。不少的系统可能只起到支撑和参考的做用,而真正须要决策的仍是人。由于你须要考虑的元素太多了,好比说要考虑你的带宽成本、带宽节点冗余量、服务器承载能力,要考虑你的客户敏感度哪些该切哪些不应切等不少细节。

 

 

传输层的优化刚才讲到了是TCP优化,在现今的互联网里,TCP优化是能够带来最直接客户体验感的一种实现方式。若是想讲TCP优化究竟是怎么回事,咱们就得先从头讲一下TCP具体的原理是怎样的。

 

上图,我画了四个不一样的红圈,cwnd,英文是,就是拥塞控制窗口,用途是控制发送端的发送速度。ss是slow start的缩写,也就是慢启动,这是任何一个TCP协议在最开始的时候必经的一个阶段。

 

后两个词较有意思,ssthresh,是slow start threshold的缩写,也就是说慢启动阈值。它是出如今你传输到必定速度的时,认为应慢点去传的时,原来的指数传输方式,增加速度方式变成现行的速度增加,来尽可能的规避和避免网络的拥塞。

 

那整个拥塞避免阶段其实就是图中右下角的CA,这是拥塞避免这个线性的过程,不是指数。指数的那个叫慢启动。

 

当TCP开始传输数据的时,最开始的时候并非以一个很快的速度发出。你看到的wget或是下载某一个东西的速度猛的就很是快,实际上它是一个由微观慢慢把速度加起来的过程,只是这个时间很短你可能并无察觉。但实际上从微观上来看最开始是有一个所谓的初始发包数量的这么一个概念。在早期的2.6.18内核,也就是3645相对应的这个版本以前,初始的发包数量是两个。

 

它发出第一轮数据,实际上只发两个数据包。等待这两个数据包的彻底确认,如这两个数据包彻底收到ACK确认数据以后,会认为第一轮数据你都已经收到了,这时它会把发包的数量调整成4个。若是第二个也收到了,就调成8个16个32个这样的一个增加过程,就是slow start的过程。那这个过程实际上就是一个最开始在TCP刚开始创建的时候的一个最初始的过程。

 

那这个过程何时会结束?其实就是在丢包的时候。若是没有丢包,我没有理由降速或者调整发送速度。当他遇到丢包的时候他就会把这个值记录下来,而且经过他的拥塞控制算法,算出一个合理的阈值。那么当下一次速度增加到这个阈值的时候,就会知道不能再指数增加了,而是应该线性的增加发包的数量,来避免再次丢包。

 

还有个概念就是RTO,其实是在服务器发数据而客户端始终没有响应的时,它会等待一个超时定时器。这个超时定时器一旦出现,就会回到另外一个协议栈里的一个状态机。当这个状态机处于丢包状态时,它就会把它的CWND降到最开始这么大,那么他的速度就会骤降,这是个很是严重的问题。且一旦骤降它会从新评估。有可能,你以前,好比说你的窗口长到24,可是你丢包了,而后他给你算出你应该到16就变成线性。若是你再出现严重问题,它可能会把阈值降到12,并启用线性传输模式了。

经过介绍这个原理,你们可能会看到,其实TCP是一个很聪明的作法。它在能尽可能传的时候拼命的提升速度,而后在丢包的时候就尽可能下降速度,尽可能的规避拥堵。

 

现现在的网络产生了很大不一样,由于,好比说WiFi的接入、3G、4G的移动信号的接入,甚至南电信北联通的一些资源的不充沛,更甚至是恶意的一些限速,会致使你丢包的缘由,有时并非真正的拥塞。而是你链路里面命中注定会有这么多的数据会丢掉。

 

你们想象一下,假若有一个恒定的丢包几率的网络。当我发一百个包的时候,丢掉百分之二十,只收到了八十个,这时若是去降速,就意味着降得速度越低,发送的数据量就越小,那么对端收到的就更少。由于丢包几率是恒定的,若是遇到这种状况的话,早期的TCP的拥塞控制算法就已经不能知足现有的这种环境的需求了,所以咱们要考虑如何去优化。

 

 

这是用tcpdump把数据包抓下来后用Verashape软件打开而且进行图形化分析的一个微观展现图。

 

图里能够看到另一个细节,就是能看到有不少不一样的这种发包的周期。这个其实就是我刚才讲的每次发两个、四个、八个、十六个这样的不一样的发送的时刻。但这里有个问题,他发送时会一古脑儿地把数据发出去。虽然说在宏观上来说,你单位时间以内只能发这么多,可是从微观上来说,实际上你这么一次发送就至关因而burst,这种大的冲击有可能会致使中间网络链路不充沛,而后会形成丢包。

 

在早期研究GPRS网络或者是25G的网络的时候,尤为会遇到这种状况。他的特征是RTT很长,同时你的带宽很小。那你们能够想象一下,若是你的带宽很小,每一次突发这么大,从微观角度来说,这个数据就已经有可能会形成微观上的丢包了。

 

 

另一种优化的方法就是的平滑发包,充分的利用每个发包周期之间的时间间隔,而后把数据包打散。这样的话,既没有让对方从宏观上感受发送速度慢,从微观上我也让这个数据变得更平滑,而不会致使某一个具体的小时间的一个时刻,因为链路不充足而致使丢包。

 

 

除了刚才说的之外,还有不少优化的方法。好比说建连优化,当你去发信包三次握手的时,默认状况下,对方若是未反馈,你会以1为一个贝司值而后以2的主数递增这样去重试。好比,一秒钟重试一次,两秒钟一次,四秒钟一次,不少次以后,它会自动的放弃。那若是按照6.18内核,以3为一个贝司值,以3的主数递增,3、6、十二这样。因此,这个环节就可能会致使很严重的问题,那对服务器来讲你能够作到什么?好比说你再发完这个CS第二次握手以后,若是他一段时间没响应,可快速给他再重发一遍。

 

这种数据包的优化实际上并不会占用什么网络,并且有可能会勾引出第三次握手,快速的解决因为你的服务器在出项上致使第二次握手丢包。

 

另外,还有不少的客户可能较关心具体的细节,好比,你的首包时间是多少?首包时间是当你发完http的get请求以后,你所拿到的第一个数据。那这第一个数据每每是你的响应头。这个响应头有多是和你的内容一块儿发送过来的,也有多是先发送一个响应头而后再发内容,这取决于你本身的server的时限。在TCP里面有一个Nagel算法,Nagel算法会把这个数据拼凑成一个大块儿后发出。若是你要是在engikers里配TCP nodelay,把这个配完后就能够。有什么发什么能够去提高这个首包的效果。

 

平滑发包刚才也讲过了,丢包预判就是你经过统计学的一些方法,把端到端的,好比,c到你的传输具体状况作一个记录。而后,若是你要是发现丢包率是比较符合规律的话,能够在没有丢包的时候你预判有可能丢包,那你就时不时的去把某些数据包重发一遍,发两遍,即便他能收到。这样的话也是能够达到加速的抗丢包效果的。

 

后面还有不少细节,这里就再也不赘述。右边是一个linux下的TCP协议栈状态机的切换跃迁图。这个图里面的open状态指的是在没有任何丢包的正常状态,Recovery状态是开始有重传。Disorder这个状态是看到有数据包乱序。CWR是一种TCP头部携带的显性的拥塞控制,这个通常不多用到.可是苹果操做系统确实支持的。左边那个Loss状态就是以前我一直讲的一旦遇到RTO之后,就会骤降这种状况。因此,在TPC优化的时还需考虑你怎么样去优化你的协议的状态跃迁,让他尽可能不跑到Loss这个状态。

不少作TCP优化的,只是改变TCP拥塞控制算法,直接编译出一个内核模块从新加载,而后改一下的拥塞控制模块,再从新加载一下就OK了。实际上,这种作法只能改变你计算CWND的数量,但并不能改变他的状态。若是你想作TCP优化,你必需要动TCP协议栈自己。

 

 

在linux协议栈里面有一个控制参数叫tcp slow start after idle。意思是,你在数据传输的时,若是等了一段时间超出必定的时间阈值以后他会把CWND给你降到初始值。

 

那么,这种状况是有问题的,假如你是一个http的业务,并且都是这种小文件。刚好你又用了keep life这种状态,每个请求结束后并不立刻断掉连接,而是期待下一次的数据请求。在第一个数据块object,好比下载第一个图片,他去请求时,这个CWND会逐渐经过慢系统长到必定的高度。因为你两次get请求之间可能间隔了一段时间,这个时间一旦超过阈值,就会在发送端本身把这个CWND降到初始值。一旦降到初始值,这个时候你第一个object在下载之后,CWND好不容易涨上去的就白长了。你在下载第二个的时候实际上仍是从慢速开始。

 

在linux系统里默认这个值开启,它其实是会给你降的。若是你想作优化的话,最简单的作法就是把它置成0。若是置成0,即便链接中间隔的时间很长,你在请求第二个object的时,他的初始的发送速度就继续按照刚才的大小继续发送。这样,就能够达到一个很好的加速效果。

 

 

经过第一部分的讲解,你们也知道CDN有一个很是重要的就是缓存系统,用来作数据的缓存。当你下载一个电影或mp3的时,数据会留在你的缓存系统里面。若是有第二我的还访问同一个文件的时,还刚好经过你去访问,那你就能够直接把这个内容给客户,而无需把这个内容递交到真正的原始服务器,而后再从原始服务器去拿。

 

但在真正的业务场景里,除了可缓存的内容之外,还有一种是彻底不可缓存的。好比说你的登录、再好比说你浏览论坛页面的时候有一些动态的一些元素,由于每一个人看到的东西是不同的,不一样的URL、不一样的cookie可能看到的东西玩是彻底不一样,那这个时候这个数据就没有办法缓存。有人会说这种状况下是否是CDN就没有价值了,若是是纯动态页面的话,其实不是的。经过架构优化我能够给你展现一下,经过CDN你能怎么样去加速,能加到一个什么样的速度?

 

这是个在没有作CDN以前,客户访问源站的时候的链接示意图。这里面提到一个概念RTT,以前也说到了它的真正术语是往返时延,实际上就是咱们平时说的ping值。可是ping值只是RTT的一部分,并非说RTT就是ping值。实际上,TCP也有往返时延的,好比,你发起一个信包,而后对方回复,这段时间间隔也是RTT。有一种ping叫TCPping,利用的就是这个特色。如图,假设客户到原站的RTT是80毫秒,假设你要下载的文件是30kb,算一下他大概须要多久时间能够下载完成。

 

图里共3种颜色,红色表明TCP的三次握手是一个建连的过程。第一次,第二次第三次而后建连完成。你们仔细看绿色的,绿色是发get请求的时候的一个数据包。蓝色的大量的数据传输表示服务器开始以不一样周期开始吐数据。那么这里边我是假设他初始CWND是2,那就是二、四、8这样去长。

 

在TCP里边还有一个MSS的概念,TCP协议能一个数据包存储的最大真正的七层内容是有多长。在普通的网络当中,MTU若是是1500字节的话,IP头是20字节,TCP头是20字节,在通常状况下MSS是1460,也就是说一个数据包能够传递1460字节的内容。那咱们算一下,30kb是30*1024这么多字节,那么它须要几轮才能传输完成呢?

 

第一轮发两个数据包,第二轮发四个数据包,第三轮发八个数据包,第四轮的时候其实剩余的数据量已经不足16个数据包了,可是仍然要发送一轮。

后面是四个来回我刚才讲了,再加上前面的TCP三次握手也占一个往返时延。也就是说一共有五个来回周期。那假设这个往返时延是80毫秒,能够算一下,5*80,这是400毫秒。也就是在这么一个网络的状况下,带宽足够充足,而后在没有抖动也没有丢包的状况下,要传一个30kb的数据在80毫秒延迟的状况下,他最快的理论速度也需400毫秒才能完成,这是在CDN以前。

 

 

上图是在作CDN以后的效果图。咱们能够看到,在客户和园站之间,部署两个CDN的节点,一个叫下层一个叫上层。下层离用户很近,这一起的距离就lastmell。上层离源站近,这一起这个距离咱们firstmell。而后上下层之间咱们叫middlemell。为确保可以充分的体现即便是动态的数据咱们也能起到很完美的加速效果,我在上下层之间我仍保留80毫秒。同时在Lastmell和firstmell分别引入20毫秒,那么总共延时就变成了120毫秒。也就是说如今网络环境总延时是原来的1.5倍。

 

首先来看一下firstmell,由于firstmell和加速以前的拓普相比,惟一不一样的是往返时延变小,由80毫秒变成了20毫秒。计算一下20*(1+4)=100毫秒。

 

再来看lastmell,因为lastmell发送端是咱们本身的服务器,因此彻底能够利用TCP优化这种方式来让数据包发送更快。好比,最简单的加速方式就是去修改你的CWND,原来是2如今修改到10,目前在2.6.32内核以上,他默认的这个CWND应该都是10,早期是2。谷歌在很早以前就已提出一个观点,早期ARFC的标准里说初始值是2,,已不合时宜,由于如今的网络带宽都很大。因此咱们应该把它提到10,后来就全部的东西都是以10为初始值。

 

假设初始值就是10,能够算一下须要多少个周期能把数据发完?共有30K的数据,第一轮发10个,每个数据包记住这里边说的10个指的是10个数据包。每一个数据包可存放的内容是1460个字节,那实际上第一轮发10个就已经变成14.6k。那第二轮20个数据包用不满就已经发完了,套用公式20*3,实际上只须要60毫秒就可完成。

 

最有意思的是middlemell这块。由于全部的东西全都是咱们本身的,服务器也是咱们本身的,而后网络也是相对来讲可控的。因此能够在middlemell这块儿作不少有意思的事情。好比TCP,因为服务器是在机房,他跟原针不同,原针有可能带宽很小,而在lastmell也不可能吐数据太快,若是太快你的最终客户端的网络又个瓶颈。但咱们两个节点之间传输的时其实是能够速度很快的,也能够直接把数据一次性的30个包传到下层,从上层传到下层。

 

除这个之外还有一个很重要的观点,我能够经过上下层的一种长链接的机制keeplive的一个机制,忽略TCP的3次握手。即便换不一样用户去访问同一个下层,但因我上下层之间已经创建了一个所谓的通道,那么也可让这个数据经过我这个通道直接把get请求发送到上层,上层把这个交给原针。这样减小一个往返。套用公式能够看一下80*(0+1),总共只须要80毫秒。

 

把三个部分一块儿加起来,能够算一下60+80+100=240。也就是说,这种环境下,在总的延时是原来1.5倍的状况下,完美的作到比原来提高40%的优化效果。在不能缓存的纯动态的状况下,我在中间的middlemell没有任何RTT减小的状况下,个人CDN架构给你带来的价值。

 

还有两个细节我没有说,第一个细节是,真正的咱们找上下层的链路的时有可能会小于80毫秒。由于利用咱们以前说的那个路由的最短路径的算法,有可能会找一个通过c点到达上层,总共的RTT可能会小于80毫秒或更小,实际上还能进一步的缩短期。另外,这里讲的是上层拿到全部的数据以后才会给下层,下层拿到全部数据以后才会给用户,实际上他不会在全部数据收到以后才传输,他是随收随传的,因此这三个过程在时间的横轴上是有叠加的,就致使时间进一步缩短。

 

 

以前我有讲,在CDN里你玩的是什么?你玩的实际上就是网络,尤为是对CDN公司来讲。坦白来说,服务器有三大部分组成,第一部分是你的操做系统,第二部分是你的Cache缓存系统,第三部分就是你的网络。而对于OS来讲,通常你的操做系统选型完毕优化以后你通常不会再动它了,除非遇到了重大的安全隐患或者是有重大的升级。而对你Cache系统来讲也是,通常都求稳,在没有重大的bug的时,不会去轻易的改变。但最复杂的就是网络,你必需要掌握对网络的控制度,这样的话你才能驾驭它。

 

若是你的网络研究的很透彻,经过你的分析会发现不少问题。给各位讲个案例,咱们在访问某一个资源的时,大概可能五年前或更早,我刚加入蓝讯的时去分析,看到咱们不如竞争对手。我经过数据包的分析发现,不是咱们资源很差,而是咱们的TCP优化没有作,而对方作了TCP优化。

 

以这个例子来说,可经过第一个信包和第二个包之间的时间差可看到他的RTT是23毫秒。经过这个RTT咱们可看到它在第19个包和第21个包之间有一个时间跳变,这个跳变就意味着它属于第二轮发包机制。那第一轮能够数一下一共是10个包,也就是说初始initcwnd值是10。假设有一个50kb的数据包须要发送,算一下须要多长时间。50*1024算出字节再除以 1460换成包数,再加1,加1的缘由是考虑到必定会有余数,余数的话就占一个包因此加1。等于36个包,要把36个包发出去才能完成这个发送。

 

假如要发36个包,初始发包数量是10。可算下36拆解等于10+20+6。他须要3个往返才能把这个数据发完。套公式算下它的发送时间须要多久?RTT咱们以前算了是23,23*4。为何是4呢,由于你还要加一个TCP三次握手的一个时间,一共须要92毫秒才能完成。

 

 

上图是竞争对手的状况,咱们能够经过第一次和第二次握手,看到他的往返时延是35毫秒。和以前的23毫秒相比,可知这个资源的ping值比原来增长了52%。经过刚才的分析方法咱们也能够找到第35和37号包的跳变点。那么35号包以前是第一个发送轮回。整个的发包数量是20,它的初始发包数量实际上并非标准的10,而是20。那么,咱们能够再算一下,若是你有50kb必需要发出,你最终须要也是36个包,可是你初始是20就需两轮,分别是20+16。

经过套公式,可知须要150毫秒完成。那150毫秒跟以前的92比只慢14%。在资源落后52%的状况下,最终效果才慢了14%,中间的这个差距实际上就是你的技术带来的价值。

 

 

这是个视频点播对比数据图,高低表明发送速率,横轴是时间。经过对比,明显可看到厂商a的发送速度高于厂商b。作完TCP后和作TCP优化前,差距仍是很大。整个过程可看到第一个厂商的速度起来之后很是平稳,直到结束,而第二个厂商他最开始速度很快逐渐发现要丢包减速,速度就就涨不起来了。这就是提速优化价值。

 

 

上图是当时分析为何服务器发送慢的一个结果,经过这个分析直接就给你们总结一下吧!结果能够看到这是因为TCP算法自身的问题而致使的,他把时间白白浪费了,他们有时间实际上是能够继续发出去的但并无继续发。

 

 

另外,还有一个有意思的,每一个厂商不管你是作CDN,作电商、作IT企业,只要你有对外提供的server,并且server的负载比较高都会遇到的一个syncookie的坑。给你们讲一下。在TCP的标准里有两个选项一个叫WScale一个是SACK。他并非在RFC793里边出现的,他是在RFC1323里补充而出现的。

 

如今讲一下这个WScale什么东西。默认的状况下在标准的TCP协议,在早期的时候是没有WScale概念的。他在TCP的头部有一个16byte的空间来表示你能发送的最大字节数,一个周期能发送的最大字节数。那根据TCP的吞吐量的计算公式,吞吐量必定是小于等于最大发送窗口除以RTT的,这个RTT是以秒为单位。

 

之因此说小于等因而由于通常的状况下他是有可能有乱序或者抖动的。假如你的TCP协议传输时,RTT是100毫秒,假设网络之间没有丢包也没有乱序也没有抖动,且带宽是无限大的。套公式可知,64k除以100毫秒,也就是0.1,吞吐量最大是640k。即便你的带宽无限大,没有丢包,没有抖动,最大640k,就是这么严格。

 

你们可能以为这个有点儿难以想象,为何咱们传输速度是远大于这个呢?由于在新的标准里引用WScale这个概念。在TCP三次握手的时候,客户端若是要支持这个选项的话,服务端被通知支持这个选项。若是服务端也支持,会记录下来讲客户端支持,同时回应也支持。在客户端拿到第二次握手时,服务端就也把本身置成支持状态了。在数据传输的时,它是以2为底数,而后以WScale的这个n值为指数的一个滑动窗口递增值。

 

利用这个WScale是可把发送窗口的数量涨到很大的,好比说64k、128k、256k甚至更大。若是要这样再套公式,他的传输效果就会变得很是好了。

 

 

关于参数SACK,选择性应答,全称是Selective ACK。在你数据传输的时,没有这个选项他会怎么样呢?好比,要传10个数据包,只有第6个数据包丢掉了,那么在服务端收到ACK的时候他会知道,只收到了5,而后就没有其余信息了。这个时候他须要怎么作呢?须要把6到10从新发一遍。那会致使两个问题,第一,你的数据从开始到传完,速度就会变慢。第二个就是占用额外带宽把7到10进行一个不必的重传。

 

一样的在TCP三次握手的时候写标记,而且两边都确认同时开启,若是要是都支持的话,在客户端反馈数据的时,他就会告诉服务端,收到连续的序号完整的到5,可是我还收到了7到10。服务端就可经过这两个信息进行拼接找到中间的空隙,就会知道只有6号丢掉了,他就只需传6就能够。为何要强调这两个,多是为后面那个syncookie的坑作铺垫。

 

接触过linux的人都知道在里面有一个叫syncookie的机制。是一种帮助你去防御必定量攻击的一种方法。那么它的原理是什么呢?在每一次数据正常创建的时它优先消耗一个叫链接队列的一个概念。等链接队列满了,若是你syncookie未开启,有新的请求过来,他就会把那个新丢掉,若是你有大量的这种假的建连数据包已充斥满了整个建连队列的,那么他就会致使拒绝服务。

 

那syncookie开启的状况下会怎么样呢?他会在协议栈以前本身伪造一个应答机制,并非真正的协议栈去代应答第二次握手。同时他的第二次握手会携带一个算好的一个cookie值做为第三次握手的校验。若是他收到了第三次握手的校验值的会被认为是一个合法的建连,那么,他会把这个经过注入的方式,直接告诉你这个连接能够直接用了。那在前期syncookie当满的时候开始启动这个状态,他是不占用队列的,因此说他是一个很是好的防攻击的一个手段,可是他的防攻击的量不会很大,微量是能够的。

 

但坑也偏偏就在这。因为syncookie他不是标准的TCP协议栈,因此说他的支持,并非很是的完备。等一段syncookie发出,他代应答的第二次握手并不携带WScale和SACK这个选项。就会让客户端误认为是不支持的,因此,后续的沟通就变得很是的低效。咱们以前作过一个实验,在有必定量丢包并且大延时的状况下,你的速度可能只有300多k。后来查了不少资料发现确实是这个样子,并且咱们作了不少的模拟时间。好比,都为syncookie出发的时,他速度确实就很快。

 

后来咱们作了一个改动,在syncookie上,若是要是代应答的时,咱们携带SACK的这个数据给客户,那后来建连的时均可以把这个功能用起来。用起来时咱们在线上是真正的无环境试验能够提高大概25%到35%的服务质量。

 

 

另外,讲一下关于Cache的选型。不少的公司都在自研,也有不少公司直接用的开源的软件,至于怎么去选可能就要看你本身的场景了。好比,裸盘技术不依赖文件系统,但他的缺点是在于他对业务的支撑比较差,且他是c++语言写的COVER住的人仍是很少的,那世界上有不少的公司是跟那个SD相结合,利用的方式去拆解业务,后续来作Cache。

还有不少公司是走的自研路线,好比像网速蓝汛,阿里快忙这些公司都是曾经作过自研的,后续是什么发展目前不太清楚。

 

 

坦白来说,并不推荐自研。能用钱解决的问题都不是问题。若是去自研,第一,你要耗费大量的人力和时间。第二,你须要去思考,你能不能cover住这些本来人家已经作好的东西。你要自研的话,前提是你在重复造车的过程中必定有一个车轮,那个车轮能不能保证你的这个龙骨可以跟人家同样,甚至比人家更好。这里给你们分享一个我以前遇到过的自研软件的坑。

 

这个数据包它体现的是什么?体现的是你看最后的那两个数据包,倒数第二个是一个get请求。它发送完这个get请求以后立刻收到了一个RST,这个链接被断掉了。当时形成了不少的投诉,那咱们也是去考虑这个问题究竟是怎么形成的?

 

 

经过抓包在服务器上抓包微观去分析,咱们发现实际上这个问题是怎么形成的呢?看上面这个截图,在客户端发起请求的时候他有一个字段就多是keep live。意思是说他指望去和服务器进行这种长链接。而服务器是怎么给的呢?服务器给出去的时,并无明确的告诉客户端是否支持。致使这个问题的缘由是因为咱们的研发人员并无真正地领会RTT协议的精髓,他没有彻底cover住这个RTT协议致使最基本的这种车轮,这个轮骨作的是有问题的致使很严重的坑。

 

在HTTP1.0协议里边,若是你要是发一个多是keeplive你但愿和server进行keeplive这种沟通的话。Server端必需要告诉说我支持这样才能支持,有点像TCP的三次握手,那个WScale和那个SACK相似这样,可是在1.1协议里边儿偏偏不是这个样子的。若是你的身边没有任何反馈的话,客户端会默认为,我发了这个东西,你有没有告诉我说你不支持,那能不能支持?那这个时候问题就出现了。

这个就致使了第一个请求结束以后,服务端其实是不支持,但没有给出明确的显性的信息来告诉客户端。研发人员把1.0和1.1协议弄混了,或是他还认为1.1协议是跟1.0同样的标准。这个时候,在发送的第一个数据以后就把这个链接关掉,此时客户端并不知道服务端是不支持的,他还尝试发起第二次请求结果就致使被塞。

阅读原文

相关文章
相关标签/搜索