资讯专栏INFORMATION COLUMN

JavaScript 工作原理之十二-网络层探秘及如何提高其性能和安全性

LoftySoul / 764人阅读

摘要:为了更加高效的网络层,它需要不仅仅只是扮演套接字管理员的角色。用套接字池来组织套接字,以源来分组套接字,每个套接字池强制限制其连接数和安全约束。协商是一个为计算机网络提供通信安全的加密协议。

原文请查阅这里,略有改动,本文采用知识共享署名 4.0 国际许可协议共享,BY Troland。

本系列持续更新中,Github 地址请查阅这里。

这是 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/h... 章节。

网络安全和沙箱

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

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

TSL 协商

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

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

客户端向服务器发送 『Client hello』 信息,附带着客户端随机值和支持的密码组合。

服务器返回给客户端 『Server hello』信息,附带着服务器随机值。

服务器返回给客户端认证证书及或许要求客户端返回一个类似的证书。服务器返回『Server hello done』信息。

如果服务器要求客户端发送一个证书,客户端进行发送。

客户端创建一个随机的 Pre-Master 密钥然后使用服务器证书中的公钥来进行加密,向服务器发送加密过的 Pre-Master 密钥。

服务器收到 Pre-Master 密钥。服务器和客户端各自生成基于 Pre-Master 密钥的主密钥和会话密钥。

客户端给服务器发送一个 『Change cipher spec』的通知,表明客户端将会开始使用新的会话密钥来哈希和加密消息。客户端也发送了一个 『Client finished』的消息。

服务器接收到『Change cipher spec』的通知然后使用会话钥匙来切换其记录层安全状态为对称加密状态。服务器返回客户端一个 『Server finished』消息。

客户端和服务器现在可以通过建立的安全通道来交换程序数据。所有客户端和服务器之间发送的信息都会使用会话密钥进行加密。

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

同源策略

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

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

里面的 JavaScript 代码。语法错误的错误信息仅适用于同源脚本。

的 CSS。由于 CSS 的松散语法规则,跨域 CSS 要求正确的 Content-Type 头。各个浏览器的限制不同。

图片

媒体文件

 和  插件

@font-face 字体。一些浏览器允许跨域字体,其它则要求同源字体。