菜单
开源 RSS

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 服务器:

JavaScript
import redis from 'k6/experimental/redis';

const client = new redis.Client('redis://:6379');

还可以使用选项对象实例化客户端,以支持更复杂的用例并提供更大的灵活性:

JavaScript
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:

JavaScript
import redis from 'k6/experimental/redis';

const client = new redis.Client('rediss://example.com');

否则,您可以使用 socket.tls 对象提供您自己的 PEM 格式的自签名证书:

JavaScript
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:

JavaScript
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 服务器集群:

JavaScript
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:

JavaScript
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 实例:

JavaScript
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',
});

实际示例

JavaScript
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 服务器发送命令。