摘要: 本文做者:白金,《CDN 之我见》是一个系列文章,共由三个篇章组成,分为“原理篇”、“详解篇”和“陨坑篇”。本篇章属于“详解篇”的第一部分:网络优化。详解篇适合那些接触过 CDN,对 CDN 多少有些了解,但不少知识点知其然而不知其因此然,想深刻了解学习的同窗。算法
本文做者:白金
上篇回顾:《CDN 之我见》系列二:原理篇(缓存、安全)https://yq.aliyun.com/article...缓存
《CDN 之我见》是一个系列文章,共由三个篇章组成,分为“原理篇”、“详解篇”和“陨坑篇”。本篇章属于“详解篇”的第一部分:网络优化。详解篇适合那些接触过 CDN,对 CDN 多少有些了解,但不少知识点知其然而不知其因此然,想深刻了解学习的同窗。安全
众所周知,当前是互联网时代,不管大大小小的公司,不管是互联网企业仍是传统企业,通通离不开一个“网”字,而相比之下,硬件服务器、操做系统、应用组件等,在没有特殊必要需求的话(若是硬件不坏、软件或系统无急需修复的 BUG、无急需实现的客户需求),这些都是不用去主动改变。服务器
相比之下网络则不一样,最频繁变化的,最不想变化而又最无奈被动跟随变化的,就是网络质量,而 CDN 的缩写也是 Content Delivery Network,所以在详解篇我会把重点放在网络上。其它的部分相对都好把控,但网络问题存在必定的随机性和不可预测性。若是咱们能够驾驭网络,相信在这个互联互通的互联网时代,咱们将变得如虎添翼、叱咤风云!网络
为此,这里提出我对 CDN 的另外一个解读:CDN - Can Do sth. on Network(咱们能够在网络层面搞些事情)并发
提到优化,其实从 OSI 七层模型来说(准确说是 TCP/IP 模型,两者是不一样的,具体能够 google 一下),从物理层到应用层其实都是能够优化的,优化手段各异。ssh
L1 物理层:硬件优化(升级硬件设备,承载更多业务)
L2 链路层:资源好坏(寻找更快的网络节点、确保 Lastmile 尽可能短)
L3 路由层:路径优化(寻找 A 到 B 的最短路径)
L4 传输层:协议栈优化(TCP Optimization)
L7 应用层:能作的事情太多了(主要面向 HTTP 面向业务)tcp
针对 CDN 而言,经常使用的是 L4 和 L7 的优化,例如上图所述。分布式
分布式就近部署:确保网民访问到的 Cache Server 离他是最近的。
策略性缓存:针对明确已知的图片、CSS、JS 等,若源站更新并不快,但却没有明确高值缓存多长时间,CDN Cache Server 能够自定义一个缓存时间(例如 60s),这样能够确保在 60s 以内的一样请求会快速给出数据而不用穿越整个 Internet 从源站获取。
传输路径优化:寻找从边缘 CDN Cache Server 经 upper CDN Cache Server 到源站的最优传输路径,确保动态传输的数据能够走端到端的最优路径。
链接加速:经过修改协议栈的 Handshake Timer 来实现快速重试,以弥补因为丢包致使的重试超时。
传输层优化:这里主要是指 TCP 协议,针对 TCP 协议能够作不少优化策略,因为篇幅问题后面再讲。
内容预取:解析 WEB 内容,对于里面的 Object,在网民请求以前,优先由 CDN Cache Server 主动获取并缓存下来,以缩短数据交互时间,提高网民体验感。
合并回源:当有多我的前后下载同一个还未缓存住的内容时(例如一个 mp4 视频文件),CDN Cache Server 作到合并链接从 upstream 拿数据,而不是几个请求,几个 to uptream connection,既作到了带宽成本优化,又作到了给 upstream 降载,后请求的人还能享受以前 CDN Cache Server 已经下载过的部分文件,这部份内容直接 HIT,只有当追上第一个 downloader 的时候才一块儿等待 MISS 数据。
持久链接池:在 Middlemile 之间预先创建好 TCP Connection,并一直保持不断开,当网民有新请求过来时,边缘 CDN Cache Server 直接借助与 upper 创建好链接,直接发送 HTTP 的 GET/POST 报文到 upper CDN Cache Server,进行 TCP “去握手化”,减小因为 TCP 链接创建而形成的时间损耗(多适用于高并发小文件请求)。
主动压缩:不少源站因为规划设计问题或担忧负载太高问题,页面中的 HTML、CSS、JS 文件(这种文件具备高度可压缩性)并未压缩传输,此时 CDN Cache Server 能够主动对其进行压缩后传输并缓存,以减小传输量、下降交互时间、提高用户体验感。
Offline:当源站挂了怎么办?网民访问时,会拿不到数据。那么 CDN 此时能够策略性发送最新缓存的一份旧数据给网民,而不是生硬的告知用户不可访问,以提高用户体验感。
总之,优化策略很是多,下面将抽取其中最具通用性的网络部分进行详细阐述。高并发
如前文所述,所谓路径优化就是找到两点间的最优路径。
对于网络而言,A 到 B 最快 ≠ A 距离 B 最近,从广东联通访问福建联通,可能不如广东联通经北京联通再到福建联通更快,所以要对节点作实时探测,计算最优路径。
计算最优路径时,还要考虑带宽饱和度、成本、客户敏感度问题综合计算,所以不是看上去那么简单的。
带宽饱和度:做为中转节点(例如上例所说的北京),若是带宽自己已经没有多少剩余,那么穿越北京的路径优化可能会做为压死大象的最后一根稻草,使本来还 OK 的北京节点变得不堪重负。
成本:还以北京为例,北京资源的带宽成本确定远高于其它省市,例如比河北联通、天津联通可能要贵不少,但可能只比河北天津慢几个毫秒(运动员起跑时最快的反应时间是 150 毫秒),那么为了这几毫秒要多支付不少带宽费用显然是不值当的,那么利用北京进行中转显然就是不值得的(固然,有的时候就是为了和对手 PK,那也没办法)。
客户敏感度:有了中转路径,提速效果固然是好的,但如前文所述也是有代价的,那么是全部业务流量都走最优路径呢?仍是只让个别业务走最优路径呢?这个就要看客户敏感度了。例如重点大客户,例如对质量要求较高的高价优质客户,这些客户可能就是首选。
如前文所述,所谓传输层优化主要是指 TCP 优化,这是全部互联网行业的通用技术,是重中之重的领域,TCP 优化若是作的好,可弥补节点质量低下而形成的响应时间过大的损失。
赛马比赛时,有好马固然跑的快。若是马通常(不是太差),骑手的骑术精湛,或许一样也能够得第一,就是这个道理!
另一点 TCP 优化重要的缘由在于,TCP 是互联网尤为是 CDN 的基础协议,基本上全部业务都是 over TCP 来进行传输的,若是将 TCP 优化作好,受益面很是广,能够说全局收益(不只是提高客户体验感,也能提高内部支撑系统的使用体验感,例如日志传输、配置下发等)。
谈到 TCP 优化,须要先将 TCP 协议基础知识。须要首先明确一些名词属于的概念。
CWND:Congestion Window,拥塞窗口,负责控制单位时间内,数据发送端的报文发送量。TCP 协议规定,一个 RTT(Round-Trip Time,往返时延,你们常说的 ping 值)时间内,数据发送端只能发送 CWND 个数据包(注意不是字节数)。TCP 协议利用 CWND/RTT 来控制速度。
SS:Slow Start,慢启动阶段。TCP 刚开始传输的时候,速度是慢慢涨起来的,除非遇到丢包,不然速度会一直指数性增加(标准 TCP 协议的拥塞控制算法,例如 cubic 就是如此。不少其它拥塞控制算法或其它厂商可能修改过慢启动增加特性,未必符合指数特性)。
CA:Congestion Avoid,拥塞避免阶段。当 TCP 数据发送方感知到有丢包后,会下降 CWND,此时速度会降低,CWND 再次增加时,再也不像 SS 那样指数增,而是线性增(同理,标准 TCP 协议的拥塞控制算法,例如 cubic 是这样,不少其它拥塞控制算法或其它厂商可能修改过慢启动增加特性,未必符合这个特性)。
ssthresh:Slow Start Threshold,慢启动阈值。当数据发送方感知到丢包时,会记录此时的 CWND,并计算合理的 ssthresh 值(ssthresh <= 丢包时的 CWND),当 CWND 从新由小至大增加,直到 sshtresh 时,再也不 SS 而是 CA。但由于数据确认超时(数据发送端始终收不到对端的接收确认报文),发送端会骤降 CWND 到最初始的状态。
了解了 TCP 的一些名词属于,其实大体也就了解了 TCP 的运做机制,可是有几点要注意。
1.不一样的操做系统、内核版本,initcwnd(初始 CWND)不一样
以 Linux 为例,kernel-2.6.18 的 initcwnd 是 2,而 kernel-2.6.32 变成了 10,经过 iproute 软件包中的 ip 命令能够修改 Linux 的 CWND 和 RWND。
具体能够参考一篇 Google 写的 Paper,是他们提出了原始 initcwnd = 2 过小了,须要扩大的理念。
原文请参考:
https://developers.google.com...
2.单位时间内(一个 RTT)发送量不是 CWND,而是 min(CWND, RWND)
除了数据发送端有个 CWND 之外,数据接收端还有个 RWND(Receive Window,接收窗口),决定还能接收多少数据,并经过接收确认报文显性告知数据发送端,发送端要参考 CWND 和 RWND 信息决定最终发送的数据量(packet 个数)。管道中究竟能放多少数据,取决于 CWND 和 RWND。
另外一个问题:TCP 怎么了?TCP 有什么问题吗?
若是能问出这个问题,证实同窗们的关注点是正确的。
TCP 是在上个世纪六七十年代设计的,当时面向的是短距离传输、窄带(可能仍是半双工通讯)的链路环境。链路自己不太可能丢包,丢包基本都是由于链路拥塞形成的。根据早起的 TCP 拥塞控制算法,丢包 -> 降速,再丢 -> 再降,算法自己的目的是但愿经过降速来规避拥塞,进而规避丢包状况。
这个算法自己的理念是正确的,可是随着时代的发展,有了 4G,有了 WiFi,有了长距传输,有了策略性丢包逻辑,致使 TCP 传输过程当中,丢包不必定就是拥塞形成的,若是按照传统的“丢包就降速”策略来考虑问题,可能不但不能缓解问题,甚至可能会致使问题更加恶化。
举个例子来讲,在一个平均随机丢包 50% 的链路上(平均发出去的包,每 2 个就必然丢 1 个),在这种环境下,发现丢包了,降速发送有用吗?答案毋庸置疑,降速只会让对端收到的有效数据更少。这种环境如何优化呢?很简单,每一个包发 2 遍不就能够了?这样对端就会 100% 收到数据了,但代价就是发送端的出口流量是以前的 2 倍。
固然,真正在作 TCP 优化时不是这么简单的,要考虑不少细节,例如如何区分丢包缘由,例如该如何控制 CWND,例如如何更早的发现接收端没收到数据,例如当对端无响应时如何快速感知等等等等……
TCP 优化其实是在用带宽换用户体验感,低成本低质量网络虽然能够经过 TCP 优化提高体验感,但综合成本甚至可能比直接采购优质高价节点更高,效果还未必比优质节点直接服务好。
TCP 之因此叫优化不叫加速,是由于它可让那些本来应当传输很快,因为算法不合理致使的传输变慢的传输变得快起来,但却不能让那些链路质量本来没有问题的变得更快。
除此以外还有一个优化点,就是 TCP Pacing。
前文咱们已经讲过,TCP 是经过 CWND/RTT 来控制传输速率的,在一个 RTT 中,最多只能发 min(CWND, RWND) 这么多数据。可是 TCP 协议栈在发送数据时,是在 RTT 周期之初一股脑将数据发送出去的,这样宏观看没有任何问题,但若是微观看,数据发送波形就像上图那样,一个又一个的凸起。虽然在 RTT 单位时间内发送量恒定,但某微观时间线上的发送速率确实超级猛烈的,这样有可能形成瞬间链路拥塞(尤为是窄带线路)。
原文请参考:https://homes.cs.washington.e...
经过 TCP 三次握手建连能够测得 RTT,在已知 CWND 的状况下,经过控制发包的时间间隔能够实现 Pacing 效果,使数据包在围观看是均衡发送充满整个 RTT 空间的效果,这样能够避免瞬时拥塞,对窄带链路后须要匀速恒定传输的业务很是有效。
除了 Pacing 之外,还有不少不一样的优化算法或策略:
建连优化:TCP 在创建链接时,若是丢包,会进入重试,重试时间是 1s、2s、4s、8s 的指数递增间隔,缩短定时器可让 TCP 在丢包环境建连时间更快,很是适用于高并发短链接的业务场景。
首包优化:此优化其实没什么实质意义,若要说必定会有意义的话,可能就是知足一些评测标准的须要吧,例若有些客户以首包时间做为性能评判的一个依据。所谓首包时间,简单解释就是从 HTTP Client 发出 GET 请求开始计时,到收到 HTTP 响应的时间。为此,Server 端能够经过 TCP_NODELAY 让服务器先吐出 HTTP 头,再吐出实际内容(分包发送,本来是粘到一块儿的),来进行提速和优化。听说更有甚者先让服务器无条件返回 "HTTP/" 这几个字符,而后再去 upstream 拿数据。这种作法在真实场景中没有任何帮助,只能欺骗一下探测者罢了,所以还没见过有直接发 "HTTP/" 的,实际上是一种做弊行为。
平滑发包:如前文所述,在 RTT 内均匀发包,规避微分时间内的流量突发,尽可能避免瞬间拥塞,此处再也不赘述。
丢包预判:有些网络的丢包是有规律性的,例如每隔一段时间出现一次丢包,例如每次丢包都连续丢几个等,若是程序能自动发现这个规律(有些不明显),就能够针对性提早多发数据,减小重传时间、提升有效发包率。
RTO 探测:如前文讲 TCP 基础时说过的,若始终收不到 ACK 报文,则须要触发 RTO 定时器。RTO 定时器通常都时间很是长,会浪费不少等待时间,并且一旦 RTO,CWND 就会骤降(标准 TCP),所以利用 Probe 提早与 RTO 去试探,能够规避因为 ACK 报文丢失而致使的速度降低问题。
带宽评估:经过单位时间内收到的 ACK 或 SACK 信息能够得知客户端有效接收速率,经过这个速率能够更合理的控制发包速度。
带宽争抢:有些场景(例如合租)是你们互相挤占带宽的,假如你和室友各 1Mbps 的速度看电影,会把 2Mbps 出口占满,而若是一共有 3 我的看,则没人只能分到 1/3。若此时你的流量流量达到 2Mbps,而他俩还都是 1Mbps,则你至少仍能够分到 2/(2+1+1) * 2Mbps = 1Mbps 的 50% 的带宽,甚至更多,代价就是服务器侧的出口流量加大,增长成本。(TCP 优化的本质就是用带宽换用户体验感)
链路质量记忆:若是一个 Client IP 或一个 C 段 Network,若已经得知了网络质量规律(例如 CWND 多大合适,丢包规律是怎样的等),就能够在下次链接时,优先使用历史经验值,取消慢启动环节直接进入告诉发包状态,以提高客户端接收数据速率。
以前讲的 TCP 优化都是须要去修改代码的,这里有一个不用修改代码的方法,仅修改参数就能够。
内核协议栈参数 net.ipv4.tcp_slow_start_after_idle 默认是开启的,这个参数的用途,是为了规避 CWND 无休止增加,所以在链接不断开,但一段时间不传输数据的话,就将 CWND 收敛到 initcwnd,kernel-2.6.32 是 10,kernel-2.6.18 是 2。所以在 HTTP Connection: keep-alive 的环境下,若连续两个 GET 请求之间存在必定时间间隔,则此时服务器端会下降 CWND 到初始值,当 Client 再次发起 GET 后,服务器会从新进入慢启动流程。
这种友善的保护机制,对于 CDN 来讲是帮倒忙,所以咱们能够经过命令将此功能关闭,以提升 HTTP Connection: keep-alive 环境下的用户体验感。
# sysctl net.ipv4.tcp_slow_start_after_idle=0