问题
我们在开发过程中,发现后端 API 请求特别慢,于是跟后端抱怨。
“怎么 API 这么慢啊,请求一个接口要十几秒”。
而且这种情况是偶现的,前端开发同学表示有时候会出现,非必现。
但是后端同学通过一顿操作后发现,接口没有问题,他们是通过 postman 工具以及 test 环境尝试,都发现接口请求速度是没有问题的。
“那感觉是前端问题”?
我们来梳理一下问题,如下:

后端 API 请求特别慢,而且是偶现的。
在 test 环境没有复现。
postman 工具请求没有复现。

问题解决过程
时间都去哪了?
第一个问题,API 耗费的时间都用来做什么了?
我们打开 Chrome 调试工具。在 network 中可以看到每个接口的耗时。

hover 到你的耗时接口的 Waterful,就可以看到该接口的具体耗时。

可以看到,其耗时主要是在 Stalled,代表浏览器得到要发出这个请求的指令到请求可以发出的等待时间,一般是代理协商、以及等待可复用的 TCP 连接释放的时间,不包括 DNS 查询、建立 TCP 连接等时间等。
所以 API 一直在等待浏览器给它发出去的指令,以上面截图的为例,整整等待了 23.84S,它请求和响应的时间很快(最多也就几百毫秒,也就是后端所说的接口并不慢)。
所以 API 到底在等待浏览器的什么处理?
什么阻塞了请求?
经过定位,我们发现,我们项目中使用 Server-Sent Events(以下简称 SSE)。它跟 WebSocket 一样,都是服务器向浏览器推送信息。但不同的是,它使用的是 HTTP 协议。
当不通过 HTTP / 2 使用时,SSE 会受到最大连接数的限制,限制为 6 次。此限制是针对每个浏览器 + 域的,因此这意味着您可以跨所有选项卡打开 6 个 SSE 连接到 www.example1.com,并打开 6 个 SSE 连接到 www.example2.com。这一点可以通过以下这个 demo 复现。
复制问题的步骤:

访问ssebin.btubbs.com/multi/
单击添加计数器6或更多次
尝试打开另一个标签到同一地址

结果是,第 6 次之后,SSE 请求一直无法响应,打开新的标签到同一个地址的时候,浏览器也无法访问。
效果图如下:

该问题在 Chrome 和 Firefox 中被标记为“无法解决”。
至于偶现,是因为前端开发者有时候用 Chrome 会打开了多个选项卡,每个选项卡都是同一个本地开发地址,就会导致达到 SSE 的最大连接数的限制,而它的执行时间会很长,也就会阻塞其他的请求,一致在等待 SSE 执行完。
所以解决的方法是什么?
解决方案
简单粗暴的两个方法

不要打开太多个选项卡。这样就不会达到它的限制数。(因为我们一个选项卡只请求一个 SSE)。
开发环境下,关闭该功能。

使用 HTTP / 2
使用 HTTP / 2 时,HTTP 同一时间内的最大连接数由服务器和客户端之间协商(默认为 100)
这解释了为什么我们 test 环境没有问题,因为 test 环境用的是 HTTP / 2。而在开发环境中,我们使用的是 HTTP 1.1 就会出现这个问题。
那如何在开发环境中使用 HTTP / 2 呢?
我们现在在开发环境,大部分还是使用 webpack-dev-server 起一个本地服务,快速开发应用程序。在文档中,我们找到 server 选项,允许设置服务器和配置项(默认为 'http')。
只需要加上这一行代码即可。
devServer: {
+ server: 'spdy',
port: PORT,
}
复制代码
看看效果,是成功了的。

原理使用 spdy 使用自签名证书通过 HTTP/2 提供服务。需要注意的一点是:

该配置项在 Node 15.0.0 及以上的版本会被忽略,因为 spdy 在这些版本中不会正常工作。一旦 Express 支持 Node 内建 HTTP/2,dev server 会进行迁移。

总结归纳
原本这个问题认为跟前端无关,没想到最后吃瓜吃到自己头上。提升相关技能的知识储备以及思考问题的方式,可能会方便我们定位到此类问题。
充分利用好浏览器的调试工具,对一个问题可以从多个角度出发进行思考。比如一开始,没想到本地也可以开启 HTTP / 2。后来偶然间想搜下是否有此类方案,结果还真有!

作者:Gopal
链接:https://juejin.cn/post/7119074496610304031
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

早在 1990 年代,我的一位朋友曾问我为什么有如此之多的编程语言。 “为什么没有一种大家都会用、公认最好的编程语言?”他精通计算机,但不是开发人员,至少不是全职开发人员。我回答说,编程语言通常是为某些任务或工作负载而设计的,从这个意义上说,大多数语言在它们使什么成为可能方面的差异较小,而在它们使什么变得容易方面差异很大。最后一句是我记得别人说过的一句话。这听起来很聪明也很恰当,但事实是我真的不知道为什么会有这么多编程语言。这个问题一直萦绕在我的脑海中。

几年前,我有机会参观了位于加利福尼亚州山景城的计算机历史博物馆。那是一个了不起的地方,在众多展品中,有一幅覆盖整面墙壁的、关于编程语言演变的图表。这张图是如此之大,以至于任何曾经在任何东西中写过“Hello World”的人都有一种将鼻子贴在墙上并逐段搜索以尝试找到他们最喜欢的语言的冲动。我当然也做了。

过去的好日子

在今天,有很多东西被认为是理所当然的。在早期,则一切都是昂贵且有限的:存储、内存和处理能力。人们不得不逆风走上坡路,然后通宵熬夜来获得计算机时间。在那段时间更轻松的事是,编程语言的命名空间是空白的,而1950年代和1960年代可以精确地为其所做的事情命名:FORTRAN(公式翻译器)、COBOL(通用面向业务的语言) )、BASIC(初学者通用符号指令代码)、ALGOL(算法语言)、LISP(列表处理器)。大多数人可能没有听说过 SNOBOL (String Oriented and Symbolic Language, 1962),但是不需要费劲猜就可以确定它存在的目的。

如果在那段时间更充分地理解了面向对象的编程概念,那么我们可能会使用“OBJOL”之类的东西进行编码——一种明确命名的面向对象语言,至少从那个时代的命名模式来看是这样。

PL/I (1964) 的大胆之处值得一提和钦佩,它的目标是成为“一种好的编程语言”。这个名字说明了一切:编程语言 1。应该不需要 2、3 或 4。虽然 PL/I 成为计算机编程的高地人的计划没有像设计者预期的那样实施,但他们提出了一个关键线索:为什么有这么多语言?早在 1960 年代初,人们就已经想到了这个问题。

此时此刻

Scala (2003)、Go (2009)、Rust (2010)、Kotlin (2011) 和 Swift (2014) 只是 2000年后创建的语言的几个例子。还有更多。在当今的技术环境中,似乎这些基本语言……满足任何平台上的所有低级、高级、功能、过程、对象、单线程、多线程、编译或脚本需求。 鉴于此,为什么仍在创建新语言? 我看到的最大主题是控制。

90年代是 Visual Basic 和 Visual C++的时代,它们都源自计算机历史博物馆语言图上的旧节点。 Visual Basic 流行于为 Windows 桌面平台构建前端应用程序,但缺乏许多高级语言功能(例如,数据结构、线程)。 Visual C++ 处于光谱的另一端——开发人员几乎可以做任何事情,但问题是 C++ 很复杂。可以这么说,存在一个“中间语言”的机会。然后 Java 在 1996 年爆发。Java 是一种功能齐全的面向对象语言,没有 C++ 的锋利边缘,这一事实令人信服,我至今记得在 Microsoft 的 Visual J++ 刚问世时,每个人都加入了 Java 派对。

Java 的主要设计重点之一是平台可移植性。不幸的是,至少就非微软平台而言,这并不是微软的目标之一,这使得 Sun Microsystems(当时 Java 背后的公司)和微软陷入了冲突,导致 1997 年开始的诉讼。这种关系最终导致微软发布了另一种名为 C# (2002) 的语言,它看起来很像 Java,但实际上并非如此。 C# 填补了 Microsoft 开发堆栈中的“中间”位置,并且与 Java 不同,Microsoft 可以控制它。

这并不是唯一的Java诉讼。甲骨文于 2010 年起诉谷歌在 Android 移动平台中使用 Java,因为甲骨文在收购 Sun Microsystems 后成为 Java 语言的所有者。这是一场长达十年的法律斗争,最终于 2021 年打到美国最高法院。

总体设计控制

维护和发展现有系统具有挑战性。软件增长的悖论是,系统的广泛采用会导致更多的使用,这可能会导致更大的成功,然后是更大的采用。但随着采用和使用的增加,它可能导致系统变得更难改变,尤其是在大方面,因为这样的改变有破坏向后兼容性的风险。管理不是不可能,只是很难。

管理编程语言的增长可以说是最困难的案例之一。编程语言用户是开发人员,优秀的开发人员不仅有生产力,而且有一种创造性地使用语言特性的方式,并非所有这些特性都是作者所希望的。如果需要找到边缘情况,开发人员就会找到它们,尤其是在大规模上。

Go (2009) 是一个有趣的例子,说明想要做的越来越多。其创建的一个驱动因素是需要能够在 Google 的容器化云环境中高效且可预测地部署的东西。另一个驱动因素是对一种强大的语言的渴望,特别是在网络和并发方面,但在语言特性方面不太强大,因为作者显然也是出于对 C++ 复杂性的“共同厌恶”。假设,谷歌可以通过为谷歌已经在使用的现有语言构建一个新的编译器和运行引擎来回应第一个驱动因素。虽然这不是一项简单的任务,但谷歌有很多聪明人,所以这是有可能的。但是要改变开发人员正在做的事情——以及做的方式——需要改变编程语言的语法和功能,而这些类型的改变很难——特别是当开发人员被告知某些事情不再被允许或必须被允许时。有时,为手头的用例创建新的东西会更容易,然后重新开始。再次开始。最终另一个节点被添加到计算机历史博物馆的墙壁大小的计算机语言图表中。

在2003,go的前身,google搞出了名为Go!的语言,这表明语言命名空间变得越来越小。尽管容易混淆,为什么Google仍使用了该名称?我不知道。 Go figure

https://cacm.acm.org/blogs/blog-cacm/262424-why-are-there-so-many-programming-languages/fulltext