全栈工程师眼中的HTTP

  

  HTTP,是Web工程师天天打交道最多的一个基本协议。不少工做流程、性能优化都围绕HTTP协议来进行,可是咱们对HTTP的理解是否全面呢?若是前端工程师和后台工程师坐在一块儿玩捉鬼游戏,他们对HTTP的描述可能会大相径庭,从这两个角色的视角看过去,HTTP呈现出大相径庭的形态。html

HTTP简介

超文本传输协议(HyperText Transfer Protocol,HTTP)是互联网上应用最为普遍的一种网络协议。设计HTTP的最初目的是提供一种发布和接收HTML页面的方法。OSI模型1义了整个世界计算机相互链接的标准,总共分为7层,其中最上层(也就是第7层)就是应用层,HTTP、HTTPS、FTP、TELNET、SSH、SMTP和POP3都属于应用层。这是软件工程师最关心的一层。前端

 

SI模型越靠近底层,就越接近硬件。在HTTP协议中,并无规定必须使用它或它支持的层。事实上,HTTP能够在任何互联网协议或其余网络上实现。HTTP假定其下层协议提供可靠的传输,所以,任何可以提供这种保证的协议均可以被其使用,也就是其在TCP/IP协议族使用TCP做为其传输层。面试

                                 OSI模型,图片来自维基百科。数据库

 

关于HTTP版本

  HTTP已经演化出了不少版本,它们中的大部分都是向下兼容的。客户端在请求的开始告诉服务器它采用的协议版本号,然后者则在响应中采用相同或者更早的协议版本。后端

  当前应用最普遍的HTTP版本为HTTP/1.1,它自从1999年发布以来,距写做本书时已有16年的时间。比起HTTP/1,它增长了几个重要特性,好比缓存处理(在下一章介绍)和持续链接,以及其余一些性能优化。浏览器

  2015年2月,HTTP/2正式发布。新的HTTP版本有一些重大更新,除了一如既往地向下兼容HTTP/1之外,还有一些优化,好比减少网络传输延迟,并简化服务器向浏览器传输内容的过程。主流的服务器(Apache、Nginx等)和浏览器(Firefox、Chrome、Safari以及iOS和Android的浏览器等)的最新版都已经支持HTTP/2,剩下的就须要网站管理员把服务器升级到最新版了。缓存

 

例子

下面是一个HTTP客户端与服务器之间会话的例子,运行于www.google.com,端口80。性能优化

 

客户端首先发出请求。服务器

 

GET / HTTP/1.1 Host:www.google.com

 

第一行指定方法、资源路径、协议版本。固然这是一个简化后的例子,实际请求中还会有当前Google登陆帐户的cookie、HTTPS头、浏览器接受何种类型的压缩格式和UA2代码等。cookie

 

服务器随之应答。

 

HTTP/1.1 200 OK Content-Length: 3059 Server: GWS/2.0 Date: Mon, 20 Apr 2015 20:30:45 GMT Content-Type: text/html Cache-control: private Set-cookie: PREF=ID=73d4aef52e57bae9:TM=1042253044:LM=1042253044:S= SMCc_HRPCQiqy X9j; expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com Connection: keep-alive

 

在这一串HTTPS头以后,会紧跟着一个空行,而后是HTML格式的文本组成的Google主页。

介绍完关于HTTP的基本知识,咱们来分别看看前端工程师和后台工程师分别是怎样看待这个最熟悉的小伙伴的。

 

前端视角

前端工程师的职责之一是,让网站又快又好地展示在用户的浏览器中。

从这个角度来讲,对HTTP的理解是这样的:打开HttpWatch3,而后随意访问一个网站。HttpWatch会按照浏览器请求的次序,列出打开这个网站的时候发生的请求细节。

  • 发出的请求列表。

  • 每一个请求的开始时间。

  • 每一个请求从开始到结束花费的时间。

  • 每一个请求的类型(好比是文本、CSS、JS,仍是图片或者字体等)。

  • 每一个请求的状态码(好比是200、仍是from cache、30四、404等)。

  • 每一个请求产生的流量消耗。

  • 每一个请求gzip压缩前的体积,以及在本地gzip解压后的体积。

经过查看站点的HTTP请求信息,能够获得不少优化信息。每个前端工程师都知道的基本优化方法是:尽可能减小同一域下的HTTP请求数,以及尽可能减小每个资源的体积。

 

尽可能减小同一域下的HTTP请求数

  浏览器经常限定了对同一域名发起的并发链接数的上限。IE6/7和Firefox2的设计规则是,同时只能对一个域名发起两个并发链接。新版本的各类浏览器广泛把这一上限设定为4至8个。若是浏览器须要对某个域进行更多的链接,则须要在用完了当前链接以后,重复使用或者从新创建TCP链接。

 

QQ空间的CSS贴图由程序自动生成,保证最佳的图片质量、最合理的图片摆放和最小的体积。

  因为浏览器针对资源的域名限制并发链接数,而不是针对浏览器地址栏中的页面域名,因此不少静态资源能够放在其余域名下(不一样的子域名也被认为是不一样的域名)。若是您只有一台服务器,能够把这些不一样的域名同时指向一个IP,也就提升了对这台服务器的并发链接数限制(不过要当心服务器压力过大)。

  把静态资源放在非主域名下,这种作法除了能够增长浏览器并发,还有一个好处是,减小HTTP请求中携带的没必要要的cookie数据。cookie是某些网站为了辨别用户身份而储存在用户浏览器中的数据。cookie的做用域是整个域名,也就是说若是某个cookie存放在google.com域名下,那么对于google.com域名下的全部HTTP请求头都会带上cookie数据。若是Google把全部的资源都放在google.com下,那么全部资源的请求都会带上cookie数据。对于静态资源来讲,这是毫无必要的,由于这对带宽和连接速度都形成了影响。因此咱们通常把静态资源放在单独的域名下。

  除此以外,前端工程师常常作的优化是合并同一域名下的资源,好比把多个CSS合并为一个CSS,或者将图片组合为CSS贴图。

  还有一些优化建议是省掉没必要要的HTTP请求,好比内嵌小型CSS、内嵌小型JavaScript、设置缓存,以及减小重定向。这些作法虽然各不相同,可是若是了解HTTP请求的过程,就知道这些优化方法的最终目的都是最大化利用有限的请求数。

 

尽可能减小每个资源的体积

咱们不光要限制请求数,还要尽可能减小每个资源的体积。由于资源的体积越大,在传输中消耗的流量就越多,等待时间也越久。

  在面试应聘者的时候,我会问的一个基础题目是“经常使用的图片格式有哪些,它们的使用场景是什么”。若是能选择合适的图片格式,就可以用更小的体积,达到更好的显示效果。对图片格式的敏感,能反映出工程师对带宽和速度的不懈追求。

  此外,对于比较大的文本资源,必须开启gzip压缩。由于gzip对于含有重复“单词”的文本文件,压缩率很是高,能有效提升传输过程。

  对于一个CSS资源的请求耗时,我想说明两个细节。

  • 这个CSS资源请求的体积是36.4KB(这是gzip压缩过的体积),解压缩以后,CSS内容其实是263KB,能够算出压缩后体积是原来的13.8%。

  • 整个链接的创建花费了30%的时间,发出请求到等待收到第一个字节回复花费了20%的时间,下载CSS资源的内容花费了50%的时间。

若是没有设置gzip,下载这个CSS文件会须要好几倍的时间。

 

后台视角

  前端工程师对HTTP的关注点在于尽可能减小同一域下的HTTP请求数,以及尽可能减小每个资源的体积。与之不一样,后台工程师对于HTTP的关注在于让服务器尽快响应请求,以及减小请求对服务器的开销。

  后台工程师知道,浏览器限定对某个域的并发链接数,很大程度上是浏览器对服务器的一种保护行为。浏览器做为一种善意的客户端,为了保护服务器不被大量的并发请求弄得崩溃,才限定了对同一个域的最大并发链接数。而一些“恶意”的客户端,好比一些下载软件,它做为一个HTTP协议客户端,不考虑到服务器的压力,而发起大量的并发请求(虽然用户感受到下载速度很快),可是因为它违反了规则,因此常常被服务器端“防范”和屏蔽。

 

那么为何服务器对并发请求数这么敏感?

  虽然服务器的多个进程看上去是在同时运行,可是对于单核CPU的架构来讲,其实是计算机系统同一段时间内,以进程的形式,将多个程序加载到存储器中,并借由时间共享,以在一个处理器上表现出同时运行的感受。因为在操做系统中,生成进程、销毁进程、进程间切换都很消耗CPU和内存,所以当负载高时,性能会明显下降。

 

提升服务器的请求处理能力

  在早期系统中(如Linux 2.4之前),进程是基本运做单位。在支持线程的系统(Linux2.6)中,线程才是基本的运做单位,而进程只是线程的容器。因为线程开销明显小于进程,并且部分资源还能够共享,所以效率较高。

  Apache是市场份额最大的服务器,超过50%的网站运行在Apache上。Apache 经过模块化的设计来适应各类环境,其中一个模块叫作多处理模块(MPM),专门用来处理多请求的状况。Apache安装在不一样系统上的时候会调用不一样的默认MPM,咱们不用关心具体的细节,只须要了解Unix上默认的MPM是prefork。为了优化,咱们能够改为worker模式。

  prefork和worker模式的最大区别就是,prefork的一个进程维持一个链接,而worker的一个线程维持一个链接。因此prefork更稳定但内存消耗也更大,worker没有那么稳定,由于不少链接的线程共享一个进程,当一个线程崩溃的时候,整个进程和全部线程一块儿死掉。可是worker的内存使用要比prefork低得多,因此很适合用在高HTTP请求的服务器上。

  近年来Nginx愈来愈受到市场的青睐。在高链接并发的状况下,Nginx是Apache服务器不错的替代品或者补充:一方面是Nginx更加轻量级,占用更少的资源和内存;另外一方面是Nginx 处理请求是异步非阻塞的,而Apache 则是阻塞型的,在高并发下Nginx 能保持低资源、低消耗和高性能。

  因为Apache和Nginx各有所长,因此常常的搭配是Nginx处理前端并发,Apache处理后台请求。

  值得一提的是,新秀Node.js也是采用基于事件的异步非阻塞方式处理请求,因此在处理高并发请求上有自然的优点。

 

DDoS攻击

DDoS是Distributed Denial of Service的缩写,DDoS攻击翻译成中文就是“分布式拒绝服务”攻击。

  简单来讲,就是黑客入侵并控制了大量用户的计算机(俗称“肉鸡”),而后在这些计算机上安装了DDoS攻击软件。咱们知道浏览器做为一种“善意”的客户端,限制了HTTP并发链接数。可是DDoS就没有这样的道德准则,每个DDoS攻击客户端均可以自由设置TCP/IP并发链接数,而且链接上服务器以后,它不会立刻断开链接,而是保持这个链接一段时间,直到同时链接的数量大于最大链接数,才断开以前的链接。

  就这样,攻击者经过海量的请求,让目标服务器瘫痪,没法响应正常的用户请求,以此达到攻击的效果。

  对于这样的攻击,几乎没有什么特别好的防御方法。除了增长带宽和提升服务器能同时接纳的客户数,另外一种方法就是让首页静态化。DDoS攻击者喜欢攻击的页面通常是会对数据库进行写操做的页面,这样的页面没法静态化,服务器更容易宕机。DDoS攻击者通常不会攻击静态化的页面或者图片,由于静态资源对服务器压力小,并且可以部署在CDN上。

  这里介绍的只是最简单的TCP/IP攻击,而DDoS是一个概称,具体来讲,有各类攻击方式,好比CC攻击、SYN攻击、NTP攻击、TCP攻击和DNS攻击等。

 

BigPipe

前端跟后端在HTTP上也能有交集,BigPipe就是一个例子。

  现有的HTTP数据请求流程是:客户端创建链接,服务器赞成链接,客户端发起请求,服务器返回数据,客户端接受并处理数据。这个处理流程有两个问题。

  现有的阻塞模型,黄色表明服务器生成页面,白色表明网络传输,紫色表明浏览器渲染页面。

 

  第一,HTTP协议的底层是TCP/IP,而TCP/IP规定3次握手才创建一次链接。每个新增的请求都要从新创建TCP/IP链接,从而消耗服务器的资源,而且浪费链接时间。对于几种不一样的服务器程序(Apache、Nginx和Node.js等),所消耗的内存和CPU资源也不太同样,可是新的链接没法避免,没有从本质上解决问题。

  第二个问题是,在现有的阻塞模型中,服务器计算生成页面须要时间。等服务器彻底生成好整个页面,才开始网络传输,网络传输也须要时间。整个页面都彻底传输到浏览器中以后,在浏览器中最后渲染仍是须要时间。三者是阻塞式的,每个环节都在等上一个环节100%完成才开始。页面做为一个总体,须要完整地经历3个阶段才能出如今浏览器中,效率很低。

  BigPipe是Facebook公司科学家Changhao Jiang发明的一种非阻塞式模型,这种模型能完美解决上面的两个问题。

  通俗来解释,BigPipe首先把HTML页面分为不少部分,而后在服务器和浏览器之间创建一条管道(BigPipe就是“大管道”的意思),HTML的不一样部分能够源源不断地从服务器传输到浏览器。BigPipe首先输送的内容是框架性HTML结构,这个框架结构可能会定义每一个Pagelet模块的位置和宽高,可是这些pagelet都是空的,就像只有钢筋混泥土骨架的毛坯房。

BigPipe页面的渲染流程。

 

服务器传输完框架性HTML结构以后,对浏览器说:“我这个请求还没结束,咱们保持这个链接不要断开,不过您能够先用我给您的这部分来渲染。”

  因此浏览器就开始渲染这个“不完整的HTML”,毛坯房页面很快出如今用户眼前,具体的页面模块都显示“正在加载”。

  接下来管道里源源不断地传输过来不少模块,这时候最开始加载在服务器中的JS代码开始工做,它会负责把每个模块依次渲染到页面上。

  在用户的感知上,页面很是快地出如今眼前,可是全部的模块都显示正在加载中,而后主要的区域(好比重要的用户动态)优先出现,接下来是logo、边栏和各类挂件等。

  为何BigPipe可以让服务器对浏览器说“我这个请求还没结束,咱们保持这个链接不要断开”呢?答案是HTTP1.1的分块传输编码。

 

  HTTP 1.1引入分块传输编码,容许服务器为动态生成的内容维持HTTP持久连接。若是一个HTTP消息(请求消息或应答消息)的Transfer-Encoding消息头的值为chunked,那么消息体由数量不肯定的块组成——也就是说想发送多少块就发送多少块——并以最后一个大小为0的块为结束。

  实现这个架构须要深入理解HTTP 1.1的规则,并且要有前端的知识。在我看来,这就是一个极佳的全栈工程师改变世界的例子。

  截止写书时,Chrome、Safari和Opera已经支持HTTP/2并默认开启,它容许服务器向浏览器“推送”内容。也就是说,返回的条目数能够比请求的条目数多,这样服务器能够在一开始就推送全部它认为浏览器“应该须要”的资源,而不须要浏览器接受并解析完HTML页面才开始请求下载CSS、JavaScript等。并且,后面的请求能够复用以前已经创建的底层链接。

 

【全栈工程师眼中的HTTP】一文摘自《Web全栈工程师的自我修养》一书,由异步社区出版。本书做者余果是腾讯公司高级工程师,在前端、后端和APP开发方面都有丰富的经验,在本书中分享了全栈工程师的技能要求、核心竞争力、将来发展方向、对移动端的思考。除此以外,本书还详细记录了做者从零开始、学习成长的心路历程。

相关文章
相关标签/搜索