漫谈 HTTP 性能优化

本文主要是侧重于 HTTP 的优化,对于 HTTPS 后续文章会讲。php

既然要作性能优化,那么,咱们就须要知道:什么是性能?它都有哪些指标,又应该如何度量,进而采起哪些手段去优化?前端

“性能”实际上是一个复杂的概念。不一样的人、不一样的应用场景都会对它有不一样的定义。对于 HTTP 来讲,它又是一个很是复杂的系统,里面有很是多的角色,因此很难用一两个简单的词就能把性能描述清楚。web

仍是从 HTTP 最基本的“请求 - 应答”模型来着手吧。在这个模型里有两个角色:客户端和服务器,还有中间的传输链路,考查性能就能够看这三个部分。算法

unpreview

HTTP 服务器

咱们先来看看服务器,它通常运行在 Linux 操做系统上,用 Apache、Nginx 等 Web 服务器软件对外提供服务,因此,性能的含义就是它的服务能力,也就是尽量多、尽量快地处理用户的请求。后端

衡量服务器性能的主要指标有三个:吞吐量(requests per second)、并发数(concurrency)和响应时间(time per request)。浏览器

吞吐量就是咱们常说的 RPS,每秒的请求次数,也有叫 TPS、QPS,它是服务器最基本的性能指标,RPS 越高就说明服务器的性能越好。缓存

并发数反映的是服务器的负载能力,也就是服务器可以同时支持的客户端数量,固然也是越多越好,可以服务更多的用户。性能优化

响应时间反映的是服务器的处理能力,也就是快慢程度,响应时间越短,单位时间内服务器就可以给越多的用户提供服务,提升吞吐量和并发数。服务器

除了上面的三个基本性能指标,服务器还要考虑 CPU、内存、硬盘和网卡等系统资源的占用程度,利用率太高或者太低均可能有问题。网络

在 HTTP 多年的发展过程当中,已经出现了不少成熟的工具来测量这些服务器的性能指标,开源的、商业的、命令行的、图形化的都有。

在 Linux 上,最经常使用的性能测试工具可能就是 ab(Apache Bench)了,好比,下面的命令指定了并发数 100,总共发送 10000 个请求:

ab -c 100 -n 10000 'http://www.xxx.com'
系统资源监控方面,Linux 自带的工具也很是多,经常使用的有 uptime、top、vmstat、netstat、sar 等等,可能你比我还要熟悉,我就列几个简单的例子吧:复制代码
top // 查看 CPU 和内存占用状况
vmstat 2 // 每 2 秒检查一次系统状态
sar -n DEV 2 // 看全部网卡的流量,定时 2 秒检查

理解了这些性能指标,咱们就知道了服务器的性能优化方向:合理利用系统资源,提升服务器的吞吐量和并发数,下降响应时间。

HTTP 客户端

看完了服务器的性能指标,咱们再来看看如何度量客户端的性能。

客户端是信息的消费者,一切数据都要经过网络从服务器获取,因此它最基本的性能指标就是“延迟”(latency)。

以前在讲 HTTP/2 的时候就简单介绍过延迟。所谓的“延迟”其实就是“等待”,等待数据到达客户端时所花费的时间。但由于 HTTP 的传输链路很复杂,因此延迟的缘由也就多种多样。

  • 首先,咱们必须谨记有一个“不可逾越”的障碍——光速,由于地理距离而致使的延迟是没法克服的,访问数千千米外的网站显然会有更大的延迟。

  • 其次,第二个因素是带宽,它又包括接入互联网时的电缆、WiFi、4G 和运营商内部网络、运营商之间网络的各类带宽,每一处都有可能成为数据传输的瓶颈,下降传输速度,增长延迟。

  • 第三个因素是 DNS 查询,若是域名在本地没有缓存,就必须向 DNS 系统发起查询,引起一连串的网络通讯成本,而在获取 IP 地址以前客户端只能等待,没法访问网站,

  • 第四个因素是 TCP 握手,你应该对它比较熟悉了吧,必需要通过 SYN、SYN/ACK、ACK 三个包以后才能创建链接,它带来的延迟由光速和带宽共同决定。

创建 TCP 链接以后,就是正常的数据收发了,后面还有解析 HTML、执行 JavaScript、排版渲染等等,这些也会耗费一些时间。不过它们已经不属于 HTTP 了,因此不在今天的讨论范围以内。

以前讲 HTTPS 时介绍过一个专门的网站“SSLLabs”,而对于 HTTP 性能优化,也有一个专门的测试网站“WebPageTest”。它的特色是在世界各地创建了不少的测试点,能够任意选择地理位置、机型、操做系统和浏览器发起测试,很是方便,用法也很简单。

网站测试的最终结果是一个直观的“瀑布图”(Waterfall Chart),清晰地列出了页面中全部资源加载的前后顺序和时间消耗,好比下图就是对 GitHub 首页的一次测试。

Chrome 等浏览器自带的开发者工具也能够很好地观察客户端延迟指标,面板左边有每一个 URI 具体消耗的时间,面板的右边也是相似的瀑布图。

点击某个 URI,在 Timing 页里会显示出一个小型的“瀑布图”,是这个资源消耗时间的详细分解,延迟的缘由都列的清清楚楚,好比下面的这张图:

图里面的这些指标都是什么含义呢?我给你解释一下:

  • 由于有“队头阻塞”,浏览器对每一个域名最多开 6 个并发链接(HTTP/1.1),当页面里连接不少的时候就必须排队等待(Queued、Queueing),这里它就等待了 1.62 秒,而后才被浏览器正式处理;

  • 浏览器要预先分配资源,调度链接,花费了 11.56 毫秒(Stalled);

  • 链接前必需要解析域名,这里由于有本地缓存,因此只消耗了 0.41 毫秒(DNS Lookup);

  • 与网站服务器创建链接的成本很高,总共花费了 270.87 毫秒,其中有 134.89 毫秒用于 TLS 握手,那么 TCP 握手的时间就是 135.98 毫秒(Initial connection、SSL);

  • 实际发送数据很是快,只用了 0.11 毫秒(Request sent);

  • 以后就是等待服务器的响应,专有名词叫 TTFB(Time To First Byte),也就是“首字节响应时间”,里面包括了服务器的处理时间和网络传输时间,花了 124.2 毫秒;

  • 接收数据也是很是快的,用了 3.58 毫秒(Content Dowload)。

从这张图你能够看到,一次 HTTP“请求 - 响应”的过程当中延迟的时间是很是惊人的,总时间 415.04 毫秒里占了差很少 99%。

因此,客户端 HTTP 性能优化的关键就是:下降延迟。

HTTP 传输链路

以 HTTP 基本的“请求 - 应答”模型为出发点,刚才咱们获得了 HTTP 性能优化的一些指标,如今,咱们来把视角放大到“真实的世界”,看看客户端和服务器之间的传输链路,它也是影响 HTTP 性能的关键。

这就是所谓的“第一千米”“中间一千米”和“最后一千米”(在英语原文中是 mile,英里)。

“第一千米”是指网站的出口,也就是服务器接入互联网的传输线路,它的带宽直接决定了网站对外的服务能力,也就是吞吐量等指标。显然,优化性能应该在这“第一千米”加大投入,尽可能购买大带宽,接入更多的运营商网络。

“中间一千米”就是由许多小网络组成的实际的互联网,其实它远不止“一千米”,而是很是很是庞大和复杂的网络,地理距离、网络互通都严重影响了传输速度。好在这里面有一个 HTTP 的“好帮手”——CDN,它能够帮助网站跨越“千山万水”,让这段距离看起来真的就好像只有“一千米”。

“最后一千米”是用户访问互联网的入口,对于固网用户就是光纤、网线,对于移动用户就是 WiFi、基站。之前它是客户端性能的主要瓶颈,延迟大带宽小,但随着近几年 4G 和高速宽带的普及,“最后一千米”的状况已经好了不少,再也不是制约性能的主要因素了。

除了这“三千米”,我我的认为还有一个“第零千米”, 就是网站内部的 Web 服务系统。它其实也是一个小型的网络(固然也可能会很是大),中间的数据处理、传输会致使延迟,增长服务器的响应时间,也是一个不可忽视的优化点。

在上面整个互联网传输链路中,末端的“最后一千米”咱们是没法控制的,因此咱们只能在“第零千米”“第一千米”和“中间一千米”这几个部分下功夫,增长带宽下降延迟,优化传输速度。

但由于咱们是没法彻底控制客户端的,因此实际上的优化工做一般是在服务器端。这里又能够细分为后端和前端,后端是指网站的后台服务,而前端就是 HTML、CSS、图片等展示在客户端的代码和数据。

知道了大体的方向,HTTP 性能优化具体应该怎么作呢?

总的来讲,任何计算机系统的优化均可以分红这么几类:硬件软件、内部外部、花钱不花钱。

投资购买现成的硬件最简单的优化方式,好比换上更强的 CPU、更快的网卡、更大的带宽、更多的服务器,效果也会“立竿见影”,直接提高网站的服务能力,也就实现了 HTTP 优化。

另外,花钱购买外部的软件或者服务也是一种行之有效的优化方式,最“物有所值”的应该算是 CDN 了。CDN 专一于网络内容交付,帮助网站解决“中间一千米”的问题,还有不少其余很是专业的优化功能。把网站交给 CDN 运营,就好像是“让网站坐上了喷气飞机”,可以直达用户,几乎不须要费什么力气就可以达成很好的优化效果。

不过这些“花钱”的手段实在是太没有“技术含量”了,属于“懒人”(无贬义)的作法,因此我就再也不细说,接下来重点就讲讲在网站内部、“不花钱”的软件优化。

我把这方面的 HTTP 性能优化归纳为三个关键词:开源、节流、缓存。

开源

这个“开源”可不是 Open Source,而是指抓“源头”,开发网站服务器自身的潜力,在现有条件不变的状况下尽可能挖掘出更多的服务能力。

首先,咱们应该选用高性能的 Web 服务器,最佳选择固然就是 Nginx/OpenResty 了,尽可能不要选择基于 Java、Python、Ruby 的其余服务器,它们用来作后面的业务逻辑服务器更好。利用 Nginx 强大的反向代理能力实现“动静分离”,动态页面交给 Tomcat、Django、Rails,图片、样式表等静态资源交给 Nginx。

Nginx 或者 OpenResty 自身也有不少配置参数能够用来进一步调优,举几个例子,好比说禁用负载均衡锁、增大链接池,绑定 CPU 等等,相关的资料有不少。

特别要说的是,对于 HTTP 协议必定要启用长链接。TCP 和 SSL 创建新链接的成本是很是高的,有可能会占到客户端总延迟的一半以上。长链接虽然不能优化链接握手,但能够把成本“均摊”到屡次请求里,这样只有第一次请求会有延迟,以后的请求就不会有链接延迟,整体的延迟也就下降了。

另外,在现代操做系统上都已经支持 TCP 的新特性“TCP Fast Open”(Win十、iOS九、Linux 4.1),它的效果相似 TLS 的“False Start”,能够在初次握手的时候就传输数据,也就是 0-RTT,因此咱们应该尽量在操做系统和 Nginx 里开启这个特性,减小外网和内网里的握手延迟。

下面给出一个简短的 Nginx 配置示例,启用了长链接等优化参数,实现了动静分离:

server {
  listen 80 deferred reuseport backlog=4096 fastopen=1024; 
 
 
  keepalive_timeout  60;
  keepalive_requests 10000;
  
  location ~* \.(png)$ {
    root /var/images/png/;
  }
  
  location ~* \.(php)$ {
    proxy_pass http://php_back_end;
  }
}

节流

“节流”是指减小客户端和服务器之间收发的数据量,在有限的带宽里传输更多的内容。

“节流”最基本的作法就是使用 HTTP 协议内置的“数据压缩”编码,不只能够选择标准的 gzip,还能够积极尝试新的压缩算法 br,它有更好的压缩效果。

不过在数据压缩的时候应当注意选择适当的压缩率,不要追求最高压缩比,不然会耗费服务器的计算资源,增长响应时间,下降服务能力,反而会“得不偿失”。

gzip 和 br 是通用的压缩算法,对于 HTTP 协议传输的各类格式数据,咱们还能够有针对性地采用特殊的压缩方式。

HTML/CSS/JS 属于纯文本,就能够采用特殊的“压缩”,去掉源码里多余的空格、换行、注释等元素。这样“压缩”以后的文本虽然看起来很混乱,对“人类”不友好,但计算机仍然可以毫无障碍地阅读,不影响浏览器上的运行效果。

图片在 HTTP 传输里占有很是高的比例,虽然它自己已经被压缩过了,不能被 gzip、br 处理,但仍然有优化的空间。好比说,去除图片里的拍摄时间、地点、机型等元数据,适当下降分辨率,缩小尺寸。图片的格式也很关键,尽可能选择高压缩率的格式,有损格式应该用 JPEG,无损格式应该用 Webp 格式。

对于小文本或者小图片,还有一种叫作“资源合并”(Concatenation)的优化方式,就是把许多小资源合并成一个大资源,用一个请求全下载到客户端,而后客户端再用 JS、CSS 切分后使用,好处是节省了请求次数,但缺点是处理比较麻烦。

刚才说的几种数据压缩都是针对的 HTTP 报文里的 body,在 HTTP/1 里没有办法能够压缩 header,但咱们也能够采起一些手段来减小 header 的大小,没必要要的字段就尽可能不发(例如 Server、X-Powered-By)。

网站常常会使用 Cookie 来记录用户的数据,浏览器访问网站时每次都会带上 Cookie,冗余度很高。因此应当少使用 Cookie,减小 Cookie 记录的数据量,总使用 domain 和 path 属性限定 Cookie 的做用域,尽量减小 Cookie 的传输。若是客户端是现代浏览器,还可使用 HTML5 里定义的 Web Local Storage,避免使用 Cookie。

压缩以外,“节流”还有两个优化点,就是域名和重定向。

DNS 解析域名会耗费很多的时间,若是网站拥有多个域名,那么域名解析获取 IP 地址就是一个不小的成本,因此应当适当“收缩”域名,限制在两三个左右,减小解析完整域名所需的时间,让客户端尽快从系统缓存里获取解析结果。

重定向引起的客户端延迟也很高,它不只增长了一次请求往返,还有可能致使新域名的 DNS 解析,是 HTTP 前端性能优化的“大忌”。除非必要,应当尽可能不使用重定向,或者使用 Web 服务器的“内部重定向”。

缓存

“缓存”不只是 HTTP,也是任何计算机系统性能优化的“法宝”,把它和上面的“开源”“节流”搭配起来应用于传输链路,就可以让 HTTP 的性能再上一个台阶。

在“第零千米”,也就是网站系统内部,可使用 Memcache、Redis、Varnish 等专门的缓存服务,把计算的中间结果和资源存储在内存或者硬盘里,Web 服务器首先检查缓存系统,若是有数据就当即返回给客户端,省去了访问后台服务的时间。

在“中间一千米”,缓存更是性能优化的重要手段,CDN 的网络加速功能就是创建在缓存的基础之上的,能够这么说,若是没有缓存,那就没有 CDN。

利用好缓存功能的关键是理解它的工做原理,为每一个资源都添加 ETag 和 Last-modified 字段,再用 Cache-Control、Expires 设置好缓存控制属性。

其中最基本的是 max-age 有效期,标记资源可缓存的时间。对于图片、CSS 等静态资源能够设置较长的时间,好比一天或者一个月,对于动态资源,除非是实时性很是高,也能够设置一个较短的时间,好比 1 秒或者 5 秒。

这样一旦资源到达客户端,就会被缓存起来,在有效期内都不会再向服务器发送请求,也就是:“没有请求的请求,才是最快的请求。”

HTTP/2

在“开源”“节流”和“缓存”这三大策略以外,HTTP 性能优化还有一个选择,那就是把协议由 HTTP/1 升级到 HTTP/2。

经过“飞翔篇”的学习,你已经知道了 HTTP/2 的不少优势,它消除了应用层的队头阻塞,拥有头部压缩、二进制帧、多路复用、流量控制、服务器推送等许多新特性,大幅度提高了 HTTP 的传输效率。

实际上这些特性也是在“开源”和“节流”这两点上作文章,但由于这些都已经内置在了协议内,因此只要换上 HTTP/2,网站就可以马上得到显著的性能提高。

不过你要注意,一些在 HTTP/1 里的优化手段到了 HTTP/2 里会有“反效果”。

对于 HTTP/2 来讲,一个域名使用一个 TCP 链接才可以得到最佳性能,若是开多个域名,就会浪费带宽和服务器资源,也会下降 HTTP/2 的效率,因此“域名收缩”在 HTTP/2 里是必需要作的。

“资源合并”在 HTTP/1 里减小了屡次请求的成本,但在 HTTP/2 里由于有头部压缩和多路复用,传输小文件的成本很低,因此合并就失去了意义。并且“资源合并”还有一个缺点,就是下降了缓存的可用性,只要一个小文件更新,整个缓存就彻底失效,必须从新下载。

因此在如今的大带宽和 CDN 应用场景下,应当尽可能少用资源合并(JS、CSS 图片合并,数据内嵌),让资源的粒度尽量地小,才能更好地发挥缓存的做用。

小结

  1. 性能优化是一个复杂的概念,在 HTTP 里能够分解为服务器性能优化、客户端性能优化和传输链路优化;

  2. 服务器有三个主要的性能指标:吞吐量、并发数和响应时间,此外还须要考虑资源利用率;

  3. 客户端的基本性能指标是延迟,影响因素有地理距离、带宽、DNS 查询、TCP 握手等;

  4. 从服务器到客户端的传输链路能够分为三个部分,咱们可以优化的是前两个部分,也就是“第一千米”和“中间一千米”;

  5. 有不少工具能够测量这些指标,服务器端有 ab、top、sar 等,客户端可使用测试网站,浏览器的开发者工具。

  6. 花钱购买硬件、软件或者服务能够直接提高网站的服务能力,其中最有价值的是 CDN;

  7. 不花钱也能够优化 HTTP,三个关键词是“开源”“节流”和“缓存”;

  8. 后端应该选用高性能的 Web 服务器,开启长链接,提高 TCP 的传输效率;

  9. 前端应该启用 gzip、br 压缩,减少文本、图片的体积,尽可能少传没必要要的头字段;

  10. 缓存是不管什么时候都不能忘记的性能优化利器,应该总使用 Etag 或 Last-modified 字段标记资源;

  11. 升级到 HTTP/2 可以直接得到许多方面的性能提高,但要留意一些 HTTP/1 的“反模式”。

 

参考文章

透视HTTP协议

相关文章
相关标签/搜索