URI, 全称为(Uniform Resource Identifier), 也就是统一资源标识符。它并不是我们常说的网址URL
, 实际上URI
= URN
+URL
。
它主要由以下几个结构组成:
scheme 表示协议名,比如http
,https
等
user:passwd@ 表示登录主机时的用户信息
host 主机名
port 端口
path 资源路径
query 表示查询参数,通常以?
开头多个值之间以&
连接
fragment 页面资源锚点位置,常见为:https://www.baidu.com/path/index.html#name
URI
只能使用ASCII
, ASCII
之外的字符是不支持显示的。此外,URI
引入了编码机制,将所有非ASCII
码字符和界定符转为十六进制字节值,然后在前面加个%
,例如:空格被转义成了%20
。
HTTP状态码总体分为五类:
整体范围 | 已定义范围 | 含义 |
---|---|---|
100~199 | 100~101 | 信息提示 |
200~299 | 200~206 | 成功 |
300~399 | 300~305 | 重定向 |
400~499 | 400~415 | 客户端错误 |
500~599 | 500~505 | 服务器错误 |
接下来详细看下每个状态码的含义:
100 请求中间状态,客户端需要继续下一步请求。 101 协议切换。例如当HTTP请求升级到websocket服务的时候,如果服务端同意升级,则会返回101状态码。
200 请求成功。请求中最常见的状态码,也是开发最喜欢的状态,表示请求一切正常。 201 成功请求并创建了新的资源。 202 服务端已接受请求,但未完成处理。 204 没有内容。表示只返回head头信息,主体内容没返回。 206 表示部分内容。常见于大型文件上传或下载的过程。
301
永久重定向。将资源永远的移到新的域名下。
302
临时重定向。将资源临时移到新地方,以后还有可能再移回来。
备注:301、302常见于SEO优化或http缓存。以http缓存为例:若永久重定向到新资源,则浏览器会缓存新资源,并抛弃老域名的缓存。若临时重定向则不做缓存优化。
304
常见于http缓存。例如 max-age
时间过期,命中协商缓存的时候,浏览器会携带相关信息到服务端,服务器对比信息后发现文件没更新可以继续使用的情况下就会返回304状态码,意思是告诉客户端文件可以继续使用。
305
使用代理。表示当前请求的资源必须通过代理访问。
307
临时重定向,与302类似。
400 开发者请求错误。比较粗糙的提示,具体错误原因不明。 401 要求请求方,也就是用户需要进行身份认证。 403 服务器拒绝访问。因法律、敏感词汇等原因,服务器拒绝客户端的请求。 404 资源未找到。这个很容易理解,也比较常见,服务端没有对应的资源内容的时候会返回此状态码。 405 请求方法错误。比较常见于服务端只支持POST请求,但是开发者用GET方式请求了接口,就会返回这个错误。反之亦然。 406 服务端资源因某种原因无法满足客户端的请求条件。 407 与401类似,要求代理的身份认证。 408 客户端发送请求时间超时,服务器没有时间继续等待了。 409 服务器处理内容的时候发生了冲突。 410 类似404,表示资源以前可能存在,现在被删除了。 413 客户端请求体过大,服务器因无法处理而拒绝。 414 请求的URL地址太长,服务器无法处理。
500 服务器内部错误,具体啥错误咱也不知道,只知道是出错了。 501 表示服务器不支持客户端的请求功能。 502 作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应。 503 服务器内部较忙,暂时无法相应。 504 充当网关或代理的服务器,未及时从远端服务器获取请求。 505 服务器不支持请求的HTTP协议的版本,无法完成处理。
从本质上来说,cookie就是存储在客户端的一小段文本,以键值对的方式存储在浏览器里。通常浏览器只会分配4KB
的空间给cookie,所以存储内容较少。通常js可以设置、读取、删除cookie,服务端也可以通过Set-Cookie
头字段对浏览器进行Cookie
写入。若服务端设置属性为http-only
,客户端js脚本则无法读取到Cookie
值。
以下是百度网站的一段Cookie
截图:
属性说明:
Name Cookie名称
Value Cookie内容
Domain 可以读取当前Cookie的有效域名
Path 可以读取当前Cookie的有效路径
Expires/Max-Age Cookie有效绝对时间/过期相对时间(Session代表当前会话期间,网页关闭即失效)
Size Cookie大小
HttpOnly 当前Cookie是否只能通过http获取,携带到服务端,js脚本无法获取
Secure 是否只能通过https传输Cookie
SameParty Cookie安全模式,支持三个值:1、None
,默认模式,http请求默认会带上Cookie值。2、Lax
模式,只能再GET方法中携带Cookie。3、Strict
,严格模式,只能在当前域名下访问Cookie,其他域名均无法访问。
Priority 优先级,chrome的提案,定义了三种优先级,Low/Medium/High,当cookie数量超出时,低优先级的cookie会被优先清除
我们通常通过Expires
和Max-Age
这两个属性来设置Cookie
的过期时间。前者指定过期的具体时间,后者是过期的相对时间。若过期,浏览器不会主动删除Cookie
内容,只是无法被读取到或者无法发送到服务端。
涉及到两个关键词汇:1、域名(Domain)。2、路径(Path)。不同域名或路径下无法相互访问Cookie
内容,当然顶级域名下的Cookie
在子域名之间是共享的,以百度网站为例:.baidu.com
下的cookie可以共享给子域名访问,例如www.baidu.com
、tieba.baidu.com
等。根路径(/
)下的cookie也可以共享给任何子目录访问。
Cookie
是以字符串的方式存储在客户端,以明文的方式在服务器与客户端之间传输,容易被拦截修改。不论当前请求是否需要Cookie
,请求都会携带上当前域名下的所有Cookie
,所以会产生大量不必要的数据,影响服务端性能以及带宽。可以通过设置Cookie
的Domain
以及Path
适当控制携带的数量。
除了Cookie
存储之外,现代浏览器还支持localStorage
、sessionStorage
存储,两者的主要区别在于前者是永久存储在浏览器,后者的生命周期是会话级别的,网页关闭即消失。还有indexDB
,感兴趣的同学可参考阮老师的 浏览器数据库 IndexedDB 入门教程
相信现在绝大多数公司采用的都是前后端分离的开发模式,通过Ajax
请求响应数据,当前访问网页的URL与接口地址用的不是一个域名。这个时候浏览器就会认为是跨域了,存在一定安全隐患。详细来说就是请求网页与接口之间只要满足以下任一条件,即被认为跨域:
因为浏览器遵循同源策略
,当请求与响应不同源的时候,浏览器会试图先发送一个OPTIONS
预请求给服务端,同时会加上Origin
源地址和Host
目标地址,询问是否允许跨域,也就是看http返回头字段Access-Control-Allow-Origin
,若是*
表示允许任何请求,也可以是指定的请求源地址。若允许则继续,否则浏览器会中断后续请求并打印错误。注意是被浏览器拦截了,请求是到达服务端了。服务端发号施令,控制是否允许跨域,而浏览器是实际执行者。
JSONP实现的原理:虽然XMLHttpRequest
对象遵循同源政策,但是script
标签不一样,它可以通过 src
填上目标地址从而发出 GET 请求,实现跨域请求并拿到响应。接下来我们封装一个简单的JSONP:
let myJSONP = (_url,params,callback) => {
let splitUrl = (_url,params) => {
let str = '';
for(let key in params){
str += key+'='+params[key]
}
return _url + '?' + str
}
return new Promise(resolve => {
let _srcipt = document.createElement('script');
_srcipt.src = splitUrl(_url,params);
document.body.appendChild(_srcipt);
window[callback] = res => {
resolve(res)
//渣男,用完即删除
document.body.removeChild(_srcipt)
}
})
}
让运维同学在Nginx
中做反向代理,将所有请求中转一下,让服务端识别请求,以为是来自同源的请求,予以通过。
比如说现在客户端的域名为client.com,服务器的域名为server.com,客户端向服务器发送 Ajax 请求。Nginx
配置方式如下:
server {
listen 443;
server_name client.com;
location /yourApiAddress {
proxy_pass server.com;
}
}
Nginx
在其中相当于跳板机的作用。
开发同学代码中CORS
设置允许任何来源即可。个人推荐这种配置,基本上一劳永逸,Nginx
代理是运维同学设置的,经常遇到服务器调整等原因导致跨域反反复复,比较烦人。
除此之外就是一些奇淫技巧的跨域解决方案,例如postMessage
、iframe
等等,现实中基本上不会用到。就不建议深入了解了,记个大概即可。
HTTP1.0:GET
、POST
、HEAD
HTTP1.1:PUT
、PATCH
、DELETE
、OPTIONS
、TRACE
、CONNECT
方法 | 描述 |
---|---|
GET | 获取资源 |
POST | 资源传输,通常会修改服务器资源 |
HEAD | 获得请求头信息 |
PUT | 更新资源 |
DELETE | 删除资源 |
PATCH | 对PUT的补充,对已知资源部分更新 |
OPTIONS | 列出请求资源支持的请求方法,用来跨域请求 |
TRACE | 追踪请求/响应路径,用于测试或诊断 |
CONNECT | 将连接改为管道方式用于代理服务器 |
User-Agent 浏览器信息,内容包含发出请求的用户信息。
Cookie 当前域名下所有能读取到的Cookie
内容,不论服务器是否需要,都会被携带。
Accept 浏览器可接受的MIME类型。
Accept-Charset 浏览器可接受的字符集。
Accept-Encoding 浏览器可接受的压缩编码方式,常见的压缩方式有gzip
、br
、Servlet
Accept-Language 浏览器能接受的语言。
Content-Length 表示请求消息正文的长度。
Authorization 授权信息,通常出现在对服务器发送的WWW-Authenticate头的应答中。
Host 客户端请求域。
If-Modified-Since 对应 response返回值 last-Modified
文件最后修改时间。
If-None-Match 对应 response返回值 Etag
文件唯一标识。
Cache-control 详见下面http缓存一栏,会详细讲
Content-encoding 压缩编码方式,对应 Accept-Encoding
Content-Length 表示内容长度。
Content-Type 表示后面的文档属于什么MIME类型
Date 当前的GMT时间。
Expires 告诉客户端缓存的绝对时间(目前基本上已不再使用)
Last-Modified 文件最后编辑时间
Etag 当前文件内容唯一标识,若被重新修改则会有变动。
Server 服务器通过这个头告诉浏览器服务器的类型。
在介绍三次握手之前,先抛出一个问题:为什么是三次握手,不是两次或者四次?
首先我们要先理解握手的目的是什么?为了确保双方的发送、接收能力正常。
就像打电话之前,双方先“喂喂喂,听得见嘛”,一个道理。
最开始客户端处于CLOSED
状态,服务器处于LISTEN
状态(随时等待被撩)。
第一握手: 客户端首先发送一段SYN
报文,包含生成的序列号,这个时候客户端处于发送后等待
状态。
第二次握手: 服务端收到客户端发送的SYN
报文后,将序列号+1
后作为ACk
应答码返回给客户端,一并返回的包括服务端生成的序列号。这个时候服务端就处于半连接
状态,服务端也能确认了客户端的发送能力正常
,自己的发送能力
、接收能力
正常,唯独不知道客户端的接收能力是否正常。所以说二次握手并不能满足实际需要。
第三次握手: 客户端接收到服务端返回的ACK
以及序列号后,将序列号+1
作为ACK
码再返回给服务端。这个时候客户端就能确认自己的发送
、接收
能力正常,服务端的发送
、接收
能力也正常。服务端接收到ACK
码之后,将之前的半连接转换成全连接,也就完成了握手连接。
所以,三次握手正好确认双方的发送
、接收
能力正常了,再多一次可以嘛?其实也可以的,只是无用,白白浪费了而已,所以三次足以。
备注:三次握手过程中,前两次不可携带数据,第三次握手可以携带数据。因为前两次若能携带数据的话,攻击者会利用这个缺陷发送大量数据给服务端,而故意忽略第三次,从而导致服务端产生大量半连接
状态会话,同时服务器要耗费更多内存去处理数据,半连接
多了,达到一定数量后影响正常连接
,导致丢包等事件发生,故而产生SYN
攻击。第三次握手是可以携带数据的,因为第三次握手的时候,已经建立了正常连接,互相信任了,这个时候处理数据也无可厚非。
有握手的过程,必然就会有分手的过程嘛。四次的过程是这样的:
FIN
报文,其中会包含客户端生成的序列号。这个时候客户端处于FIN-WAIT-1
状态,也就是说这个时候客户端处于只能接收、不会再发送的状态。CLOSED-WAIT
状态,但是还有未处理完的数据要继续处理。客户端收到服务端反馈后变成FIN-WAIT2
状态。FIN
,自己进入LAST-ACK
状态。FIN
后,自己变成了TIME-WAIT
状态,然后发送 ACK
给服务端。
备注:第三次挥手后,服务端会间隔一定时间发送报文给客户端,确保客户端收到报文。只有当收到客户端第四次挥手返回的信息,才不会发送。所以第四次挥手后,客户端需要等待足够长的时间(2MSL
),也就是报文最大生存时间,来确保没有再收到服务端返回的信息,只要没收到信息,客户端就能确保服务端收到第四次握手信息了,服务端也确保第三次握手也是成功的了。缓存是性能优化中非常重要的一环,浏览器的缓存机制对开发也是非常重要的知识点。接下来以三个部分来把浏览器的缓存机制说清楚:
关于什么时候命中强缓存
,什么时候命中协商缓存
,主要依赖于http
返回头信息里的Cache-Control
返回字段进行控制。
什么情况下命中强缓存?以常见的max-age
为例:若max-age=5356800
,则意思是说浏览器在首次请求这个文件的时候,可以在本地磁盘中保存这个文件5356800秒
,从首次加载时间开始以后的这些时间内,若再次请求这个文件,浏览器可以不发起http
请求,而直接使用缓存中的内容即可,这样不仅节省了服务器负担,也提升网站用户体验。
什么情况下命中协商缓存?仍然以常见的max-age
为例:若max-age=5356800
,距离第一次请求文件已经过去了5356800秒
,当用户再次需要这个资源的时候,浏览器就会拿之前这个文件返回的Etag
以及Last-Modified
,分别命名为If-None-Match
和If-Modified-Since
作为请求参数传给服务端,服务端与现有文件进行匹配,若文件已更新,则返回200
状态码,同时返回最新文件给客户端。若文件未更新,则返回304
状态码,告诉客户端缓存有效,可以继续使用。
关于文件缓存位置,可通过Chrome
开发面板中,Network
栏目下每个文件后面有一个Size
属性查看
xxkB
。(memory cache)
。(disk cache)
。public 公共的。表示任何人都可以缓存,包括浏览器、代理服务器等。
private 私有的。表示只允许单个用户缓存,不能作为共享缓存。
no-cache 本地可以缓存,但每次都需要与服务端协商,即每次都命中协商缓存。
no-store 不适用任何缓存。
max-age=xxx 设置缓存最大周期(单位秒),超过这个相对时间被认为是过期,需要与服务端协商是否还可继续使用。与Expires
不同,因为它是绝对时间,目前基本上已被废弃,因为若服务器时间与客户端时间相差较大的情况下,会产生一些异常问题。
s-maxage=xxx 设置代理服务器缓存文件过期相对时间,客户端会忽略它。
must-revalidate 一旦资源过期(比如超过max-age),在成功向原始服务器验证之前,缓存不能用该资源响应后续请求。
no-transform 不得对资源进行转换或转变。
only-if-cached 表明客户端只接受已缓存的响应,并且不要向原始服务器检查是否有更新的拷贝。
其实这也不能说是缺点,这算是它的特点之一,至于是缺点还是优点,要分场景来看的。那么什么是无状态呢?就是指通信过程的上下文信息,每次请求都是独立、互相无关的,且默认状态不需要保留状态信息的。但是在一些场景下,比如前一次请求需要与后一次请求有一定关联,这个时候无状态
处理起来就比较麻烦了。
HTTP/1.1 的首部无法压缩,再加上 cookie 的存在,经常会出现首部大小比请求数据大小还大的情况。
也就是协议里的报文,即传输头信息,不使用二进制,而是使用文本的形式传输。这就给中间拦截者造成可乘之机,拦截传输内容,获取敏感信息。
http
传输是基于请求-应答
的模式进行的,报文必须是一发一收,所有任务被放到一个任务队列中串行执行。当http
开启长连接的时候,当前域名下会共用一个TCP
连接,一旦队首因某些原因卡住,后续只能处于等待状态,这就是著名的队头阻塞
问题。
那么如何解决队头阻塞问题?在不升级到HTTP2.0
的前提下,有两种解决方案:
static1.baidu.com
、static2.baidu.com
等多个域名加载网站资源,每个域名再分配多个长链接,就可以相对解决队头阻塞
问题。注意:域名也不是越多越好,因为每多一个域名,都要涉及到DNS查找
、TCP三次握手
、SSL/TLS
握手,且TCP
有一个慢启动特点,就是刚开始传输慢,慢慢的才会达到一个较快的稳定速度,如果启用了一个单独域名,而这个域名只传输了一个2KB
的js文件,实际文件传输时间远远小于握手
时间,就得不偿失了。具体哪个地方耗时可以在Chrome
开发面板下Network
中查看具体文件,其中有一个Waterfall
内容,截图示例如下:
名词解释
HTTP1.1
时代,默认情况下前后两次HTTP
请求没有关联,这被称为无状态
,请求以及响应头信息里有很多字段(本文之前已介绍过),不论是否需要,都会以key:value
的形式在网络中传输,尤其对于GET
请求,请求报文几乎全是请求头,这个时候就存在非常大的优化空间。HTTP2.0
就针对这个问题采用了HPACK
压缩算法对头部内容进行压缩,大大减少了网络传输。
HPACK
压缩算法的特点是啥?在服务端与客户端建立一个哈希表,以索引的形式代替头信息,双方传输就以索引为准,拿到索引值后到哈希表中寻找对应头内容,以此减少网络传输。
上面讲到了HTTP1.1
队头阻塞的问题,虽然采用了长连接以及多域名分片方法在一定程度上规避里队头阻塞
,但并没有从根本上解决问题。比如在有限的带宽情况下,如何完成优先级较高的请求,而不是一定要按照排队
的顺序。
HTTP2.0
是以二进制分帧
的方式来解决所谓的队头阻塞
问题。将原来的Headers
、Body
的报文解析成二进制的帧数据,传输过程中看到的是10101
的格式。这些二进制帧不存在先后关系,因此也就不会排队等待,也就没有了 HTTP 的队头阻塞问题。
注意:所谓的乱序,指的是不同ID
的Stream
是乱序的,但同一个Stream ID
的帧一定是按顺序传输的。二进制帧到达后对方会将Stream ID
相同的二进制帧组装成完整的请求报文和响应报文。
我们知道在目前的HTTP1.1
时代,服务器是被动的
,只有客户端主动发起请求,服务端才会响应对应内容,是一问一答的模式。并不会主动推送内容给客户端,除非采用websocket
。而HTTP2.0
就增加了服务器推送
特性,可以对客户端的一个请求发送多个响应。比如客户端请求html
页面的时候,服务器可以将当前页面用到的js
以及css
等资源一并返回给客户端,减少后续交互。
当然,客户端也可以拒绝服务器的推送。
服务器解析 HTTP1.1 的请求时,必须不断地读入字节,直到遇到分隔符 CRLF 为止。而解析 HTTP2 的请求就不用这么麻烦,因为 HTTP2.0
是基于帧的协议,每个帧都有表示帧长度的字段。
HTTP2.0
可以对比较紧急的请求设置一个较高的优先级,服务器在收到这样的请求后,可以优先处理。
HTTPS
是超文本传输安全协议,加强版的HTTP
,在HTTP
的基础上对headers
、body
进行加密后再传输,大大增加了安全性。简单的讲,HTTPS = HTTP + SSL/TLS
。
至于为何安全,简要的说就是在之前HTTP
明文传输的基础上增加了对称加密
、不对称加密
算法。至于加密的原理及过程,需要额外一篇文章来详细讲解,感兴趣的同学可查阅相关资料,这里不再赘述。
与前端相关的另外一个HTTPS
知识点就是:HTTPS
在HTTP
握手的基础上又增加了一层SSL/TLS
握手。所以安全是要有一定代价的,代价就是在真正连接前又多了个握手的过程。握手的过程这里不再赘述,感兴趣的同学可查阅相关资料。主要分以下三类:
HTTP
是明文传输,不安全的,HTTPS
是加密传输,安全的多。HTTP
标准端口是80
,HTTPS
标准端口是443。HTTP
不用认证证书免费,HTTPS
需要认证证书要钱。HTTP
三次握手,HTTPS
中TLS1.2
版本7次,TLS1.3
版本6次。HTTP
在OSI
网络模型中是在应用层,而HTTPS
的TLS
是在传输层。HTTP
是无状态的,HTTPS
是有状态的。目前通常意义上的代理,一般认为是CDN
节点,就是架设在客户端与原服务器之间的服务器,对客户端来说它就是服务器,对服务器来说它就是客户端。主要起到负载均衡
的作用,减少原服务器的压力,分担原服务器带宽,提高用户访问资源速度,提高用户体验。
默认情况下,所有资源都会向原服务器请求。这不仅会给原服务器造成较大压力,还会因距离问题让客户端等待较长时间,例如服务器在美国,中国用户首次请求要跨越大半个地球,显然比从上海服务器请求更慢。这个时候对于原服务器来说,就需要个代理
,这个代理服务器就被称为CDN
节点,节点
按照一定规则定期向原服务器更新文件即可,附近的用户就近访问当前节点即可,既减轻了原服务器压力,也减少了请求时间,提高用户体验。
是一个能用首部,由代理服务器添加,适用于正向和反向代理,在请求和响应首部均可出现,这个消息首部可以用来追踪消息转发情况,防止循环请求,还可以识别在请求或响应传递链中消息发送者对于协议的支持能力。
Via: 1.1 vegur
Via: HTTP/1.1 GWA
Via: 1.0 fred, 1.1 p.yourAddress.net
是一种获取用户真实IP
的字段,不管中间经过多少代理,这个字段始终记录最初的客户端的IP
。
相应的,还有X-Forwarded-Host
和X-Forwarded-Proto
,分别记录客户端(注意:不包括代理)的域名和协议名。
一般记录真实发出请求的客户端的IP
,还有X-Forwarded-Host
和X-Forwarded-Proto
分别记录真实发出请求的客户端的域名和协议名。
其实能看到这里,相信你的心里已经对网络性能优化有一定的想法了,这里也就做一些简单的知识点罗列。
Cache-Control
的max-age
设置较大值,例如6个月,尽可能保持一直缓存,文件更改的时候还一个hash
文件名即可。Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8