- 五年前,我来北京参加面试, 一个面试官问我,你是做什么的,我说PHP全栈(年轻无畏,哈哈),然后面试官说,一个http的header都包含哪些信息你知道吗?然后我就怂了。。。。
- 那么今天,我们就来聊一聊http
HTTP的发展历史
- HTTP,全称Hyper Text Transfer Protocol,即超文本传输协议,是一个简单的请求-响应协议,它通常运行在TCP之上。
- HTTP目前一共经历了HTTP 0.9,HTTP 1.0,HTTP 1.1,HTTP 2.0,那么我们分别来看看每个版本的HTTP的功能和发生的变化
HTTP 0.9
HTTP 0.9是第一个版本的HTTP协议,已过时。它只允许客户端发送GET这一种请求,而想我们常用的POST PUT DELETE 是不支持的,而且由于不支持请求头,所以我们只能获取一些纯文本,如果你想获取一个图片,不好意思,不支持。 从HTTP 0.9开始,HTTP就是无状态的,每个事务独立进行处理,事务结束时就释放这个连接。一次HTTP 0.9的传输首先要建立一个由客户端到Web服务器的TCP连接,由客户端发起一个请求,然后由Web服务器返回页面内容,然后连接会关闭。如果请求的页面不存在,也不会返回任何错误码。
HTTP 1.0
HTTP 1.0是HTTP协议的第二个版本,第一个在通讯中指定版本号的HTTP协议版本,至今仍被广泛采用。相对于HTTP 0.9 增加了如下主要特性:
- 请求与响应支持头域
- 响应对象以一个响应状态行开始
- 响应对象不只限于超文本,还可以传输图像、音频、视频等二进制文件。
- 开始支持客户端通过POST方法向Web服务器提交数据,支持GET、HEAD、POST方法
- 开始支持cache,就是当客户端在规定时间内访问同一网站,直接访问cache即可。
- 支持返回状态码,方便了错误排查(504 502 404 403 302 301 等等),我们后面会开个小节去介绍
- 新增功能还包括状态码(status code)、多字符集支持、多部分发送(multi-part type)、权限(authorization)、缓存(cache)、内容编码(content encoding)等。
- 每次 TCP 连接只能发送一个请求,当服务器响应后就会关闭这次连接,下一个请求需要再次建立 TCP 连接。 TCP 连接的建立成本很高,因为需要客户端和服务器三次握手,并且开始时发送速率较慢(slow start)。为了解决这个问题,有些浏览器在请求时,即在请求头部加上 Connection 字段:
比如,我们请求一个html页面,里面有一个css文件和一个js文件,那么当我们访问这个页面的时候,就会建立三次tcp连接,这种形式就会造成性能上的缺陷,如果我们想要建立长连接,就要设置一个非标准的connection字段Connection:keep-alive
HTTP1.1
- HTTP1.1开始,默认支持长连接
Connection: keep-alive ,即在一个TCP 连接上我们可以传送多个http请求和响应,减少了建立tcp连接的次数,提高了http传输的性能 - 加入了管道机制
在同一个 TCP 连接里,允许多个请求同时发送,增加了并发性,进一步改善了 HTTP 协议的效率。 举例来说,客户端需要请求两个资源。以前的做法是,在同一个 TCP 连接里面,先发送 A 请求,然后等待服务器做出回应,收到后再发出 B 请求。 管道机制则是允许浏览器同时发出 A 请求和 B 请求,但是服务器还是按照顺序,先回应 A 请求,完成后再回应 B 请求。 - HTTP 1.1 支持文件断点续传,RANGE:bytes,HTTP 1.0 每次传送文件都是从文件头开始,即 0 字节处开始。RANGE:bytes=XXXX 表示要求服务器从文件 XXXX 字节处开始传送,断点续传。即返回码是 206(Partial Content)
- 引入了更多的缓存控制策略,如If-Unmodified-Since, If-Match, If-None-Match等缓存头来控制缓存策略
- 引入host,实现了在一台WEB服务器上可以在同一个IP地址和端口号上使用不同的主机名来创建多个虚拟WEB站点
- 添加了其他的请求方法:put、delete、options
HTTP2.0
HTTP2.0在相比之前版本,性能上有很大的提升
-
多路复用 HTTP 2.0 复用 TCP 连接,在一个连接里,客户端和浏览器都可以同时发送多个请求或回应,而且不用按照顺序一一对应,这样就避免了"队头堵塞"(HTTP 2.0 使用了多路复用的技术,做到同一个连接并发处理多个请求,而且并发请求的数量比 HTTP 1.1大了好几个数量级)。 举例来说,在一个 TCP 连接里面,服务器同时收到了 A 请求和 B 请求,于是先回应 A 请求,结果发现处理过程非常耗时,于是就发送 A 请求已经处理好的部分, 接着回应 B 请求,完成后,再发送 A 请求剩下的部分。 -
二进制分帧 HTTP 1.1 版的头信息肯定是文本(ASCII 编码),数据体可以是文本,也可以是二进制。 HTTP 2.0 则是一个彻底的二进制协议,头信息和数据体都是二进制,并且统称为"帧"(frame):头信息帧和数据帧。 -
首部压缩 HTTP 协议不带有状态,每次请求都必须附上所有信息。所以,请求的很多字段都是重复的,比如 Cookie 和 User Agent,一模一样的内容,每次请求都必须附带,这会浪费很多带宽,也影响速度。 HTTP 2.0 对这一点做了优化,引入了头信息压缩机制(header compression)。一方面,头信息使用 gzip 或c ompress 压缩后再发送;另一方面,客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号,这样就提高速度了。 -
服务器推送 HTTP 2.0 允许服务器未经请求,主动向客户端发送资源,这叫做服务器推送(server push)。意思是说,当我们对支持 HTTP 2.0 的 web server 请求数据的时候,服务器会顺便把一些客户端需要的资源一起推送到客户端,免得客户端再次创建连接发送请求到服务器端获取。这种方式非常合适加载静态资源。服务器端推送的这些资源其实存在客户端的某处地方,客户端直接从本地加载这些资源就可以了,不用走网络,速度自然是快很多的。
HTTP请求头和响应头的这些知识
Genneral
Request URL: https://m.acurd.com/blog-21/yqa242q620.html #请求地址
Request Method: GET #请求方式
Status Code: 200 OK #相应状态
Remote Address: 122.22.22.22:443 #远程服务器的地址 和端口号 http 访问是80 htts是443
Referrer Policy: strict-origin-when-cross-origin #Referrer-Policy的作用就是为了控制请求头中referrer的内容,目前是一个候选标准,可以控制referer的显示条件 显示 不显示 跨域显示等等
Request Headers
GET /blog-21/yqa242q620.html HTTP/1.1 # 请求地址 http协议版本号
Host: m.acurd.com #服务器域名
Connection: keep-alive # 该浏览器想要优先使用的连接类型
Cache-Control: max-age=0 # 用来指定在这次的请求/响应链中的所有缓存机制
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-
Referer: https://m.acurd.com/blog-21.html #访问来源
Response Headers
HTTP/1.1 200 OK #http协议 响应码
Server: nginx # 服务器
Date: Mon, 10 Jan 2022 09:15:17 GMT #相应时间
Content-Type: text/html; charset=utf-8 #相应类型和字符类型
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding
X-Powered-By: PHP/7.2.24 #解释器
Expires: Thu, 19 Nov 1981 08:52:00 GMT #缓存过期时间
Cache-Control: no-store, no-cache, must-revalidate #缓存机制 比如不需要Nginx缓存的,我们就设置成no-cache
- 其实大概来讲 就是几个,从哪来,到哪去,要什么,哪种方式传输 ,使用什么浏览器,缓存机制,缓存时间,字符类型
- 协商缓存
协商缓存是利用的是【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】这两对请求头响应头来管理的 Last-Modified 表示本地文件最后修改日期,浏览器会在request header加上If-Modified-Since(上次返回的Last-Modified的值),询问服务器在该日期后资源是否有更新,有更新的话就会将新的资源发送回来 Etag就像一个指纹,资源变化都会导致ETag变化,跟最后修改时间没有关系,ETag可以保证每一个资源是唯一的 If-None-Match的header会将上次返回的Etag发送给服务器,询问该资源的Etag是否有更新,有变动就会发送新的资源回来 而强制缓存不需要发送请求到服务端,根据请求头expires和cache-control判断是否命中强缓存 比如es的远程词库我们就需要通过配置 Last-Modified和ETag来确定是否要重新获取远程词库
关于http状态码的讲解
- 200 (成功) 服务器已成功处理了请求。 通常,这表示服务器提供了请求的网页。
- 301 (永久移动) 请求的网页已永久移动到新位置。 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。
- 302 (临时移动) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
301和302的区别,301: 旧地址A的资源不可访问了(永久移除), 重定向到网址B,搜索引擎会抓取网址B的内容,同时将网址保存为B网址。 302: 旧地址A的资源仍可访问,这个重定向只是临时从旧地址A跳转到B地址,这时搜索引擎会抓取B网址内容,但是会将网址保存为A的。 简单来说,301的话搜索引擎认为地址a废弃了,只抓取b的内容,存的网址也是b,302是说a地址还可以提供服务,只是暂时从b地址d - 403 (禁止) 服务器拒绝请求。
- 404 (未找到) 服务器找不到请求的网页。
- 500 (服务器内部错误) 服务器遇到错误,无法完成请求。
比如我的PHP文件中调用了一个不存在的类 调试状态下,如果我们加了ini_set("display_errors",true); nginx 就会返回200,并暴露错误
502 (错误网关) 服务器作为网关或代理,从上游服务器收到无效响应。
- nginx+php 出现 502 bad gateway,一般这都不是 nginx 的问题,而是由于 fastcgi 或者 php 的问题导致的,
- php.ini 的 memory_limit 过小
- php-fpm.conf 中 max_children 或者 max_requests 设置不合理(设置过小会因为没有足够的 cgi 进程处理请求,设置过大会出现一会儿有响应正常,一会儿等很久才有响应的情况,一般情况下 children 按 照内存计算,比如说 1G 设置 64 ,2G 设置 128。这个根据实际情况自行调整。另外查看当前的 PHP FastCGI 进程数是否够用的命令为:
netstat -anpo | grep "php-cgi" | wc -l 如果实际使用的 FastCGI进程数 接近预设的 FastCGI进程数,那么,说明FastCGI进程数不够用,需要增大。) - php 程序执行时间过长而超时 检查 nginx 和 fastcgi 中各种 timeout 设置。(nginx 中的 fastcgi_connect_timeout 300; 、fastcgi_send_timeout 300; 、fastcgi_read_timeout 300; 、keepalive_timeout php-fpm 中的 request_terminate_timeout,php.ini 中的 max_execution_time)
- php-fpm 有一个参数 max_requests ,该参数指明了每个 children 最多处理多少个请求后便会被关闭。在大量处理请求下,如果该值设置过小会导致 children 频繁的自杀和建立而浪费 大量时间,若所有的 children 差不多都在这个时候自杀,则重建前将没有 children 响应请求,于是出现 502 。可以将该值设置大一些或者是 0 [无限]。
简单来说,php-cgi 进程数不够用、 php 执行时间长、或者是 php-cgi 进程死掉,都会出现 502 错误。我们来演示一下
504 (网关超时) 服务器作为网关或代理,但是没有及时从上游服务器收到请求。
- nginx访问出现504 Gateway Time-out,一般是由于程序执行时间过长导致响应超时,例如程序需要执行90秒,而nginx最大响应等待时间为30秒,这样就会出现超时。
- 可能原因
1.程序在处理大量数据,导致等待超时。 2.程序中调用外部请求,而外部请求响应超时。 3.连接数据库失败而没有停止,死循环重新连。
出现这种情况,我们可以先优化程序,缩短执行时间。另一方面,可以调大nginx超时限制的参数,使程序可以正常执行。
-
解决方案 -
nginx.conf中,设置以下几个参数,增加超时时间 fastcgi_connect_timeout fastcgi连接超时时间,默认60秒 fastcgi_send_timeout nginx 进程向 fastcgi 进程发送请求过程的超时时间,默认值60秒 fastcgi_read_timeout fastcgi 进程向 nginx 进程发送输出过程的超时时间,默认值60秒 -
php.ini的配置 max_execution_time 设置单个请求的超时时间 php程序中可加入set_time_limit(seconds)设置最长执行时间 例如 set_time_limit(0) 表示不超时。 -
我们通过修改nginx的超时设置来演示一下504的情况
CDN
CDN (全称 Content Delivery Network),即内容分发网络,构建在现有网络基础之上的智能虚拟网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。CDN 的关键技术主要有内容存储和分发技术 通俗来讲,CDN 就像连锁店,当我们想吃沙县小吃的时候,我们不会真的跑到沙县去吃,而是附近就可以找到沙县小吃,而且全国的沙县小吃菜单都一样,每个沙县小吃就是一个边缘节点,那么我们怎么找到离我最近的沙县小吃呢,这个时候就用到了CNAME(配置过网站的同学都见过)和负载均衡系统,CNAME 就像你网站的别名,负载均衡系统会根据用户的IP 找到最近的边缘节点和运营商网络,并根据节点的负载情况和相应性能,分发到相应的节点
DNS
DNS(Domain Names System),域名系统,是互联网一项服务,是进行域名和与之相对应的 IP 地址进行转换的服务器,简单来讲,DNS相当于一个翻译官,负责将域名翻译成ip地址
- 计算机中DNS的记录也分成了两种缓存方式:
浏览器缓存:浏览器在获取网站域名的实际 IP 地址后会对其进行缓存,减少网络请求的损耗 操作系统缓存:操作系统的缓存其实是用户自己配置的 hosts 文件
查询过程
- 首先搜索浏览器的 DNS 缓存,缓存中维护一张域名与 IP 地址的对应表,
- 没有命中则继续搜索操作系统的 DNS 缓存
- 若仍然没有命中,则操作系统将域名发送至本地域名服务器,本地域名服务器采用递归查询自己的 DNS 缓存,查找成功则返回结果
- 若本地域名服务器的 DNS 缓存没有命中,则本地域名服务器向上级域名服务器进行迭代查询
- 本地域名服务器将得到的 IP 地址返回给操作系统,同时自己将 IP 地址缓存起来
- 操作系统将 IP 地址返回给浏览器,同时自己也将 IP 地址缓存起
- 至此,浏览器就得到了域名对应的 IP 地址,并将 IP 地址缓存起
|