V-IM-PRO压测报告

260 阅读4分钟

说明:此压测版本为集群版本,非开源的版本。

1. 测试概要

  • 测试目标:评估系统在2000 QPS高并发场景下的吞吐量及用户容量。
  • 测试工具:k6(受限于硬件性能,实际未达到理论峰值)。
  • 测试时长:100秒(实际因系统处理延迟延长至125秒,MQ堆积)。
  • 服务器配置
    • 3台机器(4核16GB),中间件与服务混部(非生产推荐架构)。

2. 关键指标

指标理论值实际值说明
请求发送总量200,000157,252硬件性能限制导致不足
请求发送成功率100%100%无请求丢失
系统处理总量-157,000每1000条合并记录导致误差
总处理时间100秒~125秒系统存在处理积压
实际吞吐量2,000 QPS1,258 QPS系统稳态处理能力
峰值处理能力-~3,200 QPS后25秒的追赶速率

3. 用户容量估算

  • 场景假设:用户每10秒发送1条数据(WebSocket长连接)。
    • 支持用户数:3,200 QPS × 10秒 ≈ 32,000活跃用户
    • 考虑20%活跃度:32,000 ÷ 0.2 ≈ 160,000总用户
  • 注意事项
    • 实际容量受其他HTTP请求、数据库IO等影响,需预留30%~50%冗余。
    • 当前测试未模拟混合请求(如API、数据库操作),生产环境需补充测试。

4. 性能瓶颈分析

  • 硬件限制
    • 中间件混部(MySQL/Redis/MongoDB/NGINX/.rocketMQ),资源争用影响性能。
    • 未使用Redis集群,单节点可能成为瓶颈。
    • k6测试机性能问题,不能模拟全量并发。
  • 处理延迟
    • 前100秒积压70,000条,后25秒处理80,000条,表明系统异步处理能力尚可但初始吞吐不足
    • 使用的是http测试的并发,大量的http连接会显著影响性能,后25秒因为没有http发送消息,处理性能大幅度提升。

5. 优化建议

  1. 架构调整
    • 中间件独立部署(如MySQL、Redis、rocketMQ、mongoDB、nginx)。
    • Redis启用集群模式,提升缓存吞吐量。
  1. 测试改进
    • 补充混合场景测试(HTTP+WebSocket+数据库操作)。
    • 使用分布式压测工具(如k6 Cloud)突破单机性能限制。
  1. 容量规划
    • 生产环境按500 QPS/节点规划(保留冗余)。

6. 结论

当前测试环境下,系统稳态吞吐量约1,258 QPS,理论可支持16万用户(20%活跃度)。但实际生产需考虑中间件分离、集群化部署及更复杂的业务场景,建议进一步优化后重新压测。


截图

压测脚本

import http from 'k6/http';
import { check } from 'k6';
import { Rate } from 'k6/metrics';
import execution from 'k6/execution'; // Import the execution module

// --- Configuration Options ---

// Create a custom metric to track failure rate specifically
const errorRate = new Rate('errors');

export const options = {
  // Use the constant-arrival-rate executor to maintain a specific request rate
  scenarios: {
    constant_request_rate: {
      executor: 'constant-arrival-rate',
      rate: 1500,        // Target requests per second
      timeUnit: '1s',    // Time unit for the rate (500 requests per 1 second)
      duration: '100s',    // Duration to maintain this rate (e.g., 1 minute)
      preAllocatedVUs: 100, // Initial number of VUs to allocate. Adjust based on response times.
                          // If response time is > 200ms (1000ms / 500 RPS), you'll need more VUs.
                          // Start with rate / avg_response_time_in_seconds roughly.
      maxVUs: 300,        // Maximum number of VUs K6 can use to try and meet the rate.
                          // Increased slightly as a precaution, adjust as needed.
    },
  },

  // IMPORTANT: Ignore TLS verification errors for IP address HTTPS
  insecureSkipTLSVerify: true,

  // Define thresholds
  thresholds: {
    http_req_failed: ['rate<0.01'], // http errors should be less than 1%
    'errors': ['rate<0.01'],        // Using our custom metric for checks failures
    http_req_duration: ['p(95)<800'], // 95% of requests should be below 800ms (adjust as needed)
    // You might need a threshold for the actual achieved RPS if meeting the target is critical
    // 'http_reqs{scenario:constant_request_rate}': ['count>=29500'], // Example: Ensure at least ~500*60 requests in 1 min
  },
};

// --- Main Test Logic ---
export default function () {
  // Get the globally unique, auto-incrementing iteration number for the current scenario
  const contentCounter = execution.scenario.iterationInTest;

  // Construct the URL dynamically with the incrementing counter
  const baseUrl = 'https://172.1.1.1:8080/vim/sdk/message/send/pushText';
  const chatId = 3;
  const chatType = 'friend';
  const url = `${baseUrl}?chatId=${chatId}&content=${contentCounter}&chatType=${chatType}`;

  // --- Define Custom Headers Here ---
  const customHeaders = {
    'sa-token': 'xxxx', // **重要**: 替换为你的认证 Token
    'Content-Type': 'application/json',        // Even if payload is empty, keep it if API expects it
    'X-Request-ID': `k6-${execution.vu.idInTest}-${execution.scenario.iterationInInstance}`, // Use more specific execution context vars
    'User-Agent': 'MyK6LoadTester/1.0-POST-Incremental',
    // Add other necessary headers
  };

  // --- Define the POST Request Body (Payload) ---
  // Since the data is in the URL parameters, the payload likely remains empty or null.
  // Confirm with your API specification.
  const payload = JSON.stringify({}); // Sending an empty JSON object
  // OR const payload = null; // If absolutely no body should be sent

  // Package headers for the request parameters
  const params = {
    headers: customHeaders,
    // Optional: Add tags for better reporting
    tags: {
        name: 'PushTextMessageIncremental', // Name this request in results
    },
  };

  // --- Send the HTTP POST Request ---
  const res = http.post(url, payload, params);

  // --- Check the Response ---
  const checkRes = check(res, {
    'status is 200 or 201': (r) => r.status === 200 || r.status === 201, // POST often returns 201 Created
    // 'response body contains success indicator': (r) => r.body.includes('"code":0'), // Uncomment and adapt if needed
  });

  // Record errors using our custom rate metric if the check fails
  if (!checkRes) {
    errorRate.add(1); // Record an error
    // Consider logging less frequently on high load to avoid log spam, or log only status/URL
    console.error(`Request failed! VU: ${execution.vu.idInTest}, Iter: ${execution.scenario.iterationInInstance}, Status: ${res.status}, URL: ${url}`); // Log failure info
  } else {
    errorRate.add(0); // Record success for the error rate calculation
  }

  // No explicit 'sleep' is needed with constant-arrival-rate executor.
}

// --- Teardown Function (Optional) ---
// export function teardown(data) {
//   console.log(`Test finished. Total iterations (requests): ${execution.scenario.iterationInTest}`);
//   // Note: iterationInTest in teardown might show the final count + 1
// }