web性能权威指南读书笔记

1 网络技术概览

1.1 延迟和带宽

  • 对所有网络通信都具有决定性影响的两个方面是延迟带宽
  • 总延迟包括传播延迟(与传输介质和传输距离有关)、传输延迟(消息中所有比特转移到链路中需要的时间,与消息的长度和链路速率有关)、处理延迟(处理分组所需要的时间)和排队延迟(分组排队等待处理的时间)

1.2 TCP

  • TCP三次握手过程:

    1. SYN 客户端选择一个随机序列号x,并发送一个SYN分组,其中还可能包括其他TCP标志和选项
    2. SYN ACK 服务器给x加1,并选择自己的一个随机序列号y,追加自己的标志和选项,然后返回响应
    3. ACK 客户端xy加1并发送握手期间的最后一个ACK分组
  • 客户端可以在发送ACK分组之后立即发送数据,而服务器必须等待接收到ACK分组之后才能发送数据,因此握手过程,都需要经历一次完整的往返过程,这带来了很大的延迟。TFO
    TCP快速打开)通过握手开始时的TFO cookie验证之前连接过的客户端,使其可以在三次握手最终ACK包收到之前就开始发送数据,能在一定程度上降低延迟。

  • 在分组交换网络中,当传输的分组数目过多时,会导致存储交换的节点缓冲区被迅速填满,发生网络拥塞现象。为预防拥塞崩溃这种情况,TCP加入了流量控制慢启动拥塞预防等机制。

    1. 流量控制是一种预防发送端过多向接收端发送数据得机制,TCP连接的每一方都要通告自己的接受窗口(rwnd),其中包含了能够保存数据的缓冲区空间大小。第一次建立连接,两端都会使用系统默认设置来发送rwnd,当某一端跟不上数据传输,将减小窗口值,每个ACK分组都会携带最新的rwnd值,以便两端动态调整流速。最初的TCP规范分配给通知窗口的字段为16位,这限制了窗口最大值为2^16(65535字节),通过TCP窗口缩放可以提升窗口字段位数。
    2. TCP建立连接时,并不知道可用带宽为多少,如果一开始就发送过多数据,可能造成拥塞,但若一直较少的传输数据,可能导致带宽得不到充分利用,因此需要一个估算机制,使传输速率逐步增高,这就是慢启动算法。发送端通过TCP连接初始化一个新的拥塞窗口(cwnd)变量,然后将其设置成一个保守的值,该变量为私有变量,发送端和接收端不会交换这个值,而TCP连接传输的最大数据量取rwnd和cwnd中的最小值,随着传输过程进行,每收到一个ACK,慢启动算法将增加cwnd窗口值1个TCP段,这个阶段通常为“指数增长,客户端和服务器都在向两者之间网络路径的有效带宽迅速靠拢。因此对于一些短暂、突发的连接,还没有达到最大窗口,请求就被终止了,这时慢启动就限制了可用的吞吐量,对于小文件的传输非常不利。TCP还具有SSR慢启动重启这种机制,其在连接空闲一段时间后会重置拥塞窗口,其对于那些会出现突发空闲的长周期TCP连接(例如http的keep-alive连接非常不利),建议禁止SSR。
    3. 拥塞预防算法把丢包作为网络拥塞的标志,介入调整窗口大小,其重置拥塞窗口后,按照自己的算法来增大窗口以避免丢包,如果某个时刻,又有数据包丢失,此时这个过程再从头开始,因此TCP连接的吞吐量曲线常为锯齿状。常用的算法包括AIMD(倍减加增,发生丢包时,先将拥塞窗口减半,每次往返再缓慢增加一个固定在),PRR(比例降速)
  • 队首阻塞 TCP在不可靠的信道上实现了可靠的网络传输,每个TCP分组都会带着唯一序列号被发出,而所有分组必须按照顺序传送到接收端,若中途有个分组丢失,则后续分组必须保存到接受端的TCP缓冲区,等待丢失的分组重发,必须等到全部的分组到达才能访问数据,因此就出现了队首阻塞。若应用程序无需按序交付数据/可以处理丢失,或对延迟和抖动要求很高,则建议使用UDP协议。

  • TCP优化建议:

    1. 增大TCP的初始拥塞窗口大小,在第一次往返时就传输较多数据
    2. 在连接空闲时禁用慢启动重启,改善瞬时发送数据的长TCP连接
    3. 启动窗口缩放,增大最大接收窗口大小
    4. TCP快速打开,减少握手阶段的延迟
    5. 使用CDN内容分发网络,把数据放在离客户端接近的地方
    6. 减少不必要的请求

1.3 UDP

  • 分组可以用来指代任何格式化的数据块,而数据报则通常只用来描述那些通过不可靠的服务传输的分组,即不保证送达,也不发送失败通知。
  • UDP用自己的分组封装用户消息,只增加4个字段:源端口(可选)、目标端口、分组长度和校验和(可选)。
  • UDP具有无服务的特点,包括:
    1. 不保证消息交付,不确认,不重传,无超时
    2. 不保证交付顺序,不设置包序号,不会发生队首阻塞
    3. 不跟踪连接状态,不建立连接
    4. 不需要拥塞控制
    5. 数据报封装在IP分组中,数据报不能分片
  • IP网络地址转换器(NAT)解决了IPv4地址数量耗尽的问题,每个NAT设备负责维护一个表,表中包含了本地内网IP和端口到外网IP和端口的映射。TCP连接具有明确的连接开始和终止状态,路由设备可以监控其状态来创建和删除路由表中的条目,但UDP协议本身没有连接状态,没有握手,也没有终止,NAT无法维护UDP的路由记录。因此,对于某些端到端的应用程序,其根本就无法建立UDP连接,因为内网中的客户端不知道外网的IP地址,其次,任何到达NAT设备外网IP的分组还必须有一个目标端口,NAT转换表也要有对应的条目将其转换为内部的IP地址和端口,如果没有这个条目,分组将被NAT设置删除。为了解决UDP协议和NAT这种情况,我们需要应用穿透技术(STUN、TURN、ICE等)。
  • STUN需要一个第三方的服务器以供应用程序查询获得外网IP和端口实现端到端通信,当两台主机需要UDP通信时,它们首先都会向各自的STUN服务器发送绑定请求,然后使用服务器返回响应中的外网IP地址和端口号交换数据。
  • TURN则依赖于外网中继设备在两端之间传递数据
  • ICE则是穿透技术的综合,其先尝试直连,若失败,则进行STUN协商,再不行使用TURN。

1.4 传输层安全TLS

  • TLS协议为在它之上运行的应用提供三个基本服务:加密身份验证数据完整性
  • TLS握手在于TCP连接建立之后,包括:
    1. 客户端以纯文本方式发送一些说明(TLS协议版本、支持的加密套件列表【密钥交换、数据加密、消息验证等算法】、其他TLS选项)
    2. 服务端取得TLS协议版本,从客户端提供的加密套件列表选择一个,再附上自己的证书,发送响应。服务器也可以发送一个请求,要求客户端提供证书以及其他TLS参数。
    3. 两端协商确定了共同的版本和加密套件后,客户端生成一个对称密钥,用服务器的非对称公钥加密后发送给服务器。
    4. 服务器解密出客户端发来的对称密钥,通过验证消息的MAC检测消息完整性,再返回客户端一个加密的“Finished”消息
    5. 客户端用之前生成的对称密钥解密消息,验证MAC,若一切顺利,则建立信道开始发送应用数据。
  • 应用层协议协商(ALPN)作为TLS协议的扩展,使我们能在TLS握手的同时协商应用协议(自定义协议等),从而省掉了HTTP的Upgrade机制所需的往返时间
  • 服务器名称指示(SNI)作为TLS协议的扩展,允许客户端再握手之初就指明要连接的主机名,通过主机名区分不同的域名,解决了服务器在一个IP地址为多个站点(每个站点都具有自己的TLS证书)提供服务的问题。
  • 会话标识符(客户端和服务器都保存会话ID,握手时通过识别ID进行简短握手)和会话记录单(服务端生成会话记录单发送给客户端,客户端将其在本地保存,下次握手时发送给服务端验证),这些机制称为会话缓存、无状态恢复,实现快速握手。
  • TLS通过证书信任链达到网站身份验证的目的
  • TLS的优化建议:
    1. 使用CDN内容分发网络
    2. 开启会话缓存和无状态恢复
    3. 禁用TLS压缩,因为可能出现安全和重复压缩问题
    4. 调节TLS记录大小,使每个TCP分组恰好封装一个TLS记录,避免出现记录过小,分帧被浪费,而记录过大,TCP多个分组传输延迟的问题。建议使用1400字节。
    5. 减少握手期间服务器发送的证书链长度,无需发送根证书颁发机构,因为浏览器已经内置。
    6. 服务器启用OCSP封套,减少客户端在线查询验证证书的请求。
    7. 追加HTTP严格传输安全首部(例如 strict-transport-security: max-age=31536000),使所有对原始服务器的访问请求都使用HTTPS

2 无线网络性能

  • 无线技术数据传输速度最直接相关的就是接收端和发送端之间的可用带宽和信号强度,要增强信号强度,要么增大发送功率,要么提高信号强度
  • Wi-Fi可以用来指称任何基于IEEE802.11标准的产品,其工作于2.4GHz ISM频段
  • 移动网络的优化建议:
    1. 开发移动应用时,应尽量少用无线电接口
    2. 消除周期性及无效的数据传输,应尽可能使用推送和通知,而不是轮询
    3. 消除不必要的长连接,因为这可能导致无线电模块长时间处于活跃状态,从而极大得消耗电量
    4. 移动端无线接口为爆发性传输做过优化,因此应该尽可能多和快地下载数据,然后让无线模块转换为空闲,而不是流式下载。

3 HTTP

3.1 HTTP 1.x

  • 优化建议:
    1. CSS和JS等重要资源应该尽早在文档中出现
    2. 非关键的JS应该推迟,避免阻塞DOM和CSSOM的构建
    3. 使用link标签预解析特定的域名、预获取重要资源、预渲染特定页面等
    4. 减少DNS查询、HTTP请求
    5. 使用CDN
    6. 资源添加Expires首部并配置ETag标签
    7. Gzip压缩文本资源
    8. 避免HTTP重定向
    9. 减少要传输的首部数据和cookie
  • 大多数浏览器,都支持每个主机打开6个TCP连接,WebSocket、SSE和挂起XHR都会占用整整一个TCP流
  • 由于一个主机最多同时发起6个连接,我们可以将资源分散到多个子域名,但额外的域名分区会因为额外的DNS查询和TCP慢启动而影响性能
  • 将多个js或css文件、图片进行拼合,但同时该操作牺牲了模块化和缓存粒度,一点更新都需要重新下载整个文件,增大的开销。除此之外,内存占用也会存在问题,因为拼合的大图片一直保存在内存中。
  • 对于通过数据URI嵌入文档的方式只适用于特别小的资源,其不能被浏览器、CDN等进行缓存,且若非文本资源,还需进行base64编码,导致编码后资源大小增加。

3.2 HTTP 2.0

  • HTTP2性能增强的核心在于新增的二进制分帧层,HTTP1.x以换行符作为纯文本的分隔符,而HTTP2则将传输信息分割为更小的消息和帧,并对它们采用二进制格式的编码。所有HTTP2通信都在一个持久连接上完成,这个连接可以承载任意数量的双向数据流。每个数据流以消息的形式发送,而消息由一个或多个帧组成,这些帧可以乱序发送,然后再根据每个帧首部的流标识符重新组装。
  • 帧是HTTP2通信最小单位,所有帧都会被二进制编码,所有首部数据都会被压缩,类型包括HEADER帧DATA帧
  • 由于HTTP2二进制分帧机制解决了HTTP1.x的队首阻塞问题,消除了并行处理和发送请求和响应时对多个连接的依赖,支持多向请求与响应,因此对于HTTP1的一些优化措施,例如拼接文件、图片精灵、域名分区等均可以不用再使用。
  • HTTP2每个流都可以携带一个31比特的优先值,服务器可根据请求流的优先级进行差异化处理和分配资源,但若规定严格的优先级可能会造成队首阻塞效应。除此之外,HTTP2还具有流量控制机制,虽然机制与TCP流量控制类似,但前者可以对同一条HTTP2连接内的多个流实施差异化策略。流量控制基于窗口更新帧进行,通过WINDOW_UPDATE帧更新,指定流ID和窗口大小递增值。接受方可以根据自己情况为每个流至整个连接设置任意窗口大小,也可以针对个别流或整个连接禁用流量控制。
  • 在HTTP2中,服务器可以对一个客户端的请求发送多个响应,因此可以额外地向客户端推送资源。客户端与服务器交换SETTING帧来限定双向并发流的最大数量,以此来开启或禁用服务器推送。这种服务器推送资源的方式具有资源可缓存、资源页面共享、资源优先级推送等多项优点,除此之外客户端还可以通过判断PUSH_PROMISE帧来选择是否拒绝这个推送流。而HTTP1.x中的内嵌资源类型于强制推送,客户端无法取消。
  • HTTP2在客户端和服务器使用“首部表”来跟踪和存储之前发送的键值对,对于相同的数据,不再通过每次请求和响应发送。首部表在HTTP2连接期间一直存在,由客户端和服务器共同更新。
  • HTTPS在握手时通过ALPN来协商是否升级到HTTP2,常规非加密HTTP则通过Upgrade机制来协商升级。
  • 所有HTTP2的帧都具有一个8字节的首部,其中包含帧的长度、类型、标志,还有一个保留位和一个31位的流标识符。在发送应用数据之前,必须要先建立流并发送相应的元数据,比如流优先级、HTTP首部等。客户端通过发送HEADERS帧来发起新流,而服务器则发送PUSH_PROMISE帧来发起推送流。客户端发起的流具有奇数ID,而服务器则是偶数ID。应用数据可以分割为多个DATA帧,最后一帧要翻转帧首部的END_STREAM字段

3.3 优化应用的交付

  • 减少DNS查询,每次查询都需要一次网络往返
  • 重用TCP连接,尽可能使用持久连接,消除握手和慢启动延迟
  • 减少HTTP重定向,特别是不同域名之间的重定向
  • 使用CDN网络
  • 去掉不必要的资源
  • 在客户端缓存资源,使用Cache-controlLast-modifiedETag首部
  • 传输压缩后的数据
  • 消除不必要的请求开销,减少HTTP首部数据(例如cookie)
  • 并行处理请求和响应
  • 升级到HTTP2
  • 服务器的初始cwnd应该是10个分组
  • 服务器应该通过ALPN(应用层协议协商)支持TLS
  • 服务器应该支持TLS会话缓存以最小化握手延迟
  • 利用服务器推送

4 浏览器API与协议

4.1 浏览器网络概述

  • 浏览器把请求管理生命周期与套接字管理分开,以套接字池的方式管理一系列套接字,每个套接字池中包含一组同一个来源的套接字(应用协议、域名、端口相同则为同一来源),每个池最大规模都是6个套接字。浏览器自动化的套接字管理会重用TCP连接,保障性能。
  • 浏览器不允许直接访问原始网络套接字API,其同源策略、TLS协商、最大连接数限制措施等有效地保证了网络安全。
  • 浏览器为每个来源都维护着独立地cookie容器,为提供会话认证等提供便利

4.2 XMLHttpRequest

  • XHR可以通过分割二进制Blob将大文件切割成小文件分多次发送。
  • XHR可以进行短轮询(setInterval)和长轮询(递归函数)

4.3 SSE服务器推送事件

  • SSE服务器推送事件以流式HTTP响应交付,客户端发起正常的HTTP请求,服务器以自定义的text/event-stream内容类型响应,然后交付UTF-8编码(不支持二进制)的事件数据,传输的事件载荷就是一个或多个相邻的data字段值,除此之外事件还可以携带id和event字段来标识事件达到断线重连的目的。SSE局限性: 只能是服务器向客户端发送数据,只能传输UTF-8数据

4.4 Websocket

  • websocket实现客户端和服务器双向、基于消息的文本或二进制数据传输,是浏览器中最接近套接字的API。ws表示纯文本通信(如ws://example.com/socket),wss表示使用加密信道通信。
  • websocket协议不做格式假设,其只关注消息的两个信息:净荷长度和类型(utf-8或二进制)。浏览器收到新消息后,会自动将其转化为DomString(文本数据),或者转化为Blob或ArrayBuffer(二进制数据)。
  • Blob对象一般代表一个不可变的文件对象或原始数据,而ArrayBuffer表示一个普通的、固定长度的二进制数据缓冲。如果你需要对二进制数据进行处理(例如切分、解码等),则应该选择后者。
  • websocket协议包含两个高层组件,开放性HTTP握手用于协商连接参数,二进制消息分帧机制用于支持低开销的消息文本和二进制数据传输。websocket本身不支持多路复用(消息没有流ID),但扩展协议可以使每个封装帧加上信道ID标识,实现多个虚拟的websocket信道共享一个TCP连接。没有进行多路复用的websocket在发送大消息时,容易造成队首阻塞问题。
  • 与浏览器中客户端发起的任何连接一样,websocket请求也必须遵守同源策略
  • websocekt优化点:
    1. 使用基于TLS的WSS部署websocket
    2. 优化二进制载荷以最小化传输数据
    3. 压缩UTF-8传输内容
    4. 设置正确的二进制类型接受二进制载荷
    5. 切分应用消息

4.5 WebRTC

  • webRTC由一组标准、协议和js API组成,用于实现浏览器之间端到端的音频、视频及数据共享。
  • webRTC主要包括三个API
    1. MediaStream: 获取音频和视频流
    2. RTCPeerConnection: 音频和视频数据通信
    3. RTCDataChannel: 任意应用数据通信
  • 初始共享发送信道,通过MediaStreamgetUserMedia从底层平台取得音频和视频流,使用RTCPeerConnectionaddstream注册本地流,并通过createOffer生成SDP会话描述符,两端交换各自生成的描述符。连接建立,开始音视频通信。

猜你喜欢

转载自blog.csdn.net/zjw_python/article/details/105971449