How Javascript works (Javascript工作原理) (十二) 网络层探秘及如何提高其性能和安全性

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

这是 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),但是可能不符合其它重要的场合,比如利用浏览器缓存或者支持任意情况下的二进制数据传输的能力。

扩展

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

原文地址:https://www.cnblogs.com/eret9616/p/9384375.html

时间: 2024-09-30 13:33:29

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

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

个人总结: 这篇文章对JS底层的工作原理进行了介绍. 原文:https://blog.sessionstack.com/how-does-javascript-actually-work-part-1-b0bacc073cf 一.引擎,运行时,调用堆栈 这是 JavaScript 工作原理的第一章.本章会对语言引擎,运行时,调用栈做一个概述. 事实上,有很多开发者在每天日常开发中都会使用 JavaScript 但是却不了解其底层的知识. 概述 几乎所有人都已经听说过 V8 引擎的概念,并且很多人

我们应该如何去了解JavaScript引擎的工作原理

我们应该如何去了解JavaScript引擎的工作原理 JavaScript探秘:编写可维护的代码的重要性 JavaScript探秘:谨慎使用全局变量 JavaScript探秘:var预解析与副作用 JavaScript探秘:for循环(for Loops) JavaScript探秘:for-in循环(for-in Loops) JavaScript探秘:Prototypes强大过头了 JavaScript探秘:eval()是"魔鬼" JavaScript探秘:用parseInt()进行

JavaScript引擎的工作原理

(转) 1. 什么是JavaScript解析引擎? 简单地说,JavaScript解析引擎就是能够“读懂”JavaScript代码,并准确地给出代码运行结果的一段程序.比方说,当你写了 var a = 1 + 1; 这样一段代码,JavaScript引擎做的事情就是看懂(解析)你这段代码,并且将a的值变为2. 学过编译原理的人都知道,对于静态语言来说(如Java.C++.C),处理上述这些事情的叫编译器(Compiler),相应地对于JavaScript这样的动态语言则叫解释器(Interpre

如何去了解JavaScript引擎的工作原理

js:我们应该如何去了解JavaScript引擎的工作原理(转) http://www.nowamagic.net/librarys/veda/detail/1579 昨天收到一封来自深圳的一位前端童鞋的邮件,邮件内容如下(很抱歉,未经过他的允许,公开邮件内容,不过我相信其他人肯定也有同样的问题,所以,直接把问题原文抛出来): "读了你的几篇关于JS(变量对象.作用域.上下文.执行代码)的文章,我个人觉得有点抽象,难以深刻理解.我想请教下通过什么途径能够深入点的了解javascript解析引擎在

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

SSD的工作原理.GC和TRIM.写入放大以及性能评测 https://blog.csdn.net/scaleqiao/article/details/50511279 SSD的物理结构和工作原理 SSD是由SSD控制器,FLASH存储阵列,板上DRAM(可选),以及跟HOST接口,诸如SAS.SATA.或者PCIE也就是我们通常说的NVMe磁盘.它的结构图如下: 上面的Nand Flash表示的是Flash颗粒,SSD控制器通过若干个主控通道并行操作这些Flash颗粒,就像RAID0一样,这样

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

个人总结:读完这篇文章需要15分钟,文章主要讲解了Babel和TypeScript的工作原理,(例如对es6 类的转换,是将原始es6代码转换为es5代码,这些代码中包含着类似于 _classCallCheck 和 _createClass这样的函数,而这些函数已经在Babel和TypeScript的标准库中预先定义好了,然后进行处理). 顺便温习了Object.create这个方法,  比如有一个obj:{name:'是ho',f:function(){alert(1)}} var a = O

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

个人总结:读完这篇文章需要15分钟,文章介绍了抽象语法树与js引擎解析这些语法树的过程,提到了懒解析--即转换为AST的过程中不直接进入函数体解析,当这个函数体需要执行的时候才进行相应转换.(因为有的函数体只是声明了,并没有实际被调用) 解析,语法抽象树及最小化解析时间的 5 条小技巧 这是 JavaScript 工作原理的第十四章. 概述 我们都知道运行一大段 JavaScript 代码性能会变得很糟糕.代码不仅仅需要在网络中传输而且还需要解析,编译为字节码,最后运行.之前的文章讨论了诸如 J

How Javascript works (Javascript工作原理) (五) 深入理解 WebSockets 和带有 SSE 机制的HTTP/2 以及正确的使用姿势

总结: 1.长连接机制--分清Websocket,http2,SSE: HTTP/2 引进了 Server Push 技术用来让服务器主动向客户端缓存发送数据.然而,它并不允许直接向客户端程序本身发送数据.服务端推送只能由浏览器处理而不能够在程序代码中进行处理,意即程序代码没有 API 可以用来获取这些事件的通知. 通过SSE(Server Side Event)来实现服务端向客户端的单向推送,SSE基于HTTP,是单向通信. WebSocket是在服务端和客户端建立双工通信.基于TCP. 这是

How Javascript works (Javascript工作原理) (六) WebAssembly 对比 JavaScript 及其使用场景

个人总结: 1.webassembly简介:WebAssembly是一种用于开发网络应用的高效,底层的字节码.允许在网络应用中使用除JavaScript的语言以外的语言(比如C,C++,Rust及其他)来编写应用程序,然后编译成(提早)WebAssembly. 这是 JavaScript 工作原理的第六章. 现在,我们将会剖析 WebAssembly 的工作原理,而最重要的是它和 JavaScript 在性能方面的比对:加载时间,执行速度,垃圾回收,内存使用,平台 API 访问,调试,多线程以及