k6: Công cụ dễ dàng và hiệu quả cho load testing (Phần 2)

Trong phần này, ta sẽ cùng tìm hiểu về cấu trúc đầy đủ của một test script, khái niệm tags, checks và những options đặc trưng như thresholds, scenarios của công cụ k6 nhé.

Upload image

Trong phần trước của bài viết, chúng ta đã được làm quen cũng như biết được cấu trúc và các khái niệm cơ bản của k6. Tuy nhiên, trên thực tế, các test script sẽ phải mô phỏng những trường hợp khác nhau của tải. Do đó, để có thể xử lý tốt các yêu cầu đề ra, chúng ta nên biết những khái niệm sau đây để dễ dàng áp dụng vào test script trong k6.

1. Checks (bài kiểm tra)

Checks dùng để kiểm tra một điều kiện đúng sai trong test của bạn và xuất ra trong test output.

Người ta thường dùng checks để kiểm tra xem response trả về của request có đúng như mong đợi hay không.

Ví dụ: Sử dụng checks để kiểm tra các response trả về có trả về status 200 OK hay không, cùng với 2 checks kiểm tra tính chất của body trả về.

import { check } from 'k6';
import http from 'k6/http';

export default function () {
  const res = http.get('http://test.k6.io/');
  check(res, {
    'is status 200': (r) => r.status === 200,
    'body size is 11,105 bytes': (r) => r.body.length == 11105,
    'is body contain k6': (r) => r.body.includes('k6')
  });
}

Mình sẽ chạy test trên với 20 VUs và 100 iterations.

k6 run --vus 20 --iterations 100 checks.js

Kết quả cho ra như sau:

Upload image

Ở test output, chúng ta đã thấy xuất hiện kết quả của các checks trong script. Ở đây mình cho chạy 20 VUs với tất cả 100 vòng lặp VU code, kết quả cho thấy phần test “body size” bị fail.

Chú ý là các checks không liên quan đến nhau và nếu check này bị fail thì mấy check kia sẽ không bị dừng hay ảnh hưởng gì nhé, do đó tổng số checks mà mình có ở hình trên là đúng 300 checks. Tại vì mỗi vòng lặp của default function sẽ chạy 3 checks như mình định nghĩa ở trên và do test script được chạy với cấu hình là 100 iterations nên số checks cần phải được kiểm tra là 300, đúng với những gì mình mong đợi.

2. Thresholds (Ngưỡng)

Thresholds là những tiêu chí mà bạn mong muốn hệ thống của mình đạt được. Nó là ngưỡng để xem xét test của bạn là pass hay fail.

Sau đây là cách cấu hình thresholds:

export const options = {
    thresholds: {
      metric_name: [
        {
          threshold: 'p(99) < 10', // string
          abortOnFail: true, // boolean
          delayAbortEval: '10s', // string
          /*...*/
        },
      ],
    },
  };

Hoặc một cách ngắn gọn:

export const options = {
    thresholds: {
      metric_name: ['p(99) < 10'],
    },
  };

Trong đó:

  • metric_name là thành phần những thông số trong output bạn muốn đặt thresholds.
  • threshold là phần định nghĩa thresholds lên thông số. Tùy theo từng loại metric mà có cách định nghĩa khác nhau, các bạn có thể xem thêm tại đây nhé.
  • abortOnFail xác định xem có nên ngưng test ngay tại thời điểm test không thỏa mãn thresholds hay không.
  • delayAbortEval do quá trình xét thresholds là liên tục nên nếu ta muốn delay một khoảng thời gian để thu thập thêm dữ liệu giữa những lần xét thì có thể setting ở đây.

Chúng ta thử đi qua một ví dụ nhé:

import http from 'k6/http';
import { check } from 'k6';

export const options = {
  vus: 50,
  duration: '10s',
  thresholds: {
    // the rate of successful checks should be higher than 90%
    checks: ['rate>0.9'],
  },
};

export default function () {
  const res = http.get('http://test.k6.io/');
  check(res, {
    'is status 200': (r) => r.status === 200,
    'body size is 11,105 bytes': (r) => r.body.length == 11105,
    'is body contain k6': (r) => r.body.includes('k6')
  });
}

Ví dụ này mình kết hợp giữa checksthresholds để xem xét một test case của mình là pass hay fail.

Upload image

Kết quả thì các bạn cũng đoán được đúng không? Sẽ có khoảng 1/3 checks fail, cho nên rate sẽ không thể >90% được, vì vậy xuất hiện dấu X bên trái thông số checks và test sẽ trả về failed với lý do some thresholds have failed.

3. Tags (nhãn)

Gán nhãn (tag) là thành phần bổ trợ rất tốt trong quá trình viết script cho k6.

Chúng ta có thể gán nhãn những thành phần như requests, checks, thresholds, custom metrics để dễ phân loại, hình dung và sử dụng trong suốt quá trình viết test script và xem output.

Sau đây là một ví dụ về cách sử dụng tags kết hợp với thresholds ở phần trên:

import http from 'k6/http';
import { sleep } from 'k6';

export const options = {
  thresholds: {
    'http_req_duration{type:API}': ['p(95)<1000'], // threshold on API requests only
    'http_req_duration{type:staticContent}': ['p(95)<500'], // threshold on static content only
  },
};

export default function () {
  //api request
  const apiRes1 = http.get('https://test-api.k6.io/public/crocodiles/1/', {
    tags: { type: 'API' },
  });
  const apiRes2 = http.get('https://test-api.k6.io/public/crocodiles/2/', {
    tags: { type: 'API' },
  });

  //static content request
  const contentResponses = http.batch([
    ['GET', 'https://test-api.k6.io/static/favicon.ico', null, { tags: { type: 'staticContent' } }],
    [
      'GET',
      'https://test-api.k6.io/static/css/site.css',
      null,
      { tags: { type: 'staticContent' } },
    ],
  ]);

  sleep(1);
}

(Lưu ý: trong ví dụ trên có sử dụng batch, bạn tìm hiểu thêm tại đây nhé)

Qua ví dụ, các bạn có thể thấy chúng ta vừa gán nhãn cho 2 requests chịu trách nhiệm về phần API, 2 requests chịu trách nhiệm về phần static content, nhờ đó mà ở phần options chúng ta có thể định nghĩa thresholds một cách rõ ràng nhờ các tags này. Test output cũng được phân biệt rõ ràng và sẽ có dạng như sau:

Upload image

Ngoài ra còn có một khái niệm khác liên quan đến tags là groups, nếu muốn các bạn có thể tham khảo thêm tại đây.

4. Scenarios (kịch bản)

Scenarios là một phần rất thú vị và quan trọng của k6. Được định nghĩa là những kịch bản chi tiết trong quá trình diễn ra test, scenarios có liên quan đến số lượng VUs (người dùng ảo) và iterations. Nó thường được dùng để mô tả những tình hình thực tế của tải, ví dụ như sự tăng giảm đột ngột của truy cập.

Chúng ta sẽ định nghĩa scenarios ở phần options, cụ thể như sau:

export const options = {
  scenarios: {
    example_scenario: {
      // name of the executor to use
      executor: 'shared-iterations',

      // common scenario configuration
      startTime: '10s',
      gracefulStop: '5s',
      env: { EXAMPLEVAR: 'testing' },
      tags: { example_tag: 'testing' },

      // executor-specific configuration
      vus: 10,
      iterations: 200,
      maxDuration: '10s',
    },
    another_scenario: {
      /*...*/
    },
  },
};

Có 3 thành phần chính của một scenario option:

Executor

Executor (bộ phận thực thi) sắp xếp khối lượng công việc của VU trong k6.

Chúng ta sẽ dùng những executors được định nghĩa sẵn để đưa vào options. Các executors được định nghĩa sẵn được phân loại theo các tiêu chí:

  • Dựa theo số lượng iterations cho mỗi VU

    • shared-iterations: các VUs sẽ tính chung iterations và chạy đủ iterations ở phần cấu hình
    • per-vu-iterations: mỗi VU sẽ chạy đúng số iterations đã định nghĩa ở phần cấu hình
  • Dựa theo số lượng VUs

    • constant-VUs: các VUs sau khi khởi tạo sẽ được giữ cố định trong suốt quá trình test
    • ramping-VUs: số lượng VUs sẽ thay đổi theo thời gian
  • Dựa theo lượng iterations trên đơn vị thời gian, gọi là iteration rate

    • constant-arrival-rate: giữ số lượng iterations ở mức cho trước và ổn định theo thời gian
    • ramping-arrival-rate: tăng giảm số lượng iterations theo thời gian

Common configuration

Cấu hình chung cho cả kịch bản, gồm những loại như thời gian bắt đầu (startTime), nhãn (tags),…

Specific configuration

Với mỗi loại executor thì sẽ có những cấu hình đặc trưng, cần phải được khai báo.

Mời các bạn xem kỹ hơn về các executor và specific configuration của nó tại đây.

Thử nghiệm

Để dễ dàng hình dung, chúng ta cùng tạo thử và áp dụng nhé.

import http from 'k6/http';

export const options = {
  discardResponseBodies: true,
  scenarios: {
    contacts: {
      executor: 'ramping-vus',
      exec: 'contacts',
      startVUs: 0,
      stages: [
        { duration: '20s', target: 50 },
        { duration: '10s', target: 0 },
      ],
      gracefulRampDown: '0s',
    },
    news: {
      executor: 'per-vu-iterations',
      exec: 'news',
      vus: 50,
      iterations: 100,
      startTime: '30s',
      maxDuration: '1m',
    },
  },
};

export function contacts() {
  http.get('https://test.k6.io/contacts.php', {
    tags: { my_custom_tag: 'contacts' },
  });
}

export function news() {
  http.get('https://test.k6.io/news.php', { tags: { my_custom_tag: 'news' } });
}

Nhìn vào script, chúng ta có một scenario tên là contacts dùng executor ramping-vus, cái còn lại là news dùng executor per-vu-iterations. Trong scenario contacts, ta có thể thấy các specific options:

  • startVUs: chỉ định số VUs khởi đầu
  • stages: miêu tả sự thay đổi của số lượng VUs
  • gracefulRampDown: thời gian chờ các iterations đã start hoàn thành trước khi tắt VU đang chạy nó.

Như vậy, script này mô tả có một lượng user truy cập liên tục vào trang contacts, tăng từ 0 lên 50 trong 20s rồi giảm về lại 0 trong vòng 10s tiếp theo. Sau 30s đó, có 50 user vào trang news, mỗi người truy cập 100 lần.

Ở ví dụ này, bên cạnh giới thiệu về cách cấu hình scenarios, ta cũng thấy được:

  • Cách định nghĩa hành động test nhưng không dùng default function.
  • Cách sử dụng startTime để sắp xếp thứ tự chạy cho các scenarios.

Upload image

Mình khuyến khích các bạn tự chạy để xem kết quả như thế nào nhé.

Kết luận

Như vậy, chúng ta vừa đi qua các khái niệm và thành phần hỗ trợ việc tạo nên k6 test script. Nếu bạn thấy việc tạo test script khó nhằn quá thì k6 Cloud có thể hỗ trợ bạn tạo script bằng giao diện thân thiện với những người không phải lập trình viên (non-programmers), các bạn có thể tham khảo thêm ở đây nhé.

Lại một bài viết dài nữa, cảm ơn các bạn đã đọc tới đây, hy vọng bài viết sẽ giúp ích cho các bạn trong quá trình tìm hiểu và sử dụng tool này.

Tham khảo: Thư Viện k6

Atekco - Home for Authentic Technical Consultants