个人总结:阅读完这篇文章需要20分钟,这篇文章主要讲解了现代浏览器在网络层传输所用到的一些技术,

应当对 window.performance.timing 这个API所有了解。

这是 JavaScript 工作原理的第十二章。

正如在之前关于渲染引擎的文章中所讲的那样,我们相信好的和伟大的 JavaScript 开发者之间的差别在于后者不仅仅只是理解了语言的具体细节还了解其内部构造和运行环境。

网络简史

49 年前,ARPAnet 诞生了。它是早期的报文分组交换网络及第一个实现 TCP/IP 协议套件的网络。该网络连通了加利福亚大堂和斯坦福研究所。20 年后,Tim Berners-Lee 分发了一个后来为人所熟知的万维网的 『Mesh』草案。在 49 年的时间里,网络走过了一段漫长的旅程,从仅仅只是是两台电脑间交换数据报文到至少 7500 万台服务器,38 亿人使用互联网以及 13 亿个网站。

本文将试着分析现代浏览器使用哪些技术来自动提升应用性能(有些你甚至不了解),然后着重介绍浏览器网络层。最后,提供一些让浏览器提升网络应用程序性能的技巧。

概述

现代浏览器专门为快速,高效和安全数据传输的网络应用/网站而设计开发的。拥有数以百计的组件运行于各个不同的层级,从进程管理和安全沙箱到 GPU 管线,音频和视频及其它更多等等,网络浏览器更类似于一个操作系统而不仅仅只是一个软件。

浏览器的整体性能是由一些大型的组件所决定的,这些组件包括:解析,布局,样式计算,JavaScript 和 WebAssembly 执行,渲染,当然还有网络堆栈。

一般情况下,工程师们会把网络堆栈看成是一个性能瓶颈。经常会发生这样的情况因为从网络抓取所有的资源会堵塞渲染剩下的步骤。为了更加高效的网络层,它需要不仅仅只是扮演套接字管理员的角色。在我们看来获取资源是一个非常简单的机制,但是实际上它集成自身的优化准则,接口和服务的一整套平台。

网页开发者不需要担心单独的 TCP 或者 UDP 数据包,请求格式化,缓存以及其它正在发生的一切。浏览器会处理这些复杂的玩意,这样就可以专注开发自己的程序。但是,知道其内部的原理可以帮助开发者开发出更加高效和安全的程序。

本质上,当用户开始和浏览器发生交互所产生的情况如下:

  • 用户在浏览器地址栏中输入 URL 地址。
  • 在网络上查找指定 URL 的资源,浏览器开始检查本地和应用程序缓存并试着使用本地副本来响应资源的请求。
  • 当缓存不可用,浏览器使用 URL 中的域名然后根据域名从 DNS 处获取服务器的 IP 地址。如果有域名缓存,将不需要进行 DNS 查询。
  • 浏览器创建一个 HTTP 报文表明其请求远程服务器的某个网页。
  • 报文被传输到 TCP 层,该层会在 HTTP 报文头部添加其自身的信息。该信息是保持创建的会话的必要信息。
  • 然后在 IP 层处理报文,该层的主要职责即找出从用户发送报文到远程服务器的路径。在 HTTP 报文头部添加该路径信息。
  • 传输报文到远程服务器。
  • 一旦接收到报文,以类似的方式返回响应数据。

W3C Navigation Timing specification 提供了浏览器接口及浏览器中每个请求背后的可视化计时和性能数据。让我们浏览下这些组件,因为每个组件在获取最佳用户体验方面扮演了重要的角色。

整个网络请求过程是相当复杂的并且有许多的层次结构,每一层都有可能成为性能瓶颈。这就是为什么浏览器使用各种技术努力提升其性能,以便把整个网络通信的性能损耗降至最低。

套接字管理

看些新技术吧:

  • 源-由应用程序协议,域名和端口号的三个部分组成(比如 https, www.example.com, 443)
  • 套接字池-属于同源的一组套接字(所有的主流浏览器都限制套接字池最多只能有 6 个套接字)

JavaScript 和 WebAssembly 禁止开发者操作单独的网络套接字的生命周期,这样是相当的明智的。这样不仅仅可以让你头发少掉点而且可以让浏览器自动优化大量的性能,这些性能包括套接字重用,请求优化和延迟绑定,协议协商,强制连接限制及其它的优化措施。

实际上,现代浏览器更一步地将请求管理周期从套接字管理中剥离了出来。用套接字池来组织套接字,以源来分组套接字,每个套接字池强制限制其连接数和安全约束。排队,优先化等待的请求,然后和套接字池中的单个套接字绑定。如果不是服务器主动关闭这些连接,多个请求可以自动重用相同的套接字。

由于创建一个新的 TCP 连接会带来额外的性能开销,重用连接会为其自带来极大的性能提升。默认情况下,当发起请求的时候,浏览器使用所谓的 『keepalive』机制以节省创建到服务器的新连接所耗费的时间。创建一个新的 TCP 连接的平均时间为:

  • 本地请求-23 毫秒
  • Transcontinental 请求-120 毫秒
  • Intercontinental 请求-225 毫秒

这样的架构衍生出了一些其它的优化方法。请求可以依据优先级来以不同的顺序执行。浏览器可以优化所有套接字间的带宽分配或者它可以创建套接字以等待预期的请求。

如上所述,这些都是浏览器所控制而不用开发者进行干预。但这并不意味着我们无所事事了。选择正确的数据传输所用的网络通信模式,类型和频率,正确的协议类型以及正确的服务器堆栈隧道/优化对于提升整个程序的性能有着至关重要的作用。

一些浏览器甚至更进一步。例如,当你使用 Chrome 的时候,当用户使用的时候它会进行自我学习从而变得更加快速。它基于访问过的网页和典型的浏览器模式来进行学习,这样就可以预期可能的用户行为且在用户进行任意操作之前进行操作。最简单的例子即当用户悬停在某个链接上的时候预渲染页面。如果你想学习更多关于 Chrome 优化技术的文章,可以查看 High-Performance Browser Networking 这本书的 https://www.igvita.com/posa/high-performance-networking-in-google-chrome/章节。

网络安全和沙箱

允许浏览器操作单独的套接字有另一个非常重要的目的即:浏览器就可以针对不被信任的程序资源强制实施一套一致的安全和政策约束措施。例如,浏览器禁止通过 API 直接访问原始网络套接字,因为这样会导致任意可疑的程序随意连接任意主机。浏览器也强制连接数限制以保护服务器免受由于客户端访问而耗尽其资源。

浏览器格式化所有流出的请求以强制格式正确和一致的协议语义来保护服务器。同样地,浏览器会自动解码响应内容以保护用户免受可疑服务器的攻击。

TLS 协商

Transport Layer Security (TLS) 是一个为计算机网络提供通信安全的加密协议。它广泛应用于大量应用程序,其中之一即浏览网页。网站可以使用 TLS 来保证服务器和网页浏览器之间的所有通信安全。

整个 TLS 握手过程包含以下几个步骤:

  1. 客户端向服务器发送 『Client hello』 信息,附带着客户端随机值和支持的密码组合。
  2. 服务器返回给客户端 『Server hello』信息,附带着服务器随机值。
  3. 服务器返回给客户端认证证书及或许要求客户端返回一个类似的证书。服务器返回『Server hello done』信息。
  4. 如果服务器要求客户端发送一个证书,客户端进行发送。
  5. 客户端创建一个随机的 Pre-Master 密钥然后使用服务器证书中的公钥来进行加密,向服务器发送加密过的 Pre-Master 密钥。
  6. 服务器收到 Pre-Master 密钥。服务器和客户端各自生成基于 Pre-Master 密钥的主密钥和会话密钥。
  7. 客户端给服务器发送一个 『Change cipher spec』的通知,表明客户端将会开始使用新的会话密钥来哈希和加密消息。客户端也发送了一个 『Client finished』的消息。
  8. 服务器接收到『Change cipher spec』的通知然后使用会话钥匙来切换其记录层安全状态为对称加密状态。服务器返回客户端一个 『Server finished』消息。
  9. 客户端和服务器现在可以通过建立的安全通道来交换程序数据。所有客户端和服务器之间发送的信息都会使用会话密钥进行加密。

每当发生任何验证失败的时候,用户会收到警告。比如服务器使用自签名的证书。

同源策略

当两个页面的协议,端口(如果有指定)以及主机名都是一样的则称为同源。

以下为一些可能包含跨域的资源示例:

  • <script src=”…”></script> 里面的 JavaScript 代码。语法错误的错误信息仅适用于同源脚本。
  • <link rel=”stylesheet” href=”…”> 的 CSS。由于 CSS 的松散语法规则,跨域 CSS 要求正确的 Content-Type 头。各个浏览器的限制不同。
  • <img> 图片
  • <video> 和 <audio> 媒体文件
  • <object><embed> 和 <applet> 插件
  • @font-face 字体。一些浏览器允许跨域字体,其它则要求同源字体。
  • 和 <iframe> 相关的一切内容。网站可以使用  X-Frame-Options 头来防止此种跨域交互。

以上的列表还远远不够;该列表旨在强调工作中的『最小特权』原则。浏览器只为应用程序代码暴露出其所必需的接口和资源:应用提供数据和 URL 地址,然后浏览器格式化请求及处理每条连接的整个生命周期。

需要注意的是并没有一个简单的 『同源策略』概念。

相反,有一系列相关的机制来强制限制浏览器的 DOM 访问,cookie 和 会话状态管理,网络连接和其它组件。

资源和客户端状态缓存

最好和最快的请求即不创建请求。在分派一个请求前,浏览器自动检查其资源缓存,进行必要的验证检查然后当指匹配指定的条件时返回一份本地资源拷贝。如果缓存中没有可用的本地资源,则发起网络请求然后把响应内容自动放置于缓存中以备之后的访问(如果这是被允许的)。

  • 浏览器自动为每个资源求值缓存指令。
  • 当条件允许时,浏览器自动重新恢复过期资源
  • 浏览器自动处理缓存和资源回收的大小

管理一个高效和优化的资源缓存是非常困难的。谢天谢地,浏览器为我们处理了整个复杂的玩意,而我们所需要做的即保证服务器返回恰当的缓存指令;想了解更多可以看 客户端资源缓存 文章。你为网页上的所有资源添加 Cache-Control,ETag,和 Last-Modified 的响应头信息。

最后,一个经常被忽略但至关重要的浏览器功能即其提供了验证,会话和 cookie 管理。浏览器为每个源维护单独的『cookie jars』,通过提供必要的程序和服务器接口来读写新的 cookie,会话和认证数据,以及自动挂载和处理适当的 HTTP 头来为我们自动处理整个过程。

例子:

一个简单但明了的用来展示浏览器的延迟会话状态管理的方便性的例子即:多个选项卡或者浏览器窗口可以共享一个认证会话,反之亦然;一个选项卡中的登出操作可以使所有其它打开窗口的会话失效。

应用程序接口和协议

了解了网络服务之后,最终要讲到应用程序接口和协议。众所周知,更底层的结构提供了一组广泛的重要服务:套接字和连接管理,请求和响应处理,各种安全策略,缓存及其它更多的强制措施。每当初始化一个 HTTP 请求或者 XMLHttpRequest,一个持久的服务推事件或者 WebSocket 会话抑或打开一个 WebRTC 连接,我们就是在和部分或者所有这些底层服务进行交互。

没有单一的最佳协议或者接口。每个复杂的程序都会基于不同的要求混合使用不同的传输协议:和浏览器缓存的交互,协议开销,消息延迟,可靠性,数据传输类型以及其它。一些协议拥有低数据传输延迟的特性(比如服务器推事件,WebSocket),但是可能不符合其它重要的场合,比如利用浏览器缓存或者支持任意情况下的二进制数据传输的能力。

扩展

关于字体文件的跨域问题可以查看这里这里

How Javascript works (Javascript工作原理) (十二) 网络层探秘及如何提高其性能和安全性的更多相关文章

  1. How Javascript works (Javascript工作原理) (一) 引擎,运行时,函数调用栈

    个人总结:该系列文章对JS底层的工作原理进行了介绍. 这篇文章讲了 运行时:js其实是和AJAX.DOM.Settimeout等WebAPI独立分离开的 调用栈:JavaScript的堆内存管理 和 ...

  2. How Javascript works (Javascript工作原理) (四) 事件循环及异步编程的出现和 5 种更好的 async/await 编程方式

    个人总结: 1.讲解了JS引擎,webAPI与event loop合作的机制. 2.setTimeout是把事件推送给Web API去处理,当时间到了之后才把setTimeout中的事件推入调用栈. ...

  3. JavaScript定时器的工作原理(翻译)

    JavaScript定时器的工作原理(翻译) 标签(空格分隔): JavaScript定时器 最近在看ajax原理的时候,看到了一篇国外的文章,讲解了JavaScript定时器的工作原理,帮助我很好的 ...

  4. [中英对照]How PCI Works | PCI工作原理

    How PCI Works | PCI工作原理 Your computer's components work together through a bus. Learn about the PCI ...

  5. [中英对照]How PCI Express Works | PCIe工作原理

    How PCI Express Works | PCIe工作原理 PCI Express is a high-speed serial connection that operates more li ...

  6. [转帖]SSD的工作原理、GC和TRIM、写入放大以及性能评测

    SSD的工作原理.GC和TRIM.写入放大以及性能评测 https://blog.csdn.net/scaleqiao/article/details/50511279   SSD的物理结构和工作原理 ...

  7. 条目十二《切勿对slt容器的线性安全性又不切实际的依赖》

    条目十二<切勿对slt容器的线性安全性又不切实际的依赖> 这一条目,我想用简短而有力的语句来总结. stl库是为了照顾大多数情况,而不是某一领域,如果在库层次实现线性安全,很大可能是对性能 ...

  8. How Javascript works (Javascript工作原理) (十四) 解析,语法抽象树及最小化解析时间的 5 条小技巧

    个人总结:读完这篇文章需要15分钟,文章介绍了抽象语法树与js引擎解析这些语法树的过程,提到了懒解析——即转换为AST的过程中不直接进入函数体解析,当这个函数体需要执行的时候才进行相应转换.(因为有的 ...

  9. How Javascript works (Javascript工作原理) (十五) 类和继承及 Babel 和 TypeScript 代码转换探秘

    个人总结:读完这篇文章需要15分钟,文章主要讲解了Babel和TypeScript的工作原理,(例如对es6 类的转换,是将原始es6代码转换为es5代码,这些代码中包含着类似于 _classCall ...

随机推荐

  1. solr + eclipse 调试环境搭建

    1: 在官网下载对应源码 http://www.fayea.com/apache-mirror/lucene/solr/4.1.0/ 选择源码文件,如图所示: 2: 解压后目录如图所示: 在根目录下存 ...

  2. 部署OGG时字符集转换问题--oracle to oracle已验证,其他异构环境应当也适用

    之前在安装OGG总是遇到字符集问题,尤其是多源端对一个目标端时,源端字符集不同,导致出现字符集问题 无法同步数据,查阅了大量的园子资料,都说要设置复制或抽取进程中SETENV (NLS_LANG=AM ...

  3. DataTable 数据导入MS ACCESS 数据库中 数字类型字段为空的解决办法

    string strSql = "insert into GongCheng (GCSY,GCBH,GCBHOLD,GCMC,GCKCJD,GCJSDW,GCSJDW,GCKCDW,GCSG ...

  4. DBCA创建数据库ORA-01034 ORACLE not available

    SYMPTOMS 在利用dbca创建数据库时,当设置完毕全部參数.開始装时 跑到2% 就报错 ORA-01034 ORACLE not available, 例如以下图 watermark/2/tex ...

  5. android -- 小问题 关于ListView设置了OnScrollListener之后onScrollStateChanged()和onScroll方法监听不到的问题

    关于ListView设置了OnScrollListener之后onScrollStateChanged()和onScroll方法监听不到的问题: 原因: 首先OnScrollListener是焦点滚动 ...

  6. UVA10491 - Cows and Cars(概率)

    UVA10491 - Cows and Cars(概率) 题目链接 题目大意:给你n个门后面藏着牛.m个门后面藏着车,然后再给你k个提示.在你作出选择后告诉你有多少个门后面是有牛的,如今问你作出决定后 ...

  7. HDU-3577-Fast Arrangement-区间更新

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3577 好吧,我认为这道题有必要说一下题目意思:毕竟我刚開始是没有看太懂,原谅我这个英语渣渣...ORZ ...

  8. 自己封装js组件 - 初级

    2天前抱着试试看的态度注册了此神博,心血来潮呕心沥血写了一篇关于vue 自定义组件的小文章 尼玛果然一个评论的没有!果然毫无人气!(当然了我这文章内容有限和大神们的比起来简直是粗制滥造...)索性我就 ...

  9. What is the difference between SET and SELECT when assigning values to variables, in T-SQL?

    http://vyaskn.tripod.com/differences_between_set_and_select.htm https://stackoverflow.com/questions/ ...

  10. 23.STL容器小结