为什么重复的GET请求变慢了?

Chrome阻塞同API并发请求
本文探讨了在Chrome浏览器中并发请求同一API时出现的阻塞现象,并通过实验验证了这种行为与浏览器缓存机制有关。此外,还介绍了如何通过修改请求使其变得唯一来解决该问题。

最近在研究慢请求监控的问题,写了一个简单的测试代码:在网页端(index.html)通过fetch函数向服务端获取数据,然后打印请求耗时。

function requestData() {
    let start = new Date();
    fetch("http://localhost:3000/company/basic")
        .then(res => {
            return res.json();
        })
        .then(res => {
            let span = new Date() - start;
            console.log("span:", span);
        });
}
requestData();

在服务端通过setTimeout延时1500s才返回数据(服务端使用ExpressJS)。

app.get("/company/basic", (req, res) => {
    setTimeout(function() {
        res.send({ hello: "Hello Fundebug!" });
    }, 1500);
});

不出所料,span数据都略微大于 1500。

而后,我突发奇想,假设我同时发送多个请求会怎么样呢?于是有了如下代码:

[1, 2, 3].forEach(function() {
    requestData();
});

结果好像也没问题,在 Chrome 浏览器下面是这个效果:

接入 Fundebug 慢请求监控测试

于是愉快地接入 Fundebug 监控:

<script
    src="https://js.fundebug.cn/fundebug.1.9.0.min.js"
    apikey="API-KEY"
></script>

并设置如果请求时长超过 2 秒就上报:

if ("fundebug" in window) {
    fundebug.httpTimeout = 2000;
}

本以为刷新页面,应该不会收到报错。

结果,万万没想到的是,Fundebug 收到 2 个慢请求报错。

这不科学啊!

点开错误详情,可以看到具体的报错信息。一个请求耗时 3018 毫秒,一个请求耗时 4525 毫秒。

也就是说,第一个请求没问题,假设是 1500 毫秒。我们把三个请求的时间放一起看看有何规律:1500,3018,4524。他们近似成等差数列,相差 1500 毫秒。于是,我怀疑三个请求是一个一个阻塞式的,而不是并发的。

测试并发请求不同 API 的情况

为了验证这一点,我将测试改为请求三个不同的 API 接口。

服务端代码:

app.get("/company/basic", resp);
app.get("/company/basic1", resp);
app.get("/company/basic2", resp);

function resp(req, res) {
    setTimeout(function() {
        res.send({ hello: "Hello Fundebug!" });
    }, 1500);
}

网页端代码(requestData函数传入请求的 URL):

[
    "http://localhost:3000/company/basic",
    "http://localhost:3000/company/basic1",
    "http://localhost:3000/company/basic2"
].forEach(function(item) {
    requestData(item);
});

为了获取请求数据,将httpTimeout改为 1500。

if ("fundebug" in window) {
    fundebug.httpTimeout = 1500;
}

Fundebug 捕获三个请求的时间,分别为 1526,1525,1529。

至此大体验证了刚刚的假设:对同一个 API 接口的并发请求会被阻塞,对不同的 API 接口并发请求正常执行。

那么为什么会被阻塞呢?意图何在?接下来慢慢给各位介绍。

背后的原因

StackOverflow上找到了答案:

Yes, this behavior is due to Chrome locking the cache and waiting to see the result of one request before requesting the same resource again. The answer is to find a way to make the requests unique.

也就是说,Chrome 特意做了这样的设计。对于连续的相同请求,Chrome 会阻塞后面的请求,直到前面的完成。通过判断前面的请求返回的 Header 里面的缓存设置来决定下一步的行动。

我们可以做个实验来验证一下。

缓存实验

  • 服务端设置缓存 2 秒

    在服务端的接口返回代码中配置缓存时间

    res.setHeader("Cache-Control", "public, max-age=2");
    

  • 服务端设置不缓存

    res.setHeader(
        "Cache-Control",
        "private, no-cache, no-store, must-revalidate"
    );
    

  • Chrome 开发者面板设置Disable Cache

最后的疑问

为什么打开和不打开谷歌开发者控制台,行为会不一样了?

其实是有原因的,而且这个干扰项一度成功阻止了我发现问题的本质。当我们在开发前端项目的时候,代码的改动希望能够实时地反应到网页上,而不是受到浏览器缓存的影响,但是我们发现往往刷新页面的时候没有真的去服务端获取数据,还是老的信息。于是,我们会去配置一个选项,将Disable Cache设置为true。也就是说,在开发环境下,缓存是被禁用了的,也就不存在等待第一个请求返回然后判断其 Header 里面Cache-Control设置的问题。这也是为什么打开谷歌开发者控制台,请求没有等待,立即执行了。

关于Fundebug

Fundebug专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java线上应用实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了10亿+错误事件,付费客户有阳光保险、核桃编程、荔枝FM、掌门1对1、微脉、青团社等众多品牌企业。欢迎大家免费试用

img

版权声明

转载时请注明作者 Fundebug以及本文地址:
https://blog.fundebug.com/2019/07/17/chrome-stall-multiple-same-request/

### 三级标题:HTTPS GET 请求报错原因及解决方法 HTTPS GET 请求报错可能由多种原因引起,常见的问题包括协议前缀缺失、SSL/TLS 证书验证失败、服务器重定向策略不当以及请求参数格式错误等。 一种常见错误是未在请求 URL 中明确指定 `https://` 前缀。例如,使用 Postman 发送请求时,若未指定 HTTPS 前缀,客户端可能默认使用 HTTP 协议,从而导致服务器拒绝请求或返回错误状态码。解决方法是在请求地址前添加 `https://`,确保使用 HTTPS 协议进行通信[^1]。 SSL/TLS 握手失败也是常见的 HTTPS 请求问题。例如,使用 `get_headers()` 函数请求 HTTPS 资源时,可能会遇到 `SSL operation failed with code 1` 错误,提示证书验证失败。该问题通常由以下原因引起: - 服务器证书未被信任,可能由自签名证书或证书链不完整导致; - 客户端或服务器端配置不正确,未启用必要的加密套件或协议版本; - 系统时间不准确,导致证书验证失败。 解决方法包括: - 在客户端配置中禁用 SSL 验证(仅限测试环境),例如在 PHP 中设置 `stream_context_create` 的 `ssl` 选项为 `verify_peer` 为 `false`; - 安装可信证书或更新证书链; - 检查服务器 SSL/TLS 配置,确保使用兼容的加密套件协议版本[^2]。 服务器重定向策略也可能导致 GET 请求失败。例如,服务器使用 301 或 302 状态码进行重定向时,浏览器可能会将 POST 请求转换为 GET 请求,从而导致数据丢失或请求失败。若服务器期望继续使用 POST 方法,则应使用 307 或 308 状态码进行重定向,以保留原始请求方法。尽管该问题通常影响 POST 请求,但在某些特殊场景下也可能间接影响 GET 请求的执行路径。 此外,GET 请求的参数格式编码方式也需正确处理。例如,URL 中的参数应使用 `urlencode` 编码,避免特殊字符导致解析错误。若参数中包含空格或非 ASCII 字符,应进行 URL 编码处理,例如将空格替换为 `%20`。 示例代码如下,展示如何在 PHP 中安全发送 HTTPS GET 请求并处理 SSL 验证: ```php <?php $context = stream_context_create([ 'ssl' => [ 'verify_peer' => true, 'cafile' => '/path/to/cacert.pem', // 指定 CA 证书路径 ] ]); $response = file_get_contents('https://example.com/api/data', false, $context); if ($response === false) { $error = error_get_last(); echo "Request failed: " . $error['message']; } else { echo $response; } ?> ``` 对于并发请求性能问题,需要注意 HTTPS 连接的建立过程可能引入额外延迟。例如,重复的 HTTPS GET 请求可能因 SSL/TLS 握手过程而变慢。若多个请求串行执行而非并发处理,可能导致请求时间呈现线性增长。优化方法包括使用连接复用(keep-alive)、合理设置超时时间以及启用 HTTP/2 以提升并发性能[^3]。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值