k6 协议支持
(Grafana k6 登堂入室, Part 14)
k6 不只是 HTTP 测试工具,它还支持 WebSocket、gRPC 和 SSL/TLS 等协议。这篇把它们过一遍。
HTTP/2
k6 在发送 HTTPS 请求时,会自动和服务器协商升级到 HTTP/2(如果服务器支持的话),和浏览器的行为一样,不需要额外配置。
如果想确认某个请求实际用的是什么协议,可以检查响应对象的 proto 属性:
import http from 'k6/http';
import { check, sleep } from 'k6';
export default function () {
const res = http.get('https://quickpizza.grafana.com');
check(res, {
'protocol is HTTP/2': (r) => r.proto === 'HTTP/2.0',
});
sleep(1);
}
HTTP/2 相比 HTTP/1.1 的主要改进是在单个 TCP 连接上实现了多路复用——多个请求/响应可以并行传输,不用排队等待。不过 TCP 层的丢包重传仍然会影响所有流,这个问题要到 HTTP/3(基于 QUIC/UDP)才能彻底解决。
WebSocket
WebSocket 提供全双工的通信通道,常见于需要服务端推送的场景——比如聊天、实时通知、在线协作。
k6 提供了两个 WebSocket 模块:
k6/ws:早期版本,基于回调k6/websockets:较新版本,API 更接近浏览器标准,性能更好,推荐用这个
和 HTTP 测试不同,WebSocket 测试不是反复执行 default 函数,而是建立连接后通过事件循环来处理消息。基本结构:
import ws from 'k6/ws';
import { check } from 'k6';
export default function () {
const url = 'wss://echo.websocket.org';
const params = { tags: { my_tag: 'hello' } };
const res = ws.connect(url, params, function (socket) {
socket.on('open', () => console.log('connected'));
socket.on('message', (data) => console.log('Message received: ', data));
socket.on('close', () => console.log('disconnected'));
});
check(res, { 'status is 101': (r) => r && r.status === 101 });
}
ws.connect() 的第三个参数是一个回调函数,接收 socket 对象。在这个函数里注册事件处理器后,k6 会阻塞直到连接关闭。
定时器和超时
可以用 socket.setInterval 做周期性操作(比如定时发 ping),用 socket.setTimeout 设置超时后关闭连接:
import ws from 'k6/ws';
import { check } from 'k6';
export default function () {
const url = 'wss://echo.websocket.org';
const res = ws.connect(url, {}, function (socket) {
socket.on('open', function () {
console.log('connected');
// 每秒发一次 ping
socket.setInterval(function () {
socket.ping();
console.log('Pinging every 1sec');
}, 1000);
});
socket.on('ping', () => console.log('PING!'));
socket.on('pong', () => console.log('PONG!'));
socket.on('close', () => console.log('disconnected'));
// 2 秒后关闭连接
socket.setTimeout(function () {
console.log('2 seconds passed, closing the socket');
socket.close();
}, 2000);
});
check(res, { 'status is 101': (r) => r && r.status === 101 });
}
错误处理
通过 error 事件捕获连接过程中的错误:
socket.on('error', function (e) {
if (e.error() != 'websocket: close sent') {
console.log('An unexpected error occurred: ', e.error());
}
});
gRPC
gRPC 是 Google 开源的 RPC 框架,使用 Protocol Buffers 做序列化,二进制传输,比 JSON 更快更紧凑。k6 从 v0.49.0 开始在核心模块 k6/net/grpc 中支持 gRPC,包括一元调用和流式调用。
加载 Proto 定义
k6 在发送 gRPC 请求前需要知道消息和服务的定义。两种方式:
从本地 .proto 文件加载:
import { Client } from 'k6/net/grpc';
const client = new Client();
client.load(['definitions'], 'hello.proto');
或者使用 gRPC 反射协议自动发现(需要服务端支持):
import { Client } from 'k6/net/grpc';
const client = new Client();
client.connect('127.0.0.1:10000', { reflect: true });
一元调用
一元调用和普通 HTTP 请求类似——发一个请求,收一个响应:
import { Client, StatusOK } from 'k6/net/grpc';
import { check, sleep } from 'k6';
const client = new Client();
client.load(['definitions'], 'hello.proto');
export default () => {
client.connect('127.0.0.1:10000', {});
const data = { greeting: 'Bert' };
const response = client.invoke('hello.HelloService/SayHello', data);
check(response, {
'status is OK': (r) => r && r.status === StatusOK,
});
console.log(JSON.stringify(response.message));
client.close();
sleep(1);
};
流式调用
k6 支持服务端流、客户端流和双向流。以服务端流为例:
import { Client, Stream } from 'k6/net/grpc';
import { sleep } from 'k6';
const COORD_FACTOR = 1e7;
const client = new Client();
export default () => {
if (__ITER == 0) {
client.connect('127.0.0.1:10000', { plaintext: true, reflect: true });
}
const stream = new Stream(client, 'main.FeatureExplorer/ListFeatures', null);
stream.on('data', function (feature) {
console.log(
`Found feature called "${feature.name}" at ${feature.location.latitude / COORD_FACTOR}, ${
feature.location.longitude / COORD_FACTOR
}`
);
});
stream.on('end', function () {
client.close();
console.log('All done');
});
// 发送请求
stream.write({
lo: { latitude: 400000000, longitude: -750000000 },
hi: { latitude: 420000000, longitude: -730000000 },
});
sleep(0.5);
};
客户端流和双向流的 API 类似,区别在于客户端可以多次调用 stream.write() 发送消息,最后调用 stream.end() 关闭发送端。
SSL/TLS
k6 默认就支持 TLS,只要你的请求 URL 用 https 就行。除此之外,k6 还提供了几个更细粒度的 TLS 配置:
- TLS 版本和密码套件:可以限制只使用特定版本的 TLS 或特定的密码套件,用于测试安全合规性
- 客户端证书:支持 mTLS(双向 TLS),在需要客户端证书认证的场景下使用
- OCSP:在线证书状态协议,用于验证证书是否被吊销
response.timings.tls_handshaking 记录了 TLS 握手的耗时,方便你分析 TLS 对请求延迟的影响。