潘忠显 / 2021-04-12
“JavaScript 工作原理”系列文章是翻译和整理自 SessionStack 网站的 How JavaScript works。因为博文发表于2017年,部分技术或信息可能已经过时。本文英文原文链接,作者 Alexander Zlatkov,翻译 潘忠显。
How JavaScript Works: Inside the Networking Layer + How to Optimize Its Performance and Security
这是致力于探索JavaScript及其构建组件的系列文章的第12个。在标识和描述核心元素的过程中,我们还共享一些在构建[SessionStack]时使用的经验法则。(https://www.sessionstack.com/?utm_source=medium&utm_medium=blog&utm_content=js-series-networking-layer -intro),这是一个JavaScript应用程序,需要强大且高效能,以帮助用户实时查看和再现其Web应用程序缺陷。
如果错过了前几章,可以在这里找到它们:
This is post # 12 of the series dedicated to exploring JavaScript and its building components. In the process of identifying and describing the core elements, we also share some rules of thumb we use when building SessionStack, a JavaScript application that needs to be robust and highly-performant to help users see and reproduce their web app defects real-time.
If you missed the previous chapters, you can find them here:
- An overview of the engine, the runtime, and the call stack
- Inside Google’s V8 engine + 5 tips on how to write optimized code
- Memory management + how to handle 4 common memory leaks
- The event loop and the rise of Async programming + 5 ways to better coding with async/await
- Deep dive into WebSockets and HTTP/2 with SSE + how to pick the right path
- A comparison with WebAssembly + why in certain cases it’s better to use it over JavaScript
- The building blocks of Web Workers + 5 cases when you should use them
- Service Workers, their life-cycle, and use cases
- The mechanics of Web Push Notifications
- Tracking changes in the DOM using MutationObserver
- The rendering engine and tips to optimize its performance
就像我们在上一篇关于[rendering engine]()的博客文章中提到的那样,我们相信优秀的JavaScript开发人员与优秀的JavaScript开发人员之间的区别在于,后者不仅了解该语言的本质,而且还了解其内部和周围环境。环境。
Just like we mentioned in our previous blog post about the rendering engine, we believe that the difference between good and great JavaScript developers is that the latter not only understand the nuts and bolts of the language but also its internals and the surrounding environment.
A little bit of history
##历史的一点点
四十九年前,创建了一个名为ARPAnet的东西。它是[早期的分组交换网络](https://en.wikipedia.org/wiki/Packet_switching)和第一个网络[用于实现TCP / IP套件](https://en.wikipedia.org/wiki/ Internet_protocol_suite)。该网络在加利福尼亚大学和斯坦福研究所之间建立了联系。 20年后,蒂姆·伯纳斯·李(Tim Berners-Lee)散发了有关“网状网”的提案,该提案后来被称为万维网。在那49年中,互联网发展了很长的路要走,从仅两台计算机交换数据包到达到超过7500万台服务器,3.8B的互联网用户和1.3B的网站。
Forty-nine years ago, a thing called ARPAnet was created. It was an early packet switching network and the first network to implement the TCP/IP suite. That network set up a link between University of California and Stanford Research Institute. 20 years later Tim Berners-Lee circulated a proposal for a “Mesh” which later became better known as the World Wide Web. In those 49, years the internet has come a long way, starting from just two computers exchanging packets of data, to reaching more than 75 million servers, 3.8B people using the internet and 1.3B websites.
在这篇文章中,我们将尝试分析现代浏览器采用哪些技术来自动提高性能(甚至在您不知道的情况下),并且将专门放大浏览器网络层。最后,我们将提供一些有关如何帮助浏览器提高Web应用程序性能的想法。
In this post, we’ll try to analyze what techniques modern browsers employ to automatically boost performance (without you even knowing it), and we’ll specifically zoom in on the browser networking layer. At the end, we’ll provide some ideas on how to help browsers boost even more the performance of your web apps.
Overview
# 概述
现代的Web浏览器是专门为快速,高效和安全地交付Web应用程序/网站而设计的。从流程管理和安全沙箱到GPU管道,音频和视频等等,数百个组件运行在不同的层上,Web浏览器看起来更像是操作系统,而不仅仅是软件应用程序。
浏览器的整体性能由许多大型组件决定:解析,布局,样式计算,JavaScript和WebAssembly执行,渲染,当然还有“网络堆栈” **。
The modern web browser has been specifically designed for the fast, efficient and secure delivery of web apps/websites. With hundreds of components running on different layers, from process management and security sandboxing to GPU pipelines, audio and video, and many more, the web browser looks more like an operating system rather than just a software application.
The overall performance of the browser is determined by a number of large components: parsing, layout, style calculation, JavaScript and WebAssembly execution, rendering, and of course, the networking stack.
工程师经常认为网络堆栈是一个瓶颈。这是经常发生的情况,因为在取消其余步骤之前,需要从Internet提取所有资源。为了使网络层高效,它需要扮演的角色不仅仅是简单的套接字管理器。它作为一种非常简单的资源获取机制提供给我们,但实际上它是具有自己的优化标准,API和服务的整个平台。
Engineers often think that the networking stack is a bottleneck. This is frequently the case since all resources need to be fetched from the internet before the rest of the steps are unblocked. For the networking layer to be efficient it needs to play the role of more than just a simple socket manager. It is presented to us as a very simple mechanism for resource fetching but it’s actually an entire platform with its own optimization criteria, APIs, and services.
作为网络开发人员,我们不必担心单个TCP或UDP数据包,请求格式,缓存以及其他所有情况。浏览器可以解决整个复杂性,因此我们可以专注于正在开发的应用程序。但是,了解幕后情况可以帮助我们创建更快,更安全的应用程序。
本质上,这是用户开始与浏览器进行交互时发生的事情:
As web developers, we don’t have to worry about the individual TCP or UDP packets, request formatting, caching and everything else that’s going on. The entire complexity is taken care of by the browser so we can focus on the application we’re developing. Knowing what’s happening under the hood, however, can help us create faster and more secure applications.
In essence, here’s what’s happening when the user starts interacting with the browser:
-用户在浏览器地址栏中输入一个URL -给定网络上资源的URL,浏览器首先检查其本地和应用程序缓存,然后尝试使用本地副本来满足请求。 -如果无法使用缓存,则浏览器将从URL中获取域名,然后从[DNS](https://en.wikipedia.org/wiki/Domain_Name_System)请求服务器的IP地址。如果域被缓存,则不需要DNS查询。 -浏览器创建一个HTTP数据包,说它请求位于远程服务器上的网页。 -数据包被发送到TCP层,TCP层在HTTP数据包的顶部添加自己的信息。需要此信息来维护启动的会话。 -然后将数据包传递到IP层,其主要工作是找出一种将数据包从用户发送到远程服务器的方法。此信息也存储在数据包的顶部。 -数据包发送到远程服务器。 -一旦接收到数据包,响应就会以类似的方式发送回去。
- The user enters a URL in the browser address bar
- Given the URL of a resource on the web, the browser starts by checking its local and application caches and tries to use a local copy to fulfill the request.
- If the cache cannot be used, the browser takes the domain name from the URL and requests the IP address of the server from a DNS. If the domain is cached, no DNS query is needed.
- The browser creates an HTTP packet saying that it requests a web page located on the remote server.
- The packet is sent to the TCP layer which adds its own information on top of the HTTP packet. This information is required to maintain the started session.
- The packet is then handed to the IP layer which main job is to figure out a way to send the packet from the user to the remote server. This information is also stored on top of the packet.
- The packet is sent to the remote server.
- Once the packet is received, the response gets sent back in a similar manner.
W3C [Navigation Timing Specification](http://www.w3.org/TR/navigation-timing/)提供了浏览器API以及对浏览器中每个请求生命周期背后的时序和性能数据的可见性。让我们检查一下各个组件,因为每个组件在提供最佳用户体验方面都起着至关重要的作用:
The W3C Navigation Timing specification provides a browser API as well as visibility into the timing and performance data behind the life of every request in the browser. Let’s inspect the components, as each plays a critical role in delivering the optimal user experience:
整个联网过程非常复杂,并且有许多不同的层会成为瓶颈。这就是为什么浏览器通过使用各种技术来努力提高性能的原因,从而使整个网络通信的影响降到最低。
The whole networking process is very complex and there are many different layers which can become a bottleneck. This is why browsers strive to improve performance on their side by using various techniques so that the impact of the entire network communication is minimal.
Socket management
#套接字管理
让我们从一些术语开始:
Let’s start with some terminology:
-来源-应用协议,域名和端口号的三倍(例如https,[www.example.com](http://www.example.com),443) -套接字池-属于同一来源的一组套接字(所有主流浏览器都将最大池大小限制为6个套接字)
- Origin — A triple of application protocol, domain name and port number (e.g. https, www.example.com, 443)
- Socket pool — a group of sockets belonging to the same origin (all major browsers limit the maximum pool size to 6 sockets)
JavaScript和WebAssembly 不允许,我们无法管理单个网络套接字的生命周期,这是一件好事!这不仅使我们保持双手清洁,而且还允许浏览器自动执行许多性能优化,其中包括套接字重用,请求优先级设置和后期绑定,协议协商,强制连接限制以及许多其他性能优化。
JavaScript and WebAssembly do not allow us to manage the lifecycle of individual network sockets, and that’s a good thing! This not only keeps our hands clean but it also allows the browser to automate a lot of performance optimizations some of which include socket reuse, request prioritization and late binding, protocol negotiation, enforcing connection limits, and many other.
实际上,现代浏览器要花更多的力气才能将请求管理周期与套接字管理分开。套接字按池进行组织,这些池按来源分组,每个池强制执行自己的连接限制和安全性约束。将待处理的请求排入队列,确定优先级,然后将其绑定到池中的各个套接字。除非服务器有意关闭连接,否则可以在多个请求之间自动重用同一套接字!
Actually, modern browsers go the extra mile to separate the request management cycle from socket management. Sockets are organized in pools, which are grouped by origin, and each pool enforces its own connection limits and security constraints. Pending requests are queued, prioritized, and then bound to individual sockets in the pool. Unless the server intentionally closes the connection, the same socket can be automatically reused across multiple requests!
由于打开新的TCP连接需要支付额外的费用,因此连接的重用本身会带来巨大的性能优势。默认情况下,浏览器使用所谓的“ keepalive”机制,该机制可在发出请求时节省打开与服务器的新连接所需的时间。打开新的TCP连接的平均时间为:
Since the opening of a new TCP connection comes at an additional cost, the reuse of connections introduces great performance benefits on its own. By default, browsers use the so-called “keepalive” mechanism which saves time from opening a new connection to the server when a request is made. The average time for opening a new TCP connection is:
-本地请求-23毫秒
-洲际请求-“ 120ms”
-洲际请求-225ms
- Local requests —
23ms
- Transcontinental requests —
120ms
- Intercontinental requests —
225ms
这种架构为许多其他优化机会打开了大门。可以根据请求的优先级以不同的顺序执行请求。浏览器可以优化所有套接字上的带宽分配,也可以在预期到请求时打开套接字。
正如我之前提到的,这全部由浏览器管理,不需要我们做任何工作。但这并不一定意味着我们无能为力。选择正确的网络通信模式,传输的类型和频率,协议的选择以及我们服务器堆栈的调整/优化可以在提高应用程序的整体性能方面发挥重要作用。
This architecture opens the door to a number of other optimization opportunities. The requests can be executed in a different order depending on their priority. The browser can optimize the bandwidth allocation across all sockets or it can open sockets in anticipation of a request.
As I mentioned before, this is all managed by the browser and does not require any work on our side. But this doesn’t necessarily mean that we can’t do anything to help. Choosing the right network communication patterns, type, and frequency of transfers, choice of protocols and tuning/optimization of our server stack can play a great role in improving the overall performance of an application.
有些浏览器甚至更进一步。例如,Chrome可以自我教学,以在您使用它时变得更快。它基于访问的站点和典型的浏览模式进行学习,因此可以预测可能的用户行为并在用户执行任何操作之前采取措施。最简单的示例是当用户将鼠标悬停在链接上时预呈现页面。如果您想了解有关Chrome优化的更多信息,可以查看[高性能浏览器网络]的本章https://www.igvita.com/posa/high-performance-networking-in-google-chrome/ ](https://hpbn.co)书。
Some browsers even go one step further. For example, Chrome can self-teach itself to get faster as you use it. It learns based on the sites visited and the typical browsing patterns so it can anticipate likely user behavior and take action before the user does anything. The simplest example is pre-rendering a page when the user hovers on a link. If you’re interested in learning more about Chrome’s optimizations, you can check out this chapter https://www.igvita.com/posa/high-performance-networking-in-google-chrome/ of the High-Performance Browser Networking book.
Network Security and Sandboxing
#网络安全和沙箱
允许浏览器管理各个套接字还有另一个非常重要的目的:通过这种方式,浏览器可以对不受信任的应用程序资源实施一组一致的安全性和策略约束。例如,浏览器不允许直接API访问原始网络套接字,因为这将使任何恶意应用程序都能与任何主机建立任意连接。浏览器还强制执行连接限制,以保护服务器和客户端免受资源耗尽的影响。
浏览器格式化所有传出的请求,以强制执行一致且格式正确的协议语义以保护服务器。同样,响应解码会自动完成,以保护用户免受恶意服务器的攻击。
Allowing the browser to manage the individual sockets has another very important purpose: this way the browser enables the enforcement of a consistent set of security and policy constraints on untrusted application resources. For example, the browser does not allow direct API access to raw network sockets as this would enable any malicious application to make arbitrary connections to any host. The browser also enforces connection limits which protect the server as well as the client from resource exhaustion.
The browser formats all outgoing requests to enforce consistent and well-formed protocol semantics to protect the server. Similarly, response decoding is done automatically to protect the user from malicious servers.
TLS negotiation
TLS协商
[传输层安全性(TLS)](https://en.wikipedia.org/wiki/Transport_Layer_Security)是一种加密协议,可通过计算机网络提供通信安全性。它在许多应用程序中得到广泛使用,其中之一就是Web浏览。网站可以使用TLS来保护其服务器和Web浏览器之间的所有通信的安全。
整个TLS握手包括以下步骤:
1.客户端向服务器发送“客户端问候”消息,以及客户端的随机值和受支持的密码套件。 2.服务器通过向客户端发送“服务器问候”消息以及服务器的随机值来做出响应。 3.服务器将其用于身份验证的证书发送给客户端,并且可以从客户端请求类似的证书。服务器发送“服务器问候已完成”消息。 4.如果服务器已从客户端请求证书,则客户端将其发送。 5.客户端创建一个随机的Pre-Master Secret,并使用服务器证书中的公钥对其进行加密,然后将加密的Pre-Master Secret发送到服务器。 6.服务器收到预主密钥。服务器和客户端分别基于“预主密钥”生成“主密钥”和会话密钥。 7.客户端向服务器发送“更改密码规范”通知,以指示客户端将开始使用新的会话密钥进行哈希和加密消息。客户端还会发送“客户端已完成”消息。 8.服务器接收“更改密码规范”,并使用会话密钥将其记录层安全状态切换为对称加密。服务器向客户端发送“服务器完成”消息。 9.客户端和服务器现在可以通过已建立的安全通道交换应用程序数据。使用会话密钥对从客户端发送到服务器以及从服务器发送回的所有消息进行加密。
如果任何验证失败(例如,服务器正在使用自签名证书),则会警告用户。
##同源政策
如果两个页面的协议,端口(如果指定了一个)和主机都相同,则两个页面的来源相同。
以下是一些可以跨域嵌入的资源示例:
-带有