@ticatec/redis-client
Version:
A lightweight TypeScript wrapper around ioredis with singleton pattern support, mock Redis for testing, and abstract caching framework.
320 lines (244 loc) • 8.29 kB
Markdown
# Ticatec Redis 客户端
[English](./README.md) | 中文
[](https://www.npmjs.com/package/@ticatec/redis-client)
[](https://opensource.org/licenses/MIT)
基于ioredis的轻量级TypeScript封装库,采用单例模式提供便捷的Redis操作方法。支持真实Redis连接和Mock Redis测试。
## 特性
- **单例模式**: 易于使用的单例Redis客户端实例
- **Mock支持**: 内置Mock Redis用于测试环境
- **TypeScript支持**: 完整的类型定义和智能提示
- **JSON序列化**: 对象的自动JSON序列化/反序列化
- **全面操作**: 支持字符串、哈希、集合和列表操作
- **缓存框架**: 抽象缓存数据管理系统
- **日志集成**: 内置log4js日志记录
## 安装
```bash
npm install @ticatec/redis-client ioredis ioredis-mock log4js
```
## 快速开始
### 基础用法
```typescript
import RedisClient from '@ticatec/redis-client';
// 初始化真实Redis连接
await RedisClient.init({
host: '127.0.0.1',
port: 6379,
// ... 其他ioredis配置选项
});
const client = RedisClient.getInstance();
// 字符串操作
await client.set('key', 'value', 3600); // 设置1小时TTL
const value = await client.get('key');
// 对象操作(JSON序列化)
await client.set('user', { name: 'John', age: 30 });
const user = await client.getObject('user');
```
### 使用Mock Redis进行测试
```typescript
// 使用Mock Redis初始化(传入null)
await RedisClient.init(null);
const client = RedisClient.getInstance();
await client.set('testKey', 'testValue');
const value = await client.get('testKey');
console.log(value); // 'testValue'
```
## API 参考
### 核心方法
#### 字符串操作
- `set(key, value, seconds?)` - 设置键值对,可选TTL
- `get(key)` - 根据键获取值
- `getObject(key)` - 获取并解析JSON对象
- `del(key)` - 删除键
- `expiry(key, seconds)` - 设置过期时间
#### 哈希操作
- `hset(key, data, seconds?)` - 设置哈希字段
- `hget(key, field)` - 获取哈希字段值
- `hgetall(key)` - 获取所有哈希字段
- `hsetnx(key, field, value)` - 仅当字段不存在时设置
#### 集合操作
- `sadd(key, members, seconds)` - 向集合添加成员
- `scard(key)` - 获取集合基数
- `isSetMember(key, value)` - 检查集合成员关系
#### 列表操作
- `rpush(key, data, seconds?)` - 向列表尾部推送数据
- `lrange(key, start, end)` - 获取列表范围
- `lrangeObject(key, start, end)` - 获取列表范围并解析JSON
- `llen(key)` - 获取列表长度
- `lpop(key)` - 从列表头部弹出元素
### 缓存框架
缓存框架提供了一种简单而灵活的方式来管理缓存数据,具有自动键生成和TTL处理功能。
#### AbstractCachedData
抽象基类,用于实现缓存数据操作。它提供三个核心方法:`load()`、`save()`和`clean()`:
```typescript
import { AbstractCachedData, GetKey } from '@ticatec/redis-client';
interface User {
id: number;
name: string;
email: string;
createdAt: Date;
}
// 使用部分键定义用户缓存,提供灵活性
class UserCache extends AbstractCachedData<User> {
constructor() {
// 键生成函数接受部分User对象,TTL为1小时
super((key: Partial<User>) => `user:${key.id}`, 3600);
}
// 带缓存的获取用户数据方法
async getUser(id: number): Promise<User | null> {
try {
// 尝试使用部分键从缓存加载
const cached = await this.load({ id });
if (cached) {
return cached;
}
} catch (error) {
console.warn('缓存未命中,用户:', id);
}
// 如果缓存中没有,从数据库获取
const user = await this.fetchUserFromDatabase(id);
if (user) {
// 保存到缓存 - getKey函数会从user对象中提取id
await this.save(user);
}
return user;
}
// 更新用户数据的自定义方法
async updateUser(id: number, userData: Partial<User>): Promise<void> {
// 在数据库中更新
const updatedUser = await this.updateUserInDatabase(id, userData);
// 用新数据更新缓存
if (updatedUser) {
await this.save(updatedUser);
}
}
// 使用户缓存失效的自定义方法
async invalidateUser(id: number): Promise<void> {
await this.clean({ id });
}
private async fetchUserFromDatabase(id: number): Promise<User | null> {
// 您的数据库逻辑
return null;
}
private async updateUserInDatabase(id: number, userData: Partial<User>): Promise<User | null> {
// 您的数据库更新逻辑
return null;
}
}
```
#### CachedDataManager
用于注册和检索缓存实例的单例管理器:
```typescript
import { CachedDataManager } from '@ticatec/redis-client';
// 初始化管理器
const manager = CachedDataManager.getInstance();
// 创建并注册缓存实例
const userCache = new UserCache();
manager.register(UserCache, userCache);
// 在应用程序的任何地方检索和使用缓存实例
const getUserCache = () => manager.get(UserCache);
// 在应用程序中使用
async function handleUserRequest(userId: number): Promise<User | null> {
const cache = getUserCache();
if (cache) {
return await cache.getUser(userId);
}
return null;
}
```
#### 高级使用模式
**1. 具有不同TTL的多种缓存类型:**
```typescript
class SessionCache extends AbstractCachedData<{ sessionId: string, data: any }> {
constructor() {
super(key => `session:${key.sessionId}`, 1800); // 30分钟
}
}
class ConfigCache extends AbstractCachedData<{ key: string, value: any }> {
constructor() {
super(key => `config:${key.key}`, 86400); // 24小时
}
}
```
**2. 复杂场景的复合键:**
```typescript
interface UserPost {
userId: number;
postId: number;
title: string;
content: string;
}
class UserPostsCache extends AbstractCachedData<UserPost[]> {
constructor() {
super(
(key: { userId: number, page: number }) =>
`user:${key.userId}:posts:page:${key.page}`,
3600 // 1小时
);
}
async getUserPostsPage(userId: number, page: number): Promise<UserPost[]> {
const key = { userId, page };
try {
return await this.load(key);
} catch (error) {
// 缓存未命中,从API获取
const posts = await this.fetchPostsFromAPI(userId, page);
await this.save(posts);
return posts;
}
}
private async fetchPostsFromAPI(userId: number, page: number): Promise<UserPost[]> {
// 您的API逻辑
return [];
}
}
```
## 配置
### Redis配置
传入任何有效的ioredis配置对象:
```typescript
await RedisClient.init({
host: 'localhost',
port: 6379,
password: 'your-password',
db: 0,
retryDelayOnFailover: 100,
maxRetriesPerRequest: 3,
lazyConnect: true
});
```
### 日志配置
客户端使用log4js进行日志记录。在应用中配置日志:
```typescript
import log4js from 'log4js';
log4js.configure({
appenders: {
console: { type: 'console' }
},
categories: {
default: { appenders: ['console'], level: 'info' },
RedisClient: { appenders: ['console'], level: 'debug' }
}
});
```
## 错误处理
客户端提供内置的错误处理和日志记录:
- 连接错误会自动记录
- JSON解析错误会优雅处理
- Redis命令错误会传播给调用者
## 最佳实践
1. **单次初始化**: 在应用中只调用一次`RedisClient.init()`
2. **测试使用Mock**: 单元测试中始终使用`init(null)`
3. **谨慎处理JSON**: JSON数据使用`getObject()`,字符串使用`get()`
4. **设置合适的TTL**: 总是考虑为缓存数据设置过期时间
5. **使用缓存框架**: 复杂缓存逻辑请扩展`AbstractCachedData`
## 依赖
- **ioredis** (^5.3.2): Node.js的Redis客户端
- **ioredis-mock** (^8.9.0): 用于测试的Mock Redis实现
- **log4js** (对等依赖): 日志框架
## 许可证
MIT许可证。详见[LICENSE](LICENSE)文件。
## 贡献
欢迎贡献!请向[GitHub仓库](https://github.com/ticatec/redis-client)提交问题和拉取请求。
## 联系方式
- 邮箱:huili.f@gmail.com
- 仓库:https://github.com/ticatec/redis-client