使用k6下载整个网站

7

我目前正在评估k6是否适合我们的负载测试需求。我们有一个相当传统的网站架构,使用Apache Web服务器、PHP和MySQL数据库。使用k6发送简单的HTTP请求看起来很简单,我认为我们可以通过它来测试所有主要功能,因为我们不太依赖JavaScript,并且大多数页面都是静态的。

然而,我不确定如何处理在请求返回的HTML中引用的资源(样式表、图像等)。我们也需要加载它们,因为这有时会导致数据库请求,这必须成为负载测试的一部分。

k6中是否有一些开箱即用的功能,允许您像浏览器一样加载所有资源?我知道k6不会呈现页面,我也不需要它这样做。我只需要请求HTML中的所有资源。

2个回答

6

你基本有两个选项,都有其注意事项:

  1. 记录会话 - 你可以像示例中所示那样直接从浏览器导出har文件,或者使用为你的浏览器制作的扩展,这里是FirefoxChrome。两者都可以在不需要k6云账户的情况下使用,你只需要设置它们下载har文件,当你停止时它们将自动(但有点悄悄地)下载。然后使用k6 har转换器(已过时,但仍然可用),或者新的har-to-k6,将其转换成脚本。

    如果你有许多页面和/或资源,这种方法特别好,并且即使你有单页样式的应用程序,它也可以工作,因为它只获取浏览器请求的内容作为HAR,然后将其转换为脚本。如果没有动态输入的内容(用户名/密码),则最终脚本通常可以直接使用。

    这种方法的最大问题是,如果你添加一个CSS文件,你就需要重新进行整个操作。如果每次更改时CSS/JS文件名发生变化之类的情况,这就更加棘手了。针对这种情况,下一种方法就很有用:

  2. 使用parseHTML,然后找到你关心的元素并请求它们。
import http from "k6/http";
import {parseHTML} from "k6/html";

export default function() {
    const res = http.get("https://stackoverflow.com");
    const doc = parseHTML(res.body);
    doc.find("link").toArray().forEach(function (item) {
        console.log(item.attr("href"));
        // make http gets for it
        // or added them to an array and make one batch request
     });
}

将会产生

NFO[0001] https://cdn.sstatic.net/Sites/stackoverflow/img/favicon.ico?v=4f32ecc8f43d
INFO[0001] https://cdn.sstatic.net/Sites/stackoverflow/img/apple-touch-icon.png?v=c78bd457575a
INFO[0001] https://cdn.sstatic.net/Sites/stackoverflow/img/apple-touch-icon.png?v=c78bd457575a
INFO[0001] /opensearch.xml
INFO[0001] https://cdn.sstatic.net/Shared/stacks.css?v=53507c7c6e93
INFO[0001] https://cdn.sstatic.net/Sites/stackoverflow/primary.css?v=d3fa9a72fd53
INFO[0001] https://cdn.sstatic.net/Shared/Product/product.css?v=c9b2e1772562
INFO[0001] /feeds
INFO[0001] https://cdn.sstatic.net/Shared/Channels/channels.css?v=f9809e9ffa90

如您所见,其中一些url是相对路径而不是绝对路径,因此您需要处理这个问题。在这个例子中,只有一些是css,因此可能需要进行更多的过滤。

问题在于您需要编写代码,如果添加了相对链接或其他内容,则需要处理它。幸运的是,k6可以脚本化,因此您可以重复使用代码:D。


6

我遵循了 Михаил Стойков 的建议,并编写了自己的函数来加载资源。您可以通过 options.concurrentResourceLoading 设置资源的加载方式(批量或连续获取)。

/**
 * @param {http.RefinedResponse<http.ResponseType>} response
 */
export function getResources(response) {
  const resources = [];
  response
    .html()
    .find('*[href]:not(a)')
    .each((index, element) => {
      resources.push(element.attributes().href.value);
    });
  response
    .html()
    .find('*[src]:not(a)')
    .each((index, element) => {
      resources.push(element.attributes().src.value);
    });

  if (options.concurrentResourceLoading) {
    const responses = http.batch(
      resources.map((r) => {
        return ['GET', resolveUrl(r, response.url), null, {
          headers: createHeader()
        }];
      })
    );
    responses.forEach(() => {
      check(response, {
        'resource returns status 200': (r) => r.status === 200,
      });
    });
  } else {
    resources.forEach((r) => {
      const res = http.get(resolveUrl(r, response.url), {
        headers: createHeader(),
      });
      !check(res, {
        'resource returns status 200': (r) => r.status === 200,
      });
    });
  }
}

这里缺少很多定义,比如resolveUrlcreateHeader - Yuriy Sountsov
我已经创建了这个Gist,并提供了完整的示例(我仅使用了并发加载):https://gist.github.com/tetebueno/64ea08aed61ed1772ae7a67c778f64c8 - tete

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接