12-浅析nginx

135 阅读15分钟

1、http报文结构

TCP:TCP头+数据部分

HTTP:也是header+body结构,具体而言就是:起始行+头部+空行+实体

起始行:

GET /home HTTP/1.1----方法+路径+http版本---请求报文 HTTP/1.1 200 OK-----http版本+状态码+原因----响应报文---该起始行也叫状态行

头部:(字段过多不一一介绍)

img

头部字段格式:

    1. 字段名不区分大小写
    1. 字段名不允许出现空格,不可以出现下划线_
    1. 字段名后面必须紧接着****:

空行:(用来区分头部和实体)

空行后的内容全部视为实体

实体:

就是具体的数据部分,请求报文对应请求体,响应报文对应响应体

2、http的请求方法

有哪些方法:

GET: 通常用来获取资源 HEAD: 获取资源的元信息 POST: 提交数据,即上传数据 PUT: 修改数据 DELETE: 删除资源(几乎用不到) CONNECT: 建立连接隧道,用于代理服务器 OPTIONS: 列出可对资源实行的请求方法,用来跨域请求 TRACE: 追踪请求-响应的传输路径

get\post的区别:

从缓存的角度,GET 请求会被浏览器主动缓存下来,留下历史记录,而 POST 默认不会。 从编码的角度,GET 只能进行 URL 编码,只能接收 ASCII 字符,而 POST 没有限制。 从参数的角度,GET 一般放在 URL 中,因此不安全,POST 放在请求体中,更适合传输敏感信息。 从幂等性的角度,GET是幂等的,而POST不是。(幂等表示执行相同的操作,结果也是相同的) 从TCP的角度,GET 请求会把请求报文一次性发出去,而 POST 会分为两个 TCP 数据包,首先发 header 部分,如果服务器响应 100(continue), 然后发 body 部分。(火狐浏览器除外,它的 POST 请求只发一个 TCP 包)

3、如何理解URI

URI:统一资源标识符,作用区分网络上不同的资源,类似身份证,表示资源的唯一性

URI:URL+URN

URI结构:

img

www.baidu.com/s?wd=HTTP&r… 这个 URI 中,https即scheme部分(协议名),www.baidu.com为host:port部分(主机名和端口) (注意,http 和 https 的默认端口分别为80、443), /s为path部分(请求路径),而wd=HTTP&rsv_spt=1就是query部分(查询参数)

4、http状态码的理解

1xx: 表示目前是协议处理的中间状态,还需要后续操作。(100post请求) 2xx: 表示成功状态。(200) 3xx: 重定向状态,资源位置发生变动,需要重新请求。(304协商缓存) 4xx: 请求报文有误。(405请求方法不被服务器允许,404资源未找到) 5xx: 服务器端发生错误。(500)

5、http的特点和缺点

特点:

灵活可扩展,主要体现在两个方面。一个是语义上的自由,只规定了基本格式,比如空格分隔单词,换行分隔字段,其他的各个部分都没有严格的语法限制。另一个是传输形式的多样性,不仅仅可以传输文本,还能传输图片、视频等任意数据,非常方便。

可靠传输。HTTP 基于 TCP/IP,因此把这一特性继承了下来。这属于 TCP 的特性,不具体介绍了。

请求-应答。也就是一发一收有来有回, 当然这个请求方和应答方不单单指客户端和服务器之间,如果某台服务器作为代理来连接后端的服务端,那么这台服务器也会扮演请求方的角色。

无状态。这里的状态是指通信过程的上下文信息,而每次 http 请求都是独立、无关的,默认不需要保留状态信息。

缺点:

无状态

所谓的优点和缺点还是要分场景来看的,对于 HTTP 而言,最具争议的地方在于它的无状态

在需要长连接的场景中,需要保存大量的上下文信息,以免传输大量重复的信息,那么这时候无状态就是 http 的缺点了。

但与此同时,另外一些应用仅仅只是为了获取一些数据,不需要保存连接上下文信息,无状态反而减少了网络开销,成为了 http 的优点。

明文传输

即协议里的报文(主要指的是头部)不使用二进制数据,而是文本形式。

这当然对于调试提供了便利,但同时也让 HTTP 的报文信息暴露给了外界,给攻击者也提供了便利。WIFI陷阱就是利用 HTTP 明文传输的缺点,诱导你连上热点,然后疯狂抓你所有的流量,从而拿到你的敏感信息。

队头阻塞问题

当 http 开启长连接时,共用一个 TCP 连接,同一时刻只能处理一个请求,那么当前请求耗时过长的情况下,其它的请求只能处于阻塞状态,也就是著名的队头阻塞问题。

6、Accept系列字段了解

Accept系列字段的介绍分为四个部分: 数据格式压缩方式支持语言字符集

img

7、定长和不定长数据,http怎么传输

定长:对于定长包体而言,发送端在传输的时候一般会带上 Content-Length, 来指明包体的长度。例如:res.setHeader('Content-Length', 10);只允许传递的数据只能为10个字节,超过10个就会被截取,例如res.write("1234567890");--显示1234567890,若res.write("12345678901234");---输出也是1234567890,后面的超出部分实际上在http的响应体中直接被截取了

不定长包体:http头部字段:Transfer-Encoding: chunked会进行数据的分块传输。设置了该字段后会有下面两个效果:基于长连接持续推送动态内容、Content-Length 字段会被忽略,例子:

const http = require('http'); const server = http.createServer(); server.on('request', (req, res) => { if(req.url === '/') { res.setHeader('Content-Type', 'text/html; charset=utf8'); res.setHeader('Content-Length', 10); res.setHeader('Transfer-Encoding', 'chunked'); res.write("

来啦

"); setTimeout(() => { res.write("第一次传输
"); }, 1000); setTimeout(() => { res.write("第二次传输"); res.end() }, 2000); } }) server.listen(8009, () => { console.log("成功启动"); }) //实现效果是,设置的Content-Length不起作用,即res返回的是什么就输出什么。 //效果2:打开地址首先看到来啦,隔1s后显示第一次传输,再隔1后显示第二次传输,实现长连接

实现长连接就是头部添加了Connection: keep-alive这个字段来维持长连接。

8、http如何处理大文件的传输

采取范围请求的方案,允许客户端仅仅请求一个资源的一部分。

服务器添加一个Accept-Ranges: none响应体来告诉客户端服务端支持范围请求

Range 字段拆解:看客户端指定的是那部分

9、http如何处理表单数据提交

在http有两种表单的提交方式(对post请求提交表单),体现为两种不同的Content-Type取值:

application/x-www-form-urlencoded multipart/form-data(主要采用的方式)

总结:multipart/form-data 格式最大的特点在于:每一个表单元素都是独立的资源表述。另外,你可能在写业务的过程中,并没有注意到其中还有boundary的存在,如果你打开抓包工具,确实可以看到不同的表单元素被拆分开了,之所以在平时感觉不到,是以为浏览器和 HTTP 给你封装了这一系列操作。

而且,在实际的场景中,对于图片等文件的上传,基本采用multipart/form-data而不用application/x-www-form-urlencoded,因为没有必要做 URL 编码,带来巨大耗时的同时也占用了更多的空间。

10、如何解决http1.1的队头阻塞问题

队头阻塞:http传输发送的时候,发送的任务被放在一个任务队列串行执行,一旦队首的请求处理太慢,就会阻塞后面请求的处理。

解决办法:

(1)并发连接,提高http请求的数量提高并发连接。相当于增加任务队列,增加队列数,在谷歌浏览器中最多并发6个连接。

(2)域名分片:多分几个域名,例如比如 content1.sanyuan.com 、content2.sanyuan.com。这样一个sanyuan.com域名下可以分出非常多的二级域名,而它们都指向同样的一台服务器,能够并发的长连接数更多了,事实上也更好地解决了队头阻塞的问题

11、cookie的了解

因为http的无状态协议,因为http是一发一回所以不需要维持状态浪费内存,正因为http的无状态协议,每次http请求都是独立的,默认不需要保留状态,但如果有需要保存状态的时候就使用到cookie了。

cookie本质上是浏览器存储的一个很小的文本文件,通过键值对的方式存储。对同一个域名下发送请求,都会携带相同的cookie,服务端拿到cookie进行解析,便能拿到客户端的状态。而服务端可以通过响应头的set-cookie字段对客户端写入cookie。

Cookie属性

(1)cookie有效期可以通过Expires和Max-Age(单位是秒,从浏览器收到报文开始计算)两个属性来设置。若cookie过期,则会把cookie删除并不会发生给服务器。

(2)作用域:Domain和path,是给cookie绑定域名和路径,若发送请求前,发现域名或者路径不匹配,就不会携带cookie。Domian主要是对某个域名绑定,例如对静态资源的域名不携带cookie等操作,只设置动态资源的域名就可以实现。

(3)安全相关:如果cookie带上secure说明是https请求才会携带cookie;若cookie带上httponly,说明不能通过js访问,防止xss攻击。sameSite属性是防止csrf攻击的,默认的值是none,请求会自动携带cookie,设置值为Strict时候,浏览器会禁止第三方请求携带cookie,比如sanyuan.com网站只能在sanyuan.com域名当中请求才能携带cookie,在其他网站请求都不能。

Cookie的缺点

(1)cookie的大小上限只有4kb,只能存储少量的信息

(2)性能缺陷:cookie紧跟域名,不管域名下某个地址是否需要cookie都会携带,这样就会导致请求数多的时候就会造成带宽的浪费。可以通过Domain和path指定作用域解决。

(3)安全缺陷:cookie以纯文本的形式在浏览器和服务器中传递,很容易被获取,然后进行一些改动。另外,在cookie的httponly为false的时候,可以通过js脚本获取cookie信息。

12、http代理

因为HTTP 是基于请求-响应模型的协议,一般由客户端发请求,服务器来进行响应。

但是特殊特殊情况就是代理服务器的情况:引入代理之后(正向代理),作为代理的服务器相当于一个中间人的角色,对于客户端而言,表现为服务器进行响应;而对于源服务器,表现为客户端发起请求,具有双重身份

http代理的作用

(1)负载均衡:根据随机算法、轮询、一致性hash等算法把请求分发到不同的原服务器中,减少服务器压力。

(2)保障安全:利用心跳机制监控后台服务器,一旦发现故障就会剔除。并且还有对数据过滤、非法ip限流等都是代理服务器的工作。

(3)缓存代理:将内容缓存到代理服务器,使得客户端可以直接从代理服务器获取。

13、如何理解http缓存和缓存代理

http缓存:

首先通过 Cache-Control 验证强缓存是否可用

  • 如果强缓存可用,直接使用

  • 否则进入协商缓存,即发送 HTTP 请求,服务器通过请求头中的If-Modified-Since或者If-None-Match这些条件请求字段检查资源是否更新

    • 若资源更新,返回资源和200状态码
    • 否则,返回304,告诉浏览器直接从缓存获取资源

代理缓存:

代理服务器接管一部分的服务端的http缓存,客户端缓存过期后就近到代理缓存中获取,代理缓存过期后才请求源服务器,降低源服务器压力。

Cache-Control: public, max-age=1000, s-maxage=2000 这个响应是允许代理服务器缓存的,客户端缓存过期了到代理中拿,并且在客户端的缓存时间为 1000 秒,在代理服务器中的缓存时间为 2000 s。

14、跨域是什么,浏览器如何拦截响应,如何解决

浏览器遵循同源策略(协议、主机和端口都相同视为同源),非同源有下面限制:

不能读取和修改对方的 DOM 不读访问对方的 Cookie、IndexDB 和 LocalStorage 限制 XMLHttpRequest 请求。

跨域请求:浏览器想目标URI发ajax请求时,只要当前URL和目标的URL不同源,就会产生跨域。

浏览器拦截响应:就是说浏览器主进程会发起请求,但是浏览器主进程会把服务端处理完的返回的响应体全部丢掉,不发给渲染进程。

解决跨域:

(1)cors:全程是跨域资源共享。需要浏览器和服务器的共同支持。

(2)jsonp:使用script标签的不跨越技巧,通过src填写目标地址从而发起get请求,实现跨域请求并拿到响应。jsonp的优势比cors兼容性好,缺点只支持ge请求。

(3)nginx:nginx是一种高性能反向代理服务器,可以轻松解决跨域问题。

img

正向代理:帮助客户端访问客户端访问不到的服务器,然后把结果返回给客户端。

反向代理:负责拿到客户端的请求,将请求转发给其他的服务器,主要的场景是维持服务器集群的负载均衡。

两者区别:正向代理是帮客户端做事情,反向代理是帮服务器做事情。

nginx解决跨域:

server { listen 80; server_name client.com;//客户端访问的地址 location /api { proxy_pass server.com;//代理的地址 } }

15、Http/2有哪些改进

http/2性能提升的两点:

(1)头部压缩:对于get请求,请求报文几乎全是请求头,存在优化空间,http2针对头部字段,采用对应的压缩算法(HPACK)。该算法的2个亮点:首先就是服务器和客户端之间建立哈希表,将用到的字段存放在这张表中,在传输的时候对之前出现的值,只需要把索引传给对方,对方根据索引查询表就可以获取对应的数据。这种传索引的方式,可以让请求头字段很大程度的精简和复用。其次就是对于证书和字符串进行哈夫曼编码,该编码原理就是先将所有出现的字符建立一张索引表,然后让出现次数多的字符对应的索引尽可能的短,传输的时候也是传输这样的索引序列,可以达到很高的压缩率。

(2)多路复用

(3)设置请求优先级

(4)服务器推送

多路复用:

解决队头阻塞:HTTP 队头阻塞的问题,其根本原因在于HTTP 基于请求-响应的模型,在同一个 TCP 长连接中,前面的请求没有得到响应,后面的请求就会被阻塞。后面使用并发连接和域名分片的方式解决,也没有从http本身层面解决问题,只是增加tcp连接分摊出现的几率而已。

二进制分帧:将报文全部换成二进制格式,全部传输01串,方便机器解析。原来的header+body的报文格式被拆分成一个个二进制的帧,用header帧存放头部字段,data帧存放请求体数据。分帧之后,服务器看到的不再是一个个完整的HTTP请求报文,而是一对乱序的二进制帧,服务器会根据每个帧的stream id来组装成一个完整的请求报文和响应报文。这些帧不存在先后顺序,因此不需要排队等待,解决了队头阻塞问题。

通信双方都可以给对方发送二进制帧,这种二进制帧的双向传输的序列,叫流。http2用流来在一个tcp连接上来进行多个数据帧的通信,这就是多路复用的概念。

服务器推送:服务器只能推送静态资源给客户端。

16、http2二进制帧的设计

img