Client
Client
是一个 Redis 客户端,用于与 Redis 服务器、Sentinel 或集群进行交互。它提供了一个基于 Promise 的 API,用户可以异步地与其交互。
尽管该 API 旨在提供全面和广泛的功能,但它并未暴露整个 Redis API。相反,其目的是暴露最适合 k6 用例的 Redis 功能。
使用
单节点服务器
您可以通过传入 URL 字符串创建一个新的 Client
实例,该实例连接到单个 Redis 服务器。URL 必须采用以下格式:
redis[s]://[[username][:password]@][host][:port][/db-number]
以下是一个 URL 字符串示例,它连接到运行在 localhost、默认端口 (6379) 并使用默认数据库 (0) 的 Redis 服务器:
import redis from 'k6/experimental/redis';
const client = new redis.Client('redis://:6379');
还可以使用选项对象实例化客户端,以支持更复杂的用例并提供更大的灵活性:
import redis from 'k6/experimental/redis';
const client = new redis.Client({
socket: {
host: 'localhost',
port: 6379,
},
username: 'someusername',
password: 'somepassword',
});
TLS
您可以通过几种方式配置 TLS 连接。
如果服务器的证书由公共证书颁发机构签名,您可以使用 rediss
URL scheme:
import redis from 'k6/experimental/redis';
const client = new redis.Client('rediss://example.com');
否则,您可以使用 socket.tls 对象提供您自己的 PEM 格式的自签名证书:
import redis from 'k6/experimental/redis';
const client = new redis.Client({
socket: {
host: 'localhost',
port: 6379,
tls: {
ca: [open('ca.crt')],
},
},
});
请注意,对于自签名证书,必须启用 k6 的 insecureSkipTLSVerify 选项(设置为 true
)。
TLS 客户端认证 (mTLS)
您还可以通过在 socket.tls 对象中设置两个额外属性来启用 mTLS:
import redis from 'k6/experimental/redis';
const client = new redis.Client({
socket: {
host: 'localhost',
port: 6379,
tls: {
ca: [open('ca.crt')],
cert: open('client.crt'), // client certificate
key: open('client.key'), // client private key
},
},
});
集群客户端
您可以通过使用 cluster 配置属性并传入 2 个或更多节点 URL 来连接 Redis 服务器集群:
import redis from 'k6/experimental/redis';
const client = new redis.Client({
cluster: {
// Cluster options
maxRedirects: 3,
readOnly: true,
routeByLatency: true,
routeRandomly: true,
nodes: ['redis://host1:6379', 'redis://host2:6379'],
},
});
或者与上面相同,但向 nodes 数组传入 socket 对象而不是 URL:
import redis from 'k6/experimental/redis';
const client = new redis.Client({
cluster: {
nodes: [
{
socket: {
host: 'host1',
port: 6379,
},
},
{
socket: {
host: 'host2',
port: 6379,
},
},
],
},
});
Sentinel 客户端
Redis Sentinel 提供高可用性功能,是 Redis 集群的替代方案。
您可以通过在传递给 Client
构造函数的对象中设置额外的选项来连接 Sentinel 实例:
import redis from 'k6/experimental/redis';
const client = new redis.Client({
username: 'someusername',
password: 'somepassword',
socket: {
host: 'localhost',
port: 6379,
},
// Sentinel options
masterName: 'masterhost',
sentinelUsername: 'sentineluser',
sentinelPassword: 'sentinelpass',
});
实际示例
import { check } from 'k6';
import http from 'k6/http';
import redis from 'k6/experimental/redis';
import exec from 'k6/execution';
import { textSummary } from 'https://jslib.k6.io/k6-summary/0.0.2/index.js';
export const options = {
scenarios: {
redisPerformance: {
executor: 'shared-iterations',
vus: 10,
iterations: 200,
exec: 'measureRedisPerformance',
},
usingRedisData: {
executor: 'shared-iterations',
vus: 10,
iterations: 200,
exec: 'measureUsingRedisData',
},
},
};
// Instantiate a new redis client
const redisClient = new redis.Client(`redis://:6379`);
// Prepare an array of rating ids for later use
// in the context of the measureUsingRedisData function.
const ratingIDs = new Array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
export async function measureRedisPerformance() {
// VUs are executed in a parallel fashion,
// thus, to ensure that parallel VUs are not
// modifying the same key at the same time,
// we use keys indexed by the VU id.
const key = `foo-${exec.vu.idInTest}`;
await redisClient.set(key, 1);
await redisClient.incrBy(key, 10);
const value = await redisClient.get(key);
if (value !== '11') {
throw new Error('foo should have been incremented to 11');
}
await redisClient.del(key);
if ((await redisClient.exists(key)) !== 0) {
throw new Error('foo should have been deleted');
}
}
export async function setup() {
await redisClient.sadd('rating_ids', ...ratingIDs);
}
export async function measureUsingRedisData() {
// Pick a random rating id from the dedicated redis set,
// we have filled in setup().
const randomID = await redisClient.srandmember('rating_ids');
const url = `https://quickpizza.grafana.com/api/ratings/${randomID}`;
const res = await http.asyncRequest('GET', url, {
headers: { Authorization: 'token abcdef0123456789' },
});
check(res, { 'status is 200': (r) => r.status === 200 });
await redisClient.hincrby('k6_rating_fetched', url, 1);
}
export async function teardown() {
await redisClient.del('rating_ids');
}
export function handleSummary(data) {
redisClient
.hgetall('k6_rating_fetched')
.then((fetched) => Object.assign(data, { k6_rating_fetched: fetched }))
.then((data) => redisClient.set(`k6_report_${Date.now()}`, JSON.stringify(data)))
.then(() => redisClient.del('k6_rating_fetched'));
return {
stdout: textSummary(data, { indent: ' ', enableColors: true }),
};
}
API
键值方法
方法 | Redis 命令 | 描述 |
---|---|---|
Client.set(key, value, expiration) | SET | 设置 key 持有 value ,存活时间等于 expiration 。 |
Client.get(key) | GET | 获取 key 的值。 |
Client.getSet(key, value) | GETSET | 原子地将 key 设置为 value 并返回存储在 key 的旧值。 |
Client.del(keys) | DEL | 移除指定的键。 |
Client.getDel(key) | GETDEL | 获取 key 的值并删除该键。 |
Client.exists(keys) | EXISTS | 返回存在的 key 参数的数量。 |
Client.incr(key) | INCR | 将存储在 key 的数字加一。 |
Client.incrBy(key, increment) | INCRBY | 将存储在 key 的数字加上 increment 。 |
Client.decr(key) | DECR | 将存储在 key 的数字减一。 |
Client.decrBy(key, decrement) | DECRBY | 将存储在 key 的数字减去 decrement 。 |
Client.randomKey() | RANDOMKEY | 返回一个随机键的值。 |
Client.mget(keys) | MGET | 返回所有指定键的值。 |
Client.expire(key, seconds) | EXPIRE | 设置键的超时时间,超时后该键将自动删除。 |
Client.ttl(key) | TTL | 返回设置了超时的键的剩余存活时间。 |
Client.persist(key) | PERSIST | 移除键上现有的超时设置。 |
列表方法
方法 | Redis 命令 | 描述 |
---|---|---|
Client.lpush(key, values) | LPSUH | 将所有指定的值插入到存储在 key 的列表头部。 |
Client.rpush(key, values) | RPUSH | 将所有指定的值插入到存储在 key 的列表尾部。 |
Client.lpop(key) | LPOP | 移除并返回存储在 key 的列表的第一个元素。 |
Client.rpop(key) | RPOP | 移除并返回存储在 key 的列表的最后一个元素。 |
Client.lrange(key, start, stop) | LRANGE | 返回存储在 key 的列表的指定元素。 |
Client.lindex(key, start, stop) | LINDEX | 返回存储在 key 的列表的指定元素。 |
Client.lset(key, index, element) | LSET | 将索引 index 处的列表元素设置为 element 。 |
Client.lrem(key, count, value) | LREM | 从存储在 key 的列表中移除前 count 个值为 value 的元素。 |
Client.llen(key) | LLEN | 返回存储在 key 的列表的长度。 |
哈希方法
方法 | Redis 命令 | 描述 |
---|---|---|
Client.hset(key, field, value) | HSET | 设置存储在 key 的哈希中指定字段的值为 value 。 |
Client.hsetnx(key, field, value) | HSETNX | 仅当 field 不存在时,才设置存储在 key 的哈希中指定字段的值为 value 。 |
Client.hget(key, field) | HGET | 返回存储在 key 的哈希中与 field 相关联的值。 |
Client.hdel(key, fields) | HDEL | 删除存储在 key 的哈希中指定的字段。 |
Client.hgetall(key) | HGETALL | 返回存储在 key 的哈希中的所有字段和值。 |
Client.hkeys(key) | HKEYS | 返回存储在 key 的哈希中的所有字段。 |
Client.hvals(key) | HVALS | 返回存储在 key 的哈希中的所有值。 |
Client.hlen(key) | HLEN | 返回存储在 key 的哈希中的字段数量。 |
Client.hincrby(key, field, increment) | HINCRBY | 将存储在 key 的哈希中 field 的整数值增加 increment 。 |
集合方法
方法 | Redis 命令 | 描述 |
---|---|---|
Client.sadd(key, members) | SADD | 将指定的成员添加到存储在 key 的集合中。 |
Client.srem(key, members) | SREM | 从存储在 key 的集合中移除指定的成员。 |
Client.sismember(key, member) | SISMEMBER | 判断 member 是否是存储在 key 的集合的成员。 |
Client.smembers(key) | SMEMBERS | 返回存储在 keys 的集合中的所有成员值。 |
Client.srandmember(key) | SRANDMEMBER | 从存储在 key 的集合值中返回一个随机元素。 |
Client.spop(key) | SPOP | 从存储在 key 的集合值中移除并返回一个随机元素。 |
杂项
方法 | 描述 |
---|---|
Client.sendCommand(command, args) | 向 Redis 服务器发送命令。 |