bangrenzhuce 2019-06-30
JavaScript 是如何工作的:深入网络层 + 如何优化性能和安全
这是专门探索 JavaScript 及其所构建的组件的系列文章的第 12 篇。
如果你错过了前面的章节,可以在这里找到它们:
正如在上一篇关于 渲染引擎 的博客文章中提到的,我们认为优秀的 JavaScript 开发人员和杰出的 JavaScript 开发人员之间的区别在于,后者不仅理解语言的具体细节,而且理解其内部结构和周遭环境。
49年前,一种叫做 ARPAnet 的网诞生了。它是一个早期的 分组交换网络,也是第一个 实现TCP/IP套件的网络。20年后,蒂姆·伯纳斯-李提出了一种“网状结构”的建议,这种结构后来被称为“万维网”。在这 49 年里,互联网走过了漫长的道路,从仅仅两台计算机交换数据包,到超过 7500
万台服务器、38
亿互联网用户和 13
亿个网站。
"阿帕"(ARPA),是美国高级研究计划署(Advanced Research ProjectAgency)的简称。他的核心机构之一是信息处理(IPTO Information Processing Techniques Office),一直在关注电脑图形、网络通讯、超级计算机等研究课题。阿帕网为美国国防部高级研究计划署开发的世界上第一个运营的封包交换网络,它是全球互联网的始祖。
在这篇文章中,我们将尝试分析现代浏览器使用什么技术来自动提高性能(甚至在你不知道的情况下),接着深入浏览器网络层。最后,我们将提供一些关于如何帮助浏览器提高 Web 应用程序性能的建议。
现代 Web 浏览器专为快速,高效,安全地提供网络应用/网站而设计。 数百个组件在不同的层上运行,从流程管理和安全沙箱到 GPU 管道,音频和视频等等,Web 浏览器看起来更像是一个操作系统,而不仅仅是一个软件应用程序。
浏览器的总体性能由许多大型组件决定:解析、布局、样式计算、JavaScript 和 WebAssembly 执行、渲染,当然还有网络堆栈。
工程师经常认为网络堆栈是一个瓶颈。这种情况经常发生,因为所有资源都需要从网上获取,然后才能解除其余步骤的阻塞。为了使网络层高效,它需要扮演的角色不仅仅是一个简单的套接字管理器。它提供给我们的是一种非常简单的资源获取机制,但实际上它是一个具有自己的优化标准、API 和服务的完整平台。
作为 Web 开发人员,我们不必担心单独的 TCP 或 UDP 数据包、请求格式化、缓存和其他一切问题。整个复杂性由浏览器负责,因此我们可以将精力集中在我们正在开发的应用程序上。然而,了解底层的情况可以帮助我们创建更快、更安全的应用程序。
本质上,当用户开始与浏览器交互时会发生以下情况:
W3C的浏览时序规范(Navigation Timing specification)提供了一个浏览器API,让我们可以看到浏览器中每项请求的生命周期背后的时序和性能数据。让我们看看这些组成部分,每一块都是影响最佳用户体验的关键点:
整个网络过程非常复杂,有许多不同的层,这可能成为瓶颈。这就是为什么浏览器努力通过使用各种技术来提高自己的性能,从而使整个网络通信的影响最小。
先了解一些术语:
JavaScript 和 WebAssembly 不允许我们管理单个网络套接字的生命周期,这是一件好事!这不仅使我们的省去较多麻烦,而且还可以让浏览器自动进行许多性能优化,其中包括套接字重用、请求优先级和后期绑定、协议协商、强制连接限制等。
实际上,现代浏览器在将请求管理周期与套接字管理分离方面做了更多的工作。套接字组织在按源分组的池中,每个池执行自己的连接限制和安全约束。挂起的请求被排队、排序,然后绑定到池中的各个套接字。除非服务器有意关闭连接,否则同一个套接字可以跨多个请求自动重用!
由于打开新的 TCP 连接需要额外的成本,因此连接的重用本身就带来了巨大的性能优势。默认情况下,浏览器使用所谓的 “keepalive” 机制,它可以在发出请求时节省打开到服务器的新连接的时间。打开新 TCP 连接的平均时间为:
23ms
120ms
225ms
这种架构为其他一些优化提供了可能, 请求可以根据其优先级以不同的顺序执行。 浏览器可以优化所有套接字的带宽分配,也可以在预期请求时打开套接字。
正如之前提到的,这一切都由浏览器管理,不需要我们做任何工作,但这并不意味着我们什么都做不了。 选择正确的网络通信模式,类型和传输频率,协议选择以及服务器堆栈的调优/优化可以在提高应用程序的整体性能方面发挥重要作用。
有些浏览器甚至更进了一步。 例如,Chrome 可以学习用户的操作习惯来使自己变得更快。 它根据访问的站点和典型的浏览模式进行学习,以便预测可能的用户行为并在用户执行任何操作之前采取措施。 最简单的例子是当用户在链接上悬停时,Chrome 会预先渲染页面, 如果有兴趣了解有关 Chrome 优化的更多信息,可以查看这篇文章 https://www.igvita.com/posa/h...
允许浏览器管理单个套接字还有另一个非常重要的目的:通过这种方式,浏览器能够对不受信任的应用程序资源执行一致的安全和策略约束。例如,浏览器不允许 API 直接访问原始网络套接字,因为这将使任何恶意应用程序能够任意连接到任何主机。浏览器还强制执行连接限制,以保护服务器和客户端免于资源耗尽。
浏览器格式化所有传出请求,以强制执行一致且格式良好的协议语义,以保护服务器。类似地,响应解码是自动完成的,以保护用户免受恶意服务器的攻击。
传输层安全性协议 (Transport Layer Security, TLS)是一种通过计算机网络提供通信安全性的加密协议。它在许多应用程序中得到了广泛的应用,其中之一就是 Web 浏览器。网站可以使用 TLS 保护服务器和Web 浏览器之间的所有通信。该协议由两层组成: TLS 记录协议(TLS Record)和 TLS 握手协议(TLS Handshake)。较低的层为 TLS 记录协议,位于某个可靠的传输协议(例如 TCP)上面。
整个TLS握手包括以下步骤:
“Client hello”
消息,与之一同发送的还有客户端产生的随机值和支持的密码套件。“Server hello done”
消息。Pre-Master Secret
,并使用服务器证书中的公钥对其进行加密,将加密的 Pre-Master Secret 发送到服务器。Pre-Master Secret
。 服务器和客户端均基于预主密钥生成主密钥和会话密钥。“Change cipher spec”
通知,以指示客户端将开始使用新的会话密钥进行散列和加密消息。 客户端还发送 “Server finished”
消息。“Change cipher spec”
,并使用会话密钥将其记录层安全状态切换为对称加密。 服务器向客户端发送 “Server finished”
消息。如果任何验证失败,则警告用户 - 例如,服务器正在使用自签名证书。
同源是指文档的来源相同,主要包括三个方面
以下是一些可能嵌入跨源资源的一些例子:
<script src =“...”> </ script>
的 JavaScript。 语法错误的错误消息仅适用于同源脚本<link rel =“stylesheet”href =“...”>
的CSS。 由于 CSS 的宽松语法规则,跨源 CSS 需要正确的 Content-Type 标头。不同浏览器可能有不同的限制<img>
加载图片<video>
和 <audio>
的媒体文件<object>
,<embed>
和 <applet>
的插件<frame>
和 <iframe>
的东西。 站点可以使用 X-Frame-Options 头部标识来阻止这种形式的跨源交互以上列表并非完整,其目的是强调工作中 “最小特权” 的原则。 浏览器仅公开应用程序代码所需的 API 和资源:应用程序提供数据和 URL,浏览器格式化请求并处理每个连接的整个生命周期。
值得注意的是,“同源策略”并不是一个单一概念。相反,有一组相关的机制来限制对 DOM 访问、cookie 和会话状态管理、网络和浏览器的其他组件。
最佳请求是没有重新请求。在发送请求之前,浏览器会自动检查其资源缓存,执行必要的验证检查,并在满足指定条件的情况下返回资源的本地副本。如果缓存中没有可用的本地资源,则发出网络请求,并自动将响应放置在缓存中,以便在有权限的情况下进行后续访问。
管理高效且优化的资源缓存很难。 值得庆幸的是,浏览器帮我们处理整个复杂事情,我们需要做的就是确保我们的服务器返回适当的缓存指令; 要了解更多信息,请参阅 客户端的缓存资源(Cache Resources on the Client)。 这个需要我们为页面上的所有资源提供了 Cache-Control,ETag 和 Last-Modified 响应头部标志。
最后,浏览器的一个经常被忽视的关键功能是提供身份验证、会话和 cookie 管理。浏览器为每个源维护独立的 “cookie jars”,提供必要的应用程序和服务器 Api 来读写新的 cookie、会话和身份验证数据,并自动附加上和处理相应的 HTTP 头以代替我们自动执行整个过程。
用一个简单但有说明性的例子来说明将会话状态管理推放到浏览器端的便利之处:同一个经过身份验证的会话可以在多个选项卡或浏览器窗口之间共享,反之亦然;单个选项卡中的注销操作将使所有其他打开的窗口中打开的会话失效。
研究完了网络服务,终于到达了应用程序 API 和协议这一步。正如我们所看到的,底层提供了大量关键服务:套接字和连接管理、请求和响应处理、各种安全策略的执行、缓存等等。每当我们启动 HTTP 或 XMLHttpRequest 、长期的 Server-Sent Events
或 WebSocket 会话,或打开 WebRTC 连接时,我们都在与这些底层服务进行交互。
没有单一的最佳协议或 API。 每个稍微复杂的应用程序都需要根据各种要求混合使用不同的传输:与浏览器缓存的交互,协议开销,消息延迟,可靠性,数据传输类型等。 某些协议可能提供低延迟传送(例如,Server-Sent Events,WebSocket),但可能不符合其他关键标准,例如在所有情况下利用浏览器缓存或支持有效二进制传输的能力。
原文:
https://blog.sessionstack.com...
我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/dev...
你的点赞是我持续分享好东西的动力,欢迎点赞!