@cogitator-ai/ redis : @ action
具有独立和集群支持的Cogitator的统一Redis客户端.
安装
pnpm add @cogitator-ai/redis ioredis
ioredis 当创建第一个客户端时( 5. x 或 6. x) 被装入; 没有它 createRedisClient 丢入安装提示.
横穿Cogitator:内存包 RedisAdapter 在它和工作流程的基础上 RedisCheckpointStore, (中文(简体) ), RedisRunStore, (中文(简体) ), RedisApprovalStore 财务报告和审定财务报表 RedisTimerStore 带走客户端 createRedisClient() (或平地的ioredis实例) client。见 重写 在网站上.
特征
- 统一接口 - 独立模式和集群模式的API相同
- 模式检测 - 说吧
detectRedisMode()检查服务器是否以集群模式运行 - 环境配置 - 通过环境变量配置
- TLS 支持 - 与TLS的安全连接
- NAT 绘图 - 支持NAT后面的集群节点
- 密钥前缀 - 透明密钥前缀(包括
keys()查看; 集群模式下使用散列标记 - 非屏蔽密钥扫描 - 说吧
keys()和雄风scan()使用 SCAN 并覆盖集群的每个主节点 - 工具缓存客户端 - 满意
RedisClientLike从@cogitator-ai/core,所以它可以回来withCache({ storage: 'redis' }) - 重接战略 - 已安装封顶的复通
- Pub/Sub 软件 - 出版/订阅,每道回调
快速启动
独立模式
import { createRedisClient } from '@cogitator-ai/redis';
const redis = await createRedisClient({
url: 'redis://localhost:6379',
});
await redis.set('key', 'value');
const value = await redis.get('key');
await redis.quit();
集群模式
import { createRedisClient } from '@cogitator-ai/redis';
const redis = await createRedisClient({
mode: 'cluster',
nodes: [
{ host: 'redis-1', port: 6379 },
{ host: 'redis-2', port: 6379 },
{ host: 'redis-3', port: 6379 },
],
keyPrefix: '{cogitator}:',
});
await redis.set('key', 'value');
创建客户端
工厂函数
import { createRedisClient } from '@cogitator-ai/redis';
// Standalone with URL
const client1 = await createRedisClient({
url: 'redis://localhost:6379',
});
// Standalone with host/port
const client2 = await createRedisClient({
host: 'localhost',
port: 6379,
password: 'secret',
db: 1,
});
// Cluster mode
const client3 = await createRedisClient({
mode: 'cluster',
nodes: [
{ host: 'node1', port: 6379 },
{ host: 'node2', port: 6379 },
{ host: 'node3', port: 6379 },
],
});
从环境变量
import { createRedisClient, createConfigFromEnv } from '@cogitator-ai/redis';
const config = createConfigFromEnv();
const redis = await createRedisClient(config);
配置
独立配置
interface RedisStandaloneConfig {
mode?: 'standalone';
// Connection
url?: string; // Redis URL (e.g., redis://user:password@localhost:6379/0)
host?: string; // Host (alternative to url, overrides its host)
port?: number; // Port (alternative to url, overrides its port)
db?: number; // Database number (overrides the url's /db)
// Authentication
password?: string; // Redis password (overrides the url's password)
// Options
keyPrefix?: string; // Prefix for all keys
tls?: boolean; // Enable TLS
maxRetriesPerRequest?: number; // Max retries (default: 3)
lazyConnect?: boolean; // Don't connect immediately (default: false)
}
集群配置
interface RedisClusterConfig {
mode: 'cluster';
nodes: { host: string; port: number }[];
// Authentication
password?: string;
// Options
keyPrefix?: string; // Use {hashtag}: format for cluster
tls?: boolean;
maxRetriesPerRequest?: number;
lazyConnect?: boolean;
// Cluster-specific
scaleReads?: 'master' | 'slave' | 'all'; // Where to read from (default: 'master')
natMap?: Record<string, { host: string; port: number }>; // NAT mapping
}
配置选项
| 选项 | 独立 | 组 | 说明 |
|---|---|---|---|
url | ✓ | - 说吧 | 重做连接 URL |
host | ✓ | - 说吧 | Redis 主机 |
port | ✓ | - 说吧 | Redis 端口 |
db | ✓ | - 说吧 | 数据库编号 |
nodes | - 说吧 | ✓ | 集群节点阵列 |
password | ✓ | ✓ | 认证密码 |
keyPrefix | ✓ | ✓ | 密钥前缀 |
tls | ✓ | ✓ | 启用 TLS |
maxRetriesPerRequest | ✓ | ✓ | 最大重试尝试 |
lazyConnect | ✓ | ✓ | 懒散连接 |
scaleReads | - 说吧 | ✓ | 从复制品中读取 |
natMap | - 说吧 | ✓ | NAT 地址映射 |
重现界面
统一的客户端界面对两种模式都相同.
键值操作
await redis.get('key');
await redis.set('key', 'value');
await redis.setex('key', 3600, 'value');
await redis.del('key1', 'key2');
await redis.expire('key', 3600);
await redis.mget('key1', 'key2', 'key3');
const present = await redis.exists('key1', 'key2'); // how many of them exist
await redis.incr('counter');
await redis.decr('counter');
排序的集
await redis.zadd('leaderboard', 100, 'player1');
await redis.zadd('leaderboard', 200, 'player2');
const top3 = await redis.zrange('leaderboard', 0, 2);
const all = await redis.zrange('leaderboard', 0, '-1'); // stop may be a string, as in ioredis
const highScores = await redis.zrangebyscore('leaderboard', 100, 500);
await redis.zrem('leaderboard', 'player1');
设定
const members = await redis.smembers('myset');
Pub/Sub 软件
用户模式中的连接无法运行正则命令, 因此订阅于 a duplicate()。频道名称是准确的(它们没有前缀) keyPrefix);多个回调可以订阅同一频道 unsubscribe 取出一切诸相.
const subscriber = redis.duplicate();
await subscriber.subscribe('channel', (channel, message) => {
console.log(`Received on ${channel}: ${message}`);
});
await redis.publish('channel', 'hello');
await subscriber.unsubscribe('channel');
活动
redis.on('connect', () => console.log('Connected'));
redis.on('ready', () => console.log('Ready'));
redis.on('error', (err) => console.error('Error:', err));
redis.on('close', () => console.log('Closed'));
redis.on('reconnecting', () => console.log('Reconnecting...'));
redis.on('end', () => console.log('Connection ended'));
方法
await redis.ping();
const info = await redis.info();
const memoryInfo = await redis.info('memory');
// Relative to keyPrefix: with keyPrefix 'myapp:' this finds 'myapp:cache:*'
// and returns ['cache:a', ...], ready to pass to get/del
const cacheKeys = await redis.keys('cache:*');
await redis.del(...cacheKeys);
// One SCAN step at a time, with ioredis' signature; also relative to keyPrefix.
// In cluster mode the cursor walks every master node in turn.
let cursor = '0';
do {
const [next, batch] = await redis.scan(cursor, 'MATCH', 'cache:*', 'COUNT', 100);
for (const key of batch) console.log(key);
cursor = next;
} while (cursor !== '0');
const sub = redis.duplicate();
await redis.quit();
环境变量
| 变量 | 说明 |
|---|---|
REDIS_URL | 重做连接 URL |
REDIS_HOST | Redis 主机何时 REDIS_URL 未设置( 默认: localhost) |
REDIS_PORT | Redis 端口时 REDIS_URL 未设置( 默认: 6379) |
REDIS_PASSWORD | 重写密码 |
REDIS_CLUSTER_NODES | JSON 集群节点阵列; 切换到集群模式( 错位值抛出) |
REDIS_KEY_PREFIX | 密钥前缀( 默认 ): cogitator:,或 {cogitator}: 组合模式) |
createConfigFromEnv(env?) 读取 process.env 除非你们经过另一物体; parseClusterNodesEnv(value) 自行解析集群节点列表.
环境实例
独立 :
REDIS_URL=redis://localhost:6379
REDIS_PASSWORD=secret
REDIS_KEY_PREFIX=myapp:
组 :
REDIS_CLUSTER_NODES='[{"host":"10.0.0.1","port":6379},{"host":"10.0.0.2","port":6379}]'
REDIS_PASSWORD=secret
REDIS_KEY_PREFIX={myapp}:
自动检测
自动检测 Redis 是否以集群模式运行 :
import { createRedisClient, detectRedisMode, type RedisConfig } from '@cogitator-ai/redis';
const mode = await detectRedisMode({
host: 'localhost',
port: 6379,
});
console.log(`Redis mode: ${mode}`);
const config: RedisConfig =
mode === 'cluster'
? { mode: 'cluster', nodes: [{ host: 'localhost', port: 6379 }] }
: { host: 'localhost', port: 6379 };
const redis = await createRedisClient(config);
分组键运行
在集群模式下, 使用散列标记来保证相关的密钥路由到同一个槽 :
const redis = await createRedisClient({
mode: 'cluster',
nodes: [{ host: 'redis-1', port: 6379 }],
keyPrefix: '{myapp}:', // Hash tag prefix
});
// All these keys route to the same slot because of {myapp}
await redis.set('users:123', '...'); // → {myapp}:users:123
await redis.set('sessions:456', '...'); // → {myapp}:sessions:456
Hash 标签规则
- 使用
{hashtag}:前缀的格式 - 内部内容
{}确定槽路由 - 具有相同散列标记的密钥进入相同的节点
- 群组中多键业务所需
TLS 配置
与 TLS 独立
const redis = await createRedisClient({
url: 'rediss://secure.redis.host:6379', // Note: rediss://
tls: true,
});
带有 TLS 的集群
const redis = await createRedisClient({
mode: 'cluster',
nodes: [
{ host: 'secure-1.redis.host', port: 6379 },
{ host: 'secure-2.redis.host', port: 6379 },
],
tls: true,
password: 'secret',
});
NAT 绘图
对于NAT/负载平衡器后面的集群节点:
const redis = await createRedisClient({
mode: 'cluster',
nodes: [{ host: 'external.host', port: 6379 }],
natMap: {
'10.0.0.1:6379': { host: 'external-1.host', port: 6379 },
'10.0.0.2:6379': { host: 'external-2.host', port: 6379 },
'10.0.0.3:6379': { host: 'external-3.host', port: 6379 },
},
});
实例
持久工作流程存储
import { createRedisClient } from '@cogitator-ai/redis';
import { RedisRunStore, RedisCheckpointStore } from '@cogitator-ai/workflows';
const redis = await createRedisClient({ url: process.env.REDIS_URL });
const runStore = new RedisRunStore({ client: redis });
const checkpointStore = new RedisCheckpointStore({ client: redis });
工具缓存
A 类 RedisClient 满意 RedisClientLike 从 @cogitator-ai/core,所以它背对 Redis 工具缓存 没有 ioredis 特定的代码。 在集群模式中使用散列标签 keyPrefix:缓存存储发送多键命令(mget, (中文(简体) ), del) (中文(简体) ).
import { createRedisClient } from '@cogitator-ai/redis';
import { withCache, webSearch } from '@cogitator-ai/core';
const redis = await createRedisClient({ url: process.env.REDIS_URL, keyPrefix: 'myapp:' });
const cachedSearch = withCache(webSearch, {
strategy: 'exact',
ttl: '30m',
maxSize: 1000,
storage: 'redis',
redisClient: redis,
});
连接集合模式
import { createRedisClient, createConfigFromEnv } from '@cogitator-ai/redis';
import type { RedisClient } from '@cogitator-ai/redis';
let client: RedisClient | null = null;
async function getRedis(): Promise<RedisClient> {
if (!client) {
const config = createConfigFromEnv();
client = await createRedisClient(config);
}
return client;
}
async function closeRedis(): Promise<void> {
if (client) {
await client.quit();
client = null;
}
}
缓存模式
import { createRedisClient } from '@cogitator-ai/redis';
const redis = await createRedisClient({
url: 'redis://localhost:6379',
keyPrefix: 'cache:',
});
async function getOrFetch<T>(
key: string,
fetcher: () => Promise<T>,
ttl: number = 3600
): Promise<T> {
const cached = await redis.get(key);
if (cached) {
return JSON.parse(cached);
}
const data = await fetcher();
await redis.setex(key, ttl, JSON.stringify(data));
return data;
}
const user = await getOrFetch('user:123', () => fetchUserFromDb(123), 600);
Pub/Sub图案
import { createRedisClient } from '@cogitator-ai/redis';
const redis = await createRedisClient({ url: 'redis://localhost:6379' });
const publisher = redis;
const subscriber = redis.duplicate();
interface Event {
type: string;
payload: unknown;
}
async function publish(event: Event): Promise<void> {
await publisher.publish('events', JSON.stringify(event));
}
async function subscribe(handler: (event: Event) => void): Promise<void> {
await subscriber.subscribe('events', (channel, message) => {
const event = JSON.parse(message);
handler(event);
});
}
await subscribe((event) => {
console.log('Received event:', event);
});
await publish({ type: 'user.created', payload: { id: 123 } });
健康检查
import { createRedisClient } from '@cogitator-ai/redis';
async function checkRedisHealth(): Promise<boolean> {
const redis = await createRedisClient({
url: 'redis://localhost:6379',
lazyConnect: true,
});
try {
const result = await redis.ping();
return result === 'PONG';
} catch {
return false;
} finally {
await redis.quit();
}
}
优雅的关机
import { createRedisClient, createConfigFromEnv } from '@cogitator-ai/redis';
const redis = await createRedisClient(createConfigFromEnv());
process.on('SIGTERM', async () => {
console.log('Shutting down...');
await redis.quit();
process.exit(0);
});
类型参考
import type {
RedisMode,
RedisNodeConfig,
RedisCommonOptions,
RedisStandaloneConfig,
RedisClusterConfig,
RedisConfig,
RedisClient,
} from '@cogitator-ai/redis';
import {
createRedisClient,
detectRedisMode,
parseClusterNodesEnv,
createConfigFromEnv,
isClusterConfig,
} from '@cogitator-ai/redis';
许可证
麻省理工学院