Http性能 - 许多小请求或一个大请求

Mic*_*ich 16 performance http

场景:

  1. 在我的网站上,我展示了书籍.
  2. 用户可以将每本书添加到"稍后阅读"列表中.

行为:

当用户进入该站点时,他们会看到一系列书籍.其中一些已经在他们的"Read Later"列表中,有些则不是.

用户在每本书旁边都有一个指示,告诉他们该书是否已被添加到列表中.

我的问题

我在辩论哪种选择对我的情况是理想的.

选项1:

  1. 对于每本书,查询服务器是否已存在于用户列表中.
  2. 更新每本书的指标.

Pro: 对服务器的请求非常小,响应非常简单(真或假).

Con:在一本有30本书的页面中,我将发送30个单独的http请求,它们可以阻止套接字,并且考虑到浏览器和服务器必须为每个事务执行整个握手,这个请求相当慢.

选项2:

  1. 我查询服务器一次,并获得一个响应,其中包含"Read Later"列表中的完整书籍列表作为数组.
  2. 在浏览器中,我查看了数组,并根据它是否存在于数组中来更新每本书的指示.

亲:我只提出一个请求,并立即更新所有书籍的指标.

Con: "Read Later"列表可能有数百本书,传递一个大数组可能会证明是缓慢而过度的.特别是在屏幕上没有出现30本书的情况下,只有2-3本.(也就是说,我想检查某个书是否在列表中,为此我让服务器从列表中向客户端发送整个书籍列表).

所以,

你会采用哪种方式来最大限度地提高性能:1还是2?

我有什么替代品吗?

Sta*_*kin 18

实时测试、解决方案和真实数据

这个答案是用 JavaScript 编写的,并包含易于理解的代码示例。

介绍

OP 询问向“稍后阅读”API 发出请求的最有效方法是什么,每个请求都需要等待一段时间,而后端会保存书籍。

对于这个答案,我创建了一个“稍后阅读”API 端点的演示,每个请求都会随机等待 70-130 毫秒以保存每本书。

我每次都在所有场景中测试 30 本书。

最后,我们将通过专业地测量我们将采取的每个操作的真实运行时间来看到每种方法的最佳结果。

同步请求(OP 的选项 1)

在这里,我们将通过 JS 同步运行每个调用。

代码:

async function saveBooksSync() {
    console.time('save-books-sync');

    // creates 30 book IDs
    const booksIds = Array.from({length: 30}, (_, i) => i + 1);

    // creates 30 API links for each request
    const urls = booksIds.map(bookId => `http://localhost:7777/books/read-later?bookId=${bookId}`);

    for(let url of urls) {
        const response = await fetch(url);
        const json = await response.json();
        console.log(json);
    }
    console.timeEnd('save-books-sync');
}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

运行时间:3712.40087890625 毫秒

一大请求

尽管我们不会创建许多与服务器的请求连接,但运行时本身就说明了一切。

代码:

async function saveAllBooksAtOnce() {
    console.time('save-all-books')
    const booksIds = Array.from({length: 30}, (_, i) => i + 1);
    const url = `http://localhost:7777/books/read-later?all=1`;

    const response = await fetch(url);
    const json = await response.json();
    console.timeEnd('save-all-books');
}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述 在此输入图像描述

运行时间:3486.71484375 毫秒

并行异步请求(解决方案)

神奇的事情发生了,问题的解决方案是什么是最有效的请求方法。

在这里,我们提出了30 个并行的小请求,并取得了惊人的结果。

代码:

async function saveBooksParallel() {
    console.time('save-books')
    const booksIds = Array.from({length: 30}, (_, i) => i + 1);
    const urls = booksIds.map(bookId => `http://localhost:7777/books/read-later?bookId=${bookId}`);

    const promises = urls.map((url) =>
        fetch(url).then((response) => response.json())
    );

    const data = await Promise.all(promises);

    console.log(data);
    console.timeEnd('save-books');
}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

在这个异步并行示例中,我使用了该Promise.all方法。

Promise.all() 方法将可迭代的 Promise 作为输入,并返回单个 Promise,该 Promise 解析为输入 Promise 结果的数组

运行时间:668.47705078125 毫秒

结论

结果很清楚,发出这些多个请求的最有效方法是在异步并行中执行此操作。

更新:我按照@Iglesias Leonardo的请求删除了数据输出的console.log(),因为(大概)它需要大量资源。

这些是运行时结果:

  • 同步请求:3371.695 毫秒
  • 一大请求:3358.269 毫秒
  • 并行异步请求:613.506

更新结论: 运行时间几乎保持不变,从而反映了并行异步请求在速度上无法匹敌的现实

在此输入图像描述


Gho*_*ica 12

我认为2017年的解决方案不仅关乎整体性能,还关乎用户体验和用户期望.

而现在用户不能容忍延迟.从这个意义上讲,复杂的用户界面尽可能快地响应.因此:如果您可以使用这些小请求来启用用户执行某些操作(而不是等待一个大请求返回2秒),您应该更喜欢该解决方案.

据我所知,有很多"高保真"网站,有一个页面可能会发送50个,100个请求......所以我会考虑这种常见做法.

也许它在这里很有用:se-radio.net播客第277集在尾部延迟的背景下深入讨论了这个主题.


小智 10

选项 1 听起来不错,但在可扩展性方面存在很大问题。

选项 2 缓解了这种可扩展性问题,我们可以改进其设计:

客户端,通过 javascript,仅收集显示的书籍 ID 并通过 ajax 查询一次,以获取一系列稍后阅读的信息,仅适用于这 30 本书。通过这种方式,您仍然可以快速提供页面并请求一小组额外信息,一次使用单个 http 请求。

在服务器端,您可以进一步改进缓存每个用户稍后读取 id 的内存数组。