125 行
3.2 KiB
TypeScript
125 行
3.2 KiB
TypeScript
import Redis from 'ioredis';
|
||
import { Config } from './config';
|
||
import { newLogger } from './log';
|
||
import { Logger } from 'winston';
|
||
import { parseJSON } from './index';
|
||
|
||
export let DefaultRedis: Redis;
|
||
|
||
export function initCache() {
|
||
if (!Config.config.global?.redis?.host) {
|
||
setTimeout(() => initCache(), 5000);
|
||
return;
|
||
}
|
||
DefaultRedis = new Redis(Config.config.global.redis);
|
||
DefaultRedis.on('ready', () => {
|
||
console.info(
|
||
`redis[${Config.config.global.redis.host}:${Config.config.global.redis.port}] ready`,
|
||
);
|
||
});
|
||
DefaultRedis.on('error', (e) => {
|
||
console.debug(
|
||
`redis[${Config.config.global.redis.host}:${Config.config.global.redis.port}] failed,${e.message}`,
|
||
);
|
||
});
|
||
}
|
||
|
||
export class StringPool {
|
||
private redis: Redis;
|
||
private readonly key: string;
|
||
private logger!: Logger;
|
||
|
||
constructor(redis: Redis, key: string) {
|
||
this.redis = redis;
|
||
this.key = key;
|
||
this.logger = newLogger(`${this.key}`);
|
||
}
|
||
|
||
async add(value: string): Promise<number> {
|
||
this.logger.debug(`add ${value}`);
|
||
return this.redis.sadd(this.key, value);
|
||
}
|
||
|
||
async remove(value: string): Promise<void> {
|
||
this.logger.debug(`remove ${value}`);
|
||
await this.redis.srem(this.key, value);
|
||
}
|
||
|
||
async random(): Promise<string | null> {
|
||
this.logger.debug(`random`);
|
||
return this.redis.srandmember(this.key);
|
||
}
|
||
|
||
async size(): Promise<number> {
|
||
this.logger.debug(`size`);
|
||
return this.redis.scard(this.key);
|
||
}
|
||
|
||
async clear(): Promise<void> {
|
||
this.logger.debug(`clear`);
|
||
await this.redis.del(this.key);
|
||
}
|
||
|
||
// pop
|
||
async pop(): Promise<string | null> {
|
||
this.logger.debug(`pop`);
|
||
return this.redis.spop(this.key);
|
||
}
|
||
}
|
||
|
||
// string类型的key,传入初始化方法
|
||
// 如果key不存在,会调用init方法初始化
|
||
// key需要有过期时间
|
||
// 防止缓存穿透和缓存雪崩
|
||
export class CommCache<T> {
|
||
private redis: Redis;
|
||
private readonly _key: string;
|
||
private readonly init?: () => Promise<T | null>;
|
||
private readonly expire: number;
|
||
private logger!: Logger;
|
||
|
||
constructor(
|
||
redis: Redis,
|
||
key: string,
|
||
expire: number,
|
||
init?: () => Promise<T | null>,
|
||
) {
|
||
this.redis = redis;
|
||
this._key = key;
|
||
this.init = init;
|
||
this.expire = expire;
|
||
this.logger = newLogger(`${this._key}`);
|
||
}
|
||
|
||
key(subkey: string) {
|
||
return this._key + ':' + subkey;
|
||
}
|
||
|
||
async get(subkey: string, init?: () => Promise<T | null>): Promise<T | null> {
|
||
if (!init && !this.init) {
|
||
throw new Error('init is null');
|
||
}
|
||
const initFunc = init || this.init;
|
||
const v = await this.redis.get(this.key(subkey));
|
||
if (v) {
|
||
this.logger.debug(`${subkey} cache got`);
|
||
return parseJSON<T | null>(v, null);
|
||
}
|
||
const nv = await initFunc!();
|
||
const sv = JSON.stringify(nv);
|
||
await this.redis.set(this.key(subkey), sv, 'EX', this.expire);
|
||
this.logger.debug(`${subkey} cache miss`);
|
||
return nv;
|
||
}
|
||
|
||
async set(subkey: string, value: string): Promise<void> {
|
||
this.logger.debug(`set ${value}`);
|
||
await this.redis.set(this.key(subkey), value, 'EX', this.expire);
|
||
}
|
||
|
||
async clear(subkey: string): Promise<void> {
|
||
this.logger.debug(`clear`);
|
||
await this.redis.del(this.key(subkey));
|
||
}
|
||
}
|