You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

k6持续发送指定并发HTTP请求时性能不达预期的问题排查

k6持续发送指定并发HTTP请求时性能不达预期的问题排查

看起来你遇到的问题核心是k6单线程运行时的事件循环阻塞加上测试配置(Iterations)的错误设置,导致请求发送速率远低于预期。我来一步步拆解问题原因,再给出针对性的修复方案:

一、问题根源分析

1. 错误的Iterations配置限制了测试执行次数

你在options里设置了iterations: 12 + vus:12,这意味着整个测试只会执行12次迭代(每个VU刚好跑1次)。虽然你在default函数里用while(Date.now() - startTime < duration)试图让测试持续10秒,但k6的迭代模型中,单个迭代的执行逻辑会被事件循环的单线程特性限制,没办法高效持续发起异步请求。

2. 同步while循环阻塞了k6的事件循环

k6的JavaScript运行时是单线程的,你在default函数里写的同步while循环会持续占用线程资源。哪怕用了http.asyncRequest(异步请求),但发起请求的for循环、while循环的判断都是同步操作,会卡住事件循环,导致k6无法及时调度新的异步请求,甚至无法处理已发起请求的回调逻辑,最终请求发送速率被严重限制。

3. 手动速率控制的精度极低

你试图用while循环 + 可选SLEEP_INTERVAL来控制每秒100次请求,但这种手动方式完全依赖同步代码的执行速度,既不精准,又容易触发单线程瓶颈。当你把NUM_EVENTS调到1000时,单次循环要发起1000个请求,k6的单线程根本处理不过来,导致1秒和10秒的总请求数没区别——本质是线程已经被占满,没办法发起更多请求了。

二、修复方案:用k6原生特性重构测试

我们需要抛弃手动while循环的方式,改用k6推荐的Scenarios(场景)和速率控制函数来实现精准的请求发送逻辑,同时避免事件循环阻塞。

1. 核心修改思路

  • 移除iterations配置,改用duration或Scenario控制测试时长
  • 用k6内置的速率控制(如rate()函数或Scenario的arrival-rate executor)来精准控制每个VU每秒发送100次请求
  • 保持每个VU对应一个URL的映射逻辑,避免资源竞争

2. 修复后的完整代码示例

import { open } from 'k6/x/file';
import { parseCSV } from 'https://jslib.k6.io/k6-utils/1.4.0/index.js';
import { check, rate } from 'k6';
import encoding from 'k6/encoding';
import http from 'k6/http';

export let options = {
    vus: 12,  // 12个VU对应12个URL
    duration: '10s', // 测试总时长10秒,替代手动while循环的时长判断
    insecureSkipTLSVerify: true,
    thresholds: {
        http_req_duration: ['p(95)<2000'],
        http_req_failed: ['rate<0.01']
    }
};

// **API Endpoints & Authentication for Different VUs**
const CREDENTIALS = [
  { url: 'url1', username: 'user1', password: 'pass1' },
  { url: 'url2', username: 'user2', password: 'pass2' },
  // ... 补充剩下10个配置
]; // 确保数组长度为12

const NUM_EVENTS_PER_SECOND = 100;
const ENTITY_PREFIX = "ABC";
const getResponse = false;

// 加载测试数据(请替换为你的CSV路径)
const parsedCSV = parseCSV(open('./your-test-data.csv'));

// **Main Test Execution**
export default function () {
    // 每个VU对应唯一的凭证
    const userIndex = (__VU - 1) % CREDENTIALS.length;
    const API_URL = CREDENTIALS[userIndex].url;
    const USERNAME = CREDENTIALS[userIndex].username;
    const PASSWORD = CREDENTIALS[userIndex].password;

    // 用k6的rate函数严格控制每秒发送100次请求
    rate(NUM_EVENTS_PER_SECOND);

    // 循环复用测试数据(避免数据耗尽)
    const dataIndex = (__ITER - 1) % parsedCSV.length;
    let event = parsedCSV[dataIndex];

    // 处理请求payload
    event.entity = `${ENTITY_PREFIX}_${event.entity}`;
    event.entityid = event.entity;
    event.parameterid = event.parametername;
    let payload = JSON.stringify(event);

    // 构造请求参数
    let params = {
        headers: {
            'Content-Type': 'application/json',
            'Authorization': 'Basic ' + encoding.b64encode(`${USERNAME}:${PASSWORD}`)
        },
        insecureSkipTLSVerify: true,
    };

    // 发起异步请求
    let request = http.asyncRequest('POST', API_URL, payload, params);
    
    // 可选:处理响应(如果需要)
    if (getResponse) {
        request.then(res => {
            console.log(`🔍 VU ${__VU} | Response ${__ITER} | Status: ${res.status}`);
            check(res, {
                "is status 200": (r) => r.status === 200
            });
        }).catch(error => {
            console.error(`❌ VU ${__VU} | Request ${__ITER} failed: ${error.message}`);
        });
    }
}

3. 额外优化建议

  • 如果需要更精准的请求到达率:可以改用k6的constant-arrival-rate场景执行器,它能严格控制每秒发起的请求总数,适合需要精准流量模型的测试。示例配置如下:

    export let options = {
        insecureSkipTLSVerify: true,
        thresholds: {
            http_req_duration: ['p(95)<2000'],
            http_req_failed: ['rate<0.01']
        },
        scenarios: {
          targeted_request_rate: {
            executor: 'constant-arrival-rate',
            rate: 1200, // 总请求率:12个URL * 100请求/秒 = 1200请求/秒
            timeUnit: '1s', // 速率单位
            duration: '10s', // 测试时长
            preAllocatedVUs: 12, // 预分配12个VU对应12个URL
            maxVUs: 12, // 最大VU数和预分配一致,避免动态扩容
          },
        },
    };
    

    这种模式下,每个VU会平均承担100请求/秒的压力,刚好匹配你的需求。

  • 避免测试数据不足:确保parsedCSV的长度至少是10秒 * 100请求/秒 = 1000条每个VU,或者在代码里做好数据循环复用的逻辑,避免因数据耗尽导致请求中断。

  • 关闭不必要的响应处理:如果getResponse为false,完全可以去掉request.then()的逻辑,减少事件循环的回调处理压力,进一步提升请求发送效率。

三、验证效果

修改完成后,你可以直接运行脚本查看k6终端输出的http_reqs指标,正常情况下应该能达到12 * 100 *10 = 12000次请求的预期。如果还是有瓶颈,可以检查本地机器的CPU/网络资源(k6是单线程工具,高并发场景下可能需要多实例分布式测试)。


备注:内容来源于stack exchange,提问作者sarath chandra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.13 19:19:30