mm_redis 2.0.8 → 2.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +102 -38
- package/README_EN.md +433 -0
- package/index.js +63 -115
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,22 +1,28 @@
|
|
|
1
1
|
# mm_redis
|
|
2
2
|
|
|
3
|
-
mm_redis
|
|
4
|
-
|
|
5
|
-
[](https://badge.fury.io/js/mm_redis)
|
|
3
|
+
[](https://www.npmjs.com/package/mm_redis)
|
|
6
4
|
[](https://www.npmjs.com/package/mm_redis)
|
|
5
|
+
[](https://opensource.org/licenses/ISC)
|
|
6
|
+
|
|
7
|
+
> 一个高效的Redis客户端封装库,提供完整的连接池管理、数据类型操作和批量操作优化
|
|
7
8
|
|
|
8
9
|
## 版本信息
|
|
9
10
|
|
|
11
|
+
- **最新版本**: 2.0.0
|
|
10
12
|
- **更新日期**: 2025-10-20
|
|
11
13
|
- **作者**: qww
|
|
12
14
|
- **NPM**: [https://www.npmjs.com/package/mm_redis](https://www.npmjs.com/package/mm_redis)
|
|
13
15
|
- **GitHub**: [https://gitee.com/qiuwenwu91/mm_redis](https://gitee.com/qiuwenwu91/mm_redis)
|
|
16
|
+
- **Node.js**: >= 14.0.0
|
|
14
17
|
|
|
15
18
|
## 功能特性
|
|
16
19
|
|
|
20
|
+
### 🚀 核心功能
|
|
17
21
|
- **完整的连接池管理**:优化并发性能,支持连接复用和自动回收
|
|
18
22
|
- **Pipeline批量操作**:支持Redis Pipeline机制,大幅提升批量操作效率
|
|
19
23
|
- **全面的数据类型支持**:覆盖String、List、Hash、Set、Sorted Set等所有主要Redis数据类型
|
|
24
|
+
|
|
25
|
+
### 🔧 高级特性
|
|
20
26
|
- **智能连接管理**:自动重连机制,使用指数退避策略,确保连接稳定可靠
|
|
21
27
|
- **健壮的错误处理**:全面的错误处理机制和详细的日志记录,便于问题排查
|
|
22
28
|
- **便捷的数据序列化**:自动JSON序列化/反序列化,无需手动转换数据格式
|
|
@@ -26,55 +32,113 @@ mm_redis是一个高效的Redis客户端封装库,提供了全面的Redis功
|
|
|
26
32
|
|
|
27
33
|
## 安装
|
|
28
34
|
|
|
35
|
+
### npm
|
|
36
|
+
```bash
|
|
37
|
+
npm install mm_redis
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### yarn
|
|
41
|
+
```bash
|
|
42
|
+
yarn add mm_redis
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### pnpm
|
|
29
46
|
```bash
|
|
30
|
-
|
|
47
|
+
pnpm add mm_redis
|
|
31
48
|
```
|
|
32
49
|
|
|
33
|
-
##
|
|
50
|
+
## 快速开始
|
|
51
|
+
|
|
52
|
+
```javascript
|
|
53
|
+
const { Redis } = require('mm_redis');
|
|
54
|
+
|
|
55
|
+
// 创建Redis实例
|
|
56
|
+
const redis = new Redis({
|
|
57
|
+
host: 'localhost',
|
|
58
|
+
port: 6379,
|
|
59
|
+
password: 'your_password',
|
|
60
|
+
db: 0
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// 连接Redis
|
|
64
|
+
await redis.connect();
|
|
65
|
+
|
|
66
|
+
// 设置缓存
|
|
67
|
+
await redis.set('user:1', { name: '张三', age: 25 });
|
|
68
|
+
|
|
69
|
+
// 获取缓存
|
|
70
|
+
const user = await redis.get('user:1');
|
|
71
|
+
console.log(user); // { name: '张三', age: 25 }
|
|
34
72
|
|
|
35
|
-
|
|
36
|
-
|
|
73
|
+
// 错误处理
|
|
74
|
+
try {
|
|
75
|
+
await redis.set('key', 'value');
|
|
76
|
+
} catch (err) {
|
|
77
|
+
console.error('Redis操作失败:', err);
|
|
78
|
+
}
|
|
79
|
+
```
|
|
37
80
|
|
|
38
81
|
## 配置引入方式
|
|
39
82
|
|
|
40
|
-
mm_redis
|
|
83
|
+
mm_redis支持多种配置引入方式,满足不同场景的需求:
|
|
41
84
|
|
|
42
|
-
1.
|
|
85
|
+
### 1. 对象配置(推荐)
|
|
43
86
|
```javascript
|
|
44
|
-
const Redis = require('mm_redis');
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
host:
|
|
49
|
-
// 端口号
|
|
87
|
+
const { Redis } = require('mm_redis');
|
|
88
|
+
|
|
89
|
+
// 直接传入配置对象
|
|
90
|
+
const redis = new Redis({
|
|
91
|
+
host: 'localhost',
|
|
50
92
|
port: 6379,
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
prefix: "app:",
|
|
57
|
-
// 连接超时时间(ms)
|
|
58
|
-
connect_timeout: 10000,
|
|
59
|
-
// 重试次数
|
|
60
|
-
retry_count: 3,
|
|
61
|
-
// 重试间隔(ms)
|
|
62
|
-
retry_delay: 1000,
|
|
63
|
-
// 连接池配置
|
|
64
|
-
pool: {
|
|
65
|
-
max_clients: 10, // 最大连接数
|
|
66
|
-
min_idle: 2, // 最小空闲连接数
|
|
67
|
-
max_idle: 5, // 最大空闲连接数
|
|
68
|
-
idle_timeout: 30000 // 空闲超时时间(ms)
|
|
69
|
-
}
|
|
93
|
+
password: 'your_password',
|
|
94
|
+
db: 0,
|
|
95
|
+
key_prefix: 'app:',
|
|
96
|
+
max_clients: 10,
|
|
97
|
+
connect_timeout: 5000
|
|
70
98
|
});
|
|
71
99
|
```
|
|
72
100
|
|
|
73
|
-
2.
|
|
101
|
+
### 2. 文件配置
|
|
102
|
+
```javascript
|
|
103
|
+
// config/redis.json
|
|
104
|
+
{
|
|
105
|
+
"host": "localhost",
|
|
106
|
+
"port": 6379,
|
|
107
|
+
"password": "your_password",
|
|
108
|
+
"db": 0,
|
|
109
|
+
"key_prefix": "app:",
|
|
110
|
+
"max_clients": 10,
|
|
111
|
+
"connect_timeout": 5000
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// 使用配置
|
|
115
|
+
const { Redis } = require('mm_redis');
|
|
116
|
+
const config = require('./config/redis.json');
|
|
117
|
+
const redis = new Redis(config);
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 3. 环境变量配置
|
|
74
121
|
```javascript
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
122
|
+
// 环境变量
|
|
123
|
+
REDIS_HOST=localhost
|
|
124
|
+
REDIS_PORT=6379
|
|
125
|
+
REDIS_PASSWORD=your_password
|
|
126
|
+
REDIS_DB=0
|
|
127
|
+
REDIS_KEY_PREFIX=app:
|
|
128
|
+
REDIS_MAX_CLIENTS=10
|
|
129
|
+
REDIS_CONNECT_TIMEOUT=5000
|
|
130
|
+
|
|
131
|
+
// 使用环境变量
|
|
132
|
+
const { Redis } = require('mm_redis');
|
|
133
|
+
const redis = new Redis({
|
|
134
|
+
host: process.env.REDIS_HOST,
|
|
135
|
+
port: parseInt(process.env.REDIS_PORT),
|
|
136
|
+
password: process.env.REDIS_PASSWORD,
|
|
137
|
+
db: parseInt(process.env.REDIS_DB),
|
|
138
|
+
key_prefix: process.env.REDIS_KEY_PREFIX,
|
|
139
|
+
max_clients: parseInt(process.env.REDIS_MAX_CLIENTS),
|
|
140
|
+
connect_timeout: parseInt(process.env.REDIS_CONNECT_TIMEOUT)
|
|
141
|
+
});
|
|
78
142
|
```
|
|
79
143
|
## 变更日志
|
|
80
144
|
|
package/README_EN.md
ADDED
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
# mm_redis
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/mm_redis)
|
|
4
|
+
[](https://www.npmjs.com/package/mm_redis)
|
|
5
|
+
[](https://opensource.org/licenses/ISC)
|
|
6
|
+
|
|
7
|
+
A high-performance Redis client wrapper library that provides comprehensive Redis functionality support, including connection pool management, data type operations, error handling, and batch operation optimization.
|
|
8
|
+
|
|
9
|
+
## Version Information
|
|
10
|
+
|
|
11
|
+
- **Latest Version**: 2.0.0
|
|
12
|
+
- **Update Date**: 2025-10-20
|
|
13
|
+
- **Author**: qww
|
|
14
|
+
- **NPM**: [https://www.npmjs.com/package/mm_redis](https://www.npmjs.com/package/mm_redis)
|
|
15
|
+
- **GitHub**: [https://gitee.com/qiuwenwu91/mm_redis](https://gitee.com/qiuwenwu91/mm_redis)
|
|
16
|
+
- **Node.js**: >= 14.0.0
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
### 🚀 Core Features
|
|
21
|
+
- **Complete Connection Pool Management**: Optimized concurrent performance with connection reuse and automatic recycling
|
|
22
|
+
- **Pipeline Batch Operations**: Support for Redis Pipeline mechanism, significantly improving batch operation efficiency
|
|
23
|
+
- **Comprehensive Data Type Support**: Covers all major Redis data types including String, List, Hash, Set, Sorted Set
|
|
24
|
+
|
|
25
|
+
### 🔧 Advanced Features
|
|
26
|
+
- **Smart Connection Management**: Automatic reconnection with exponential backoff strategy for reliable connections
|
|
27
|
+
- **Robust Error Handling**: Comprehensive error handling mechanism with detailed logging
|
|
28
|
+
- **Automatic Data Serialization**: Automatic JSON serialization/deserialization without manual conversion
|
|
29
|
+
- **Key Space Isolation**: Support for key prefix management for multi-business scenario data isolation
|
|
30
|
+
- **Flexible Event System**: Unified event mechanism supporting pre/post operation event listening and interception
|
|
31
|
+
- **Rich Numeric Operations**: Support for atomic increment/decrement operations, suitable for counter scenarios
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
### Using npm
|
|
36
|
+
```bash
|
|
37
|
+
npm install mm_redis --save
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Using yarn
|
|
41
|
+
```bash
|
|
42
|
+
yarn add mm_redis
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Using pnpm
|
|
46
|
+
```bash
|
|
47
|
+
pnpm add mm_redis
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Node.js Version Requirements
|
|
51
|
+
|
|
52
|
+
- **Minimum Supported Version**: Node.js 14.0.0
|
|
53
|
+
- **Recommended Version**: Node.js 16.0.0 and above
|
|
54
|
+
|
|
55
|
+
## Quick Start
|
|
56
|
+
|
|
57
|
+
### Basic Usage
|
|
58
|
+
```javascript
|
|
59
|
+
const Redis = require('mm_redis');
|
|
60
|
+
|
|
61
|
+
// Create Redis instance
|
|
62
|
+
const redis = new Redis('default');
|
|
63
|
+
|
|
64
|
+
async function example() {
|
|
65
|
+
try {
|
|
66
|
+
// Connect to Redis
|
|
67
|
+
await redis.open();
|
|
68
|
+
|
|
69
|
+
// Set cache with expiration
|
|
70
|
+
await redis.set('user:1', { name: 'John', age: 25 }, 3600);
|
|
71
|
+
|
|
72
|
+
// Get cache
|
|
73
|
+
const user = await redis.get('user:1');
|
|
74
|
+
console.log('User info:', user);
|
|
75
|
+
|
|
76
|
+
// Close connection
|
|
77
|
+
await redis.close();
|
|
78
|
+
} catch (error) {
|
|
79
|
+
console.error('Operation failed:', error);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
example();
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Configuration
|
|
87
|
+
|
|
88
|
+
mm_redis supports multiple configuration loading methods:
|
|
89
|
+
|
|
90
|
+
### Object Configuration (Recommended)
|
|
91
|
+
```javascript
|
|
92
|
+
const Redis = require('mm_redis');
|
|
93
|
+
const redis = new Redis('default');
|
|
94
|
+
|
|
95
|
+
redis.setConfig({
|
|
96
|
+
host: "localhost",
|
|
97
|
+
port: 6379,
|
|
98
|
+
password: "",
|
|
99
|
+
database: 0,
|
|
100
|
+
prefix: "app:",
|
|
101
|
+
connect_timeout: 10000,
|
|
102
|
+
retry_count: 3,
|
|
103
|
+
retry_delay: 1000,
|
|
104
|
+
pool: {
|
|
105
|
+
max_clients: 10,
|
|
106
|
+
min_idle: 2,
|
|
107
|
+
max_idle: 5,
|
|
108
|
+
idle_timeout: 30000
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### File Configuration
|
|
114
|
+
```javascript
|
|
115
|
+
const Redis = require('mm_redis');
|
|
116
|
+
const redis = new Redis('default');
|
|
117
|
+
|
|
118
|
+
redis.setConfig('./config.json');
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Configuration file example (`config.json`):
|
|
122
|
+
```json
|
|
123
|
+
{
|
|
124
|
+
"host": "localhost",
|
|
125
|
+
"port": 6379,
|
|
126
|
+
"password": "",
|
|
127
|
+
"database": 0,
|
|
128
|
+
"prefix": "app:",
|
|
129
|
+
"connect_timeout": 10000,
|
|
130
|
+
"retry_count": 3,
|
|
131
|
+
"retry_delay": 1000,
|
|
132
|
+
"pool": {
|
|
133
|
+
"max_clients": 10,
|
|
134
|
+
"min_idle": 2,
|
|
135
|
+
"max_idle": 5,
|
|
136
|
+
"idle_timeout": 30000
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Environment Variables
|
|
142
|
+
```javascript
|
|
143
|
+
const Redis = require('mm_redis');
|
|
144
|
+
const redis = new Redis('default');
|
|
145
|
+
|
|
146
|
+
redis.setConfig({
|
|
147
|
+
host: process.env.REDIS_HOST || 'localhost',
|
|
148
|
+
port: parseInt(process.env.REDIS_PORT) || 6379,
|
|
149
|
+
password: process.env.REDIS_PASSWORD || '',
|
|
150
|
+
database: parseInt(process.env.REDIS_DB) || 0
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Change Log
|
|
155
|
+
|
|
156
|
+
### v2.0.0 (2025-10-14)
|
|
157
|
+
- Removed list_push method, unified to use addForList method for list operations
|
|
158
|
+
- Updated documentation, replaced all list_push references with addForList
|
|
159
|
+
- Updated API list and usage examples in documentation
|
|
160
|
+
- Optimized connection pool configuration management, unified configuration to this.config.pool
|
|
161
|
+
- Added connection pool configuration parameters: min_idle, max_idle, idle_timeout
|
|
162
|
+
|
|
163
|
+
### v1.9.8 (2024-12-22)
|
|
164
|
+
- Updated documentation, improved content required for npm publishing
|
|
165
|
+
- Added GitHub repository link to documentation
|
|
166
|
+
- Optimized feature description, enhanced document readability
|
|
167
|
+
- Added Node.js version requirement description
|
|
168
|
+
|
|
169
|
+
### v1.9.5 (2024-12-21)
|
|
170
|
+
- Fixed compatibility issues with hmset and hgetall methods to ensure compatibility with Redis client v5
|
|
171
|
+
- Removed deprecated hexists, hkeys and hvals methods, optimized API design
|
|
172
|
+
- Optimized implementation of mget and mset methods to ensure full compatibility with Redis v5 API
|
|
173
|
+
- Updated test scripts, removed calls to deprecated methods
|
|
174
|
+
- Updated compatibility table to ensure documentation matches actual API
|
|
175
|
+
- Fixed errors in native Redis API calls, improved code stability
|
|
176
|
+
- Optimized connection pool management, fixed potential memory leak issues
|
|
177
|
+
|
|
178
|
+
### v1.9.4 (2024-12-20)
|
|
179
|
+
- Optimized configuration loading methods, supporting both object configuration and file path configuration
|
|
180
|
+
- Improved implementation of setConfig method, using Object.assign instead of original merge method
|
|
181
|
+
- Fixed warning messages related to connection pool
|
|
182
|
+
- Enhanced error handling mechanism
|
|
183
|
+
|
|
184
|
+
### v1.9.0 (2024-11-20)
|
|
185
|
+
- Initial version release
|
|
186
|
+
- Support for complete connection pool management
|
|
187
|
+
- Implementation of Pipeline batch operations
|
|
188
|
+
- Support for all major Redis data type operations
|
|
189
|
+
|
|
190
|
+
## Usage Examples
|
|
191
|
+
|
|
192
|
+
### Basic Operations
|
|
193
|
+
```javascript
|
|
194
|
+
// Connect to Redis
|
|
195
|
+
await redis.open();
|
|
196
|
+
|
|
197
|
+
// Set cache
|
|
198
|
+
await redis.set('user:1', { name: 'John', age: 25 }, 3600);
|
|
199
|
+
|
|
200
|
+
// Get cache
|
|
201
|
+
const user = await redis.get('user:1');
|
|
202
|
+
console.log('User info:', user);
|
|
203
|
+
|
|
204
|
+
// Check if key exists
|
|
205
|
+
const exists = await redis.has('user:1');
|
|
206
|
+
|
|
207
|
+
// Delete cache
|
|
208
|
+
await redis.del('user:1');
|
|
209
|
+
|
|
210
|
+
// Close connection
|
|
211
|
+
await redis.close();
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### List Operations
|
|
215
|
+
```javascript
|
|
216
|
+
// Set list
|
|
217
|
+
await redis.setForList('users', ['user1', 'user2', 'user3']);
|
|
218
|
+
|
|
219
|
+
// Add element to list
|
|
220
|
+
await redis.addForList('users', 'user4');
|
|
221
|
+
|
|
222
|
+
// Get list
|
|
223
|
+
const users = await redis.getForList('users');
|
|
224
|
+
console.log('User list:', users);
|
|
225
|
+
|
|
226
|
+
// Get partial list elements
|
|
227
|
+
const partialUsers = await redis.getForList('users', 0, 2);
|
|
228
|
+
|
|
229
|
+
// Clear list
|
|
230
|
+
await redis.clearForList('users');
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Hash Operations
|
|
234
|
+
```javascript
|
|
235
|
+
// Set hash field
|
|
236
|
+
await redis.hset('user:profile:1', 'name', 'John');
|
|
237
|
+
await redis.hset('user:profile:1', 'age', 25);
|
|
238
|
+
|
|
239
|
+
// Batch set hash fields
|
|
240
|
+
await redis.hmset('user:profile:1', {
|
|
241
|
+
'email': 'john@example.com',
|
|
242
|
+
'phone': '1234567890'
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// Get hash field
|
|
246
|
+
const name = await redis.hget('user:profile:1', 'name');
|
|
247
|
+
|
|
248
|
+
// Get all hash fields and values
|
|
249
|
+
const profile = await redis.hgetall('user:profile:1');
|
|
250
|
+
console.log('User profile:', profile);
|
|
251
|
+
|
|
252
|
+
// Get hash field count
|
|
253
|
+
const fieldCount = await redis.hlen('user:profile:1');
|
|
254
|
+
|
|
255
|
+
// Delete hash fields
|
|
256
|
+
await redis.hdel('user:profile:1', ['email', 'phone']);
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Batch Operations
|
|
260
|
+
```javascript
|
|
261
|
+
// Batch get
|
|
262
|
+
const data = await redis.mget(['user:1', 'user:2', 'user:3']);
|
|
263
|
+
console.log('Batch get result:', data);
|
|
264
|
+
|
|
265
|
+
// Batch set
|
|
266
|
+
await redis.mset({
|
|
267
|
+
'user:4': { name: 'Jane', age: 30 },
|
|
268
|
+
'user:5': { name: 'Bob', age: 35 }
|
|
269
|
+
}, 3600);
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Pipeline Optimization
|
|
273
|
+
```javascript
|
|
274
|
+
// Create Pipeline
|
|
275
|
+
const pipeline = redis.pipeline();
|
|
276
|
+
|
|
277
|
+
// Add multiple commands
|
|
278
|
+
pipeline.set('counter:1', 100);
|
|
279
|
+
pipeline.ttl('counter:1', 3600);
|
|
280
|
+
pipeline.hset('stats:today', 'visits', 500);
|
|
281
|
+
pipeline.hset('stats:today', 'users', 100);
|
|
282
|
+
|
|
283
|
+
// Execute all commands
|
|
284
|
+
const results = await pipeline.exec();
|
|
285
|
+
console.log('Pipeline execution result:', results);
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Publish/Subscribe
|
|
289
|
+
```javascript
|
|
290
|
+
// Subscribe to channel
|
|
291
|
+
await redis.subscribe('notifications', async (message) => {
|
|
292
|
+
console.log('Received notification:', message);
|
|
293
|
+
// Process received message
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// Publish message
|
|
297
|
+
const receivedCount = await redis.publish('notifications', JSON.stringify({
|
|
298
|
+
type: 'info',
|
|
299
|
+
content: 'System update successful'
|
|
300
|
+
}));
|
|
301
|
+
|
|
302
|
+
console.log(`Message published, ${receivedCount} clients received`);
|
|
303
|
+
|
|
304
|
+
// Unsubscribe
|
|
305
|
+
await redis.unsubscribe('notifications');
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## API Documentation
|
|
309
|
+
|
|
310
|
+
### Connection Management
|
|
311
|
+
- `setConfig(config)`: Set Redis configuration
|
|
312
|
+
- `open()`: Connect to Redis
|
|
313
|
+
- `close()`: Close connection
|
|
314
|
+
- `isConnected()`: Check connection status
|
|
315
|
+
|
|
316
|
+
### String Operations
|
|
317
|
+
- `set(key, value, seconds)`: Set cache
|
|
318
|
+
- `get(key)`: Get cache
|
|
319
|
+
- `has(key)`: Check if key exists
|
|
320
|
+
- `del(key)`: Delete cache
|
|
321
|
+
- `add(key, value, seconds)`: Set cache only if key doesn't exist
|
|
322
|
+
- `ttl(key, seconds)`: Set or get expiration time
|
|
323
|
+
- `addInt(key, num)`: Increment integer value
|
|
324
|
+
- `addFloat(key, num)`: Increment float value
|
|
325
|
+
- `addStr(key, str)`: Append string
|
|
326
|
+
- `getrange(key, start, end)`: Get substring
|
|
327
|
+
- `setrange(key, index, value)`: Set substring
|
|
328
|
+
|
|
329
|
+
### List Operations
|
|
330
|
+
- `setForList(key, value, seconds)`: Set list
|
|
331
|
+
- `addForList(key, value)`: Add element to list
|
|
332
|
+
- `getForList(key, start, end)`: Get list
|
|
333
|
+
- `hasForList(key, value)`: Check if element exists in list
|
|
334
|
+
- `clearForList(key, value)`: Clear list or reset list
|
|
335
|
+
|
|
336
|
+
### Hash Operations
|
|
337
|
+
- `hset(key, field, value)`: Set hash field
|
|
338
|
+
- `hmset(key, obj)`: Batch set hash fields
|
|
339
|
+
- `hget(key, field)`: Get hash field value
|
|
340
|
+
- `hgetall(key)`: Get all hash fields and values
|
|
341
|
+
- `hdel(key, fields)`: Delete hash fields
|
|
342
|
+
- `hlen(key)`: Get hash field count
|
|
343
|
+
|
|
344
|
+
### Batch Operations
|
|
345
|
+
- `mget(keys)`: Batch get cache
|
|
346
|
+
- `mset(obj, seconds)`: Batch set cache
|
|
347
|
+
- `pipeline()`: Create Pipeline object for batch operation optimization
|
|
348
|
+
|
|
349
|
+
### Set Operations
|
|
350
|
+
- `sadd(key, members)`: Add members to set
|
|
351
|
+
- `srem(key, members)`: Remove members from set
|
|
352
|
+
- `sismember(key, member)`: Check if member is in set
|
|
353
|
+
- `smembers(key)`: Get all members in set
|
|
354
|
+
- `scard(key)`: Get member count in set
|
|
355
|
+
- `sinter(keys)`: Get intersection of multiple sets
|
|
356
|
+
- `sunion(keys)`: Get union of multiple sets
|
|
357
|
+
- `sdiff(keys)`: Get difference of multiple sets
|
|
358
|
+
|
|
359
|
+
### Sorted Set Operations
|
|
360
|
+
- `zadd(key, score, member)`: Add member to sorted set
|
|
361
|
+
- `zrem(key, members)`: Remove members from sorted set
|
|
362
|
+
- `zscore(key, member)`: Get member score in sorted set
|
|
363
|
+
- `zrank(key, member)`: Get member rank in sorted set
|
|
364
|
+
- `zrange(key, start, stop, withScores)`: Get members in specified range
|
|
365
|
+
- `zcard(key)`: Get member count in sorted set
|
|
366
|
+
- `zincrby(key, increment, member)`: Increment member score in sorted set
|
|
367
|
+
|
|
368
|
+
### Numeric Operations
|
|
369
|
+
- `incr(key)`: Increment key value by 1
|
|
370
|
+
- `decr(key)`: Decrement key value by 1
|
|
371
|
+
- `incrby(key, increment)`: Increment key value by specified integer
|
|
372
|
+
- `decrby(key, decrement)`: Decrement key value by specified integer
|
|
373
|
+
- `expire(key, seconds)`: Set key expiration time
|
|
374
|
+
|
|
375
|
+
## Connection Pool Management
|
|
376
|
+
|
|
377
|
+
This module has built-in efficient connection pool management that automatically handles connection creation, recycling, and reuse. Connection pool configuration can be set during initialization:
|
|
378
|
+
|
|
379
|
+
```javascript
|
|
380
|
+
{
|
|
381
|
+
"pool": {
|
|
382
|
+
"max_clients": 10, // Maximum connections
|
|
383
|
+
"min_idle": 2, // Minimum idle connections
|
|
384
|
+
"max_idle": 5, // Maximum idle connections
|
|
385
|
+
"idle_timeout": 30000 // Idle timeout (ms)
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
## Error Handling
|
|
391
|
+
|
|
392
|
+
All methods include comprehensive error handling mechanisms and provide detailed log output. When errors occur, error information is automatically recorded and fallback solutions are provided when possible.
|
|
393
|
+
|
|
394
|
+
## Best Practices
|
|
395
|
+
|
|
396
|
+
1. **Use Connection Pool**: For high-concurrency applications, connection pool configuration is crucial. Reasonably set maximum connections and idle connections.
|
|
397
|
+
2. **Use Pipeline**: For batch operations, using Pipeline can significantly improve performance.
|
|
398
|
+
3. **Set Expiration Time Reasonably**: Avoid unlimited growth of data in Redis.
|
|
399
|
+
4. **Use Key Prefix**: Isolate business through key prefix to avoid key conflicts.
|
|
400
|
+
5. **Close Connections Timely**: Call close() method to close all connections when application ends.
|
|
401
|
+
6. **Leverage Event System**: Use event system for logging, data validation, and operation interception.
|
|
402
|
+
|
|
403
|
+
## Contributing
|
|
404
|
+
|
|
405
|
+
We welcome community contributions! If you're interested in contributing to the mm_redis project, please follow these steps:
|
|
406
|
+
|
|
407
|
+
1. Fork this repository
|
|
408
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
409
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
410
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
411
|
+
5. Open a Pull Request
|
|
412
|
+
|
|
413
|
+
Please ensure your code follows the project's coding standards and passes all tests.
|
|
414
|
+
|
|
415
|
+
## License
|
|
416
|
+
|
|
417
|
+
This project is licensed under the ISC License - see the [LICENSE](LICENSE) file for details.
|
|
418
|
+
|
|
419
|
+
## Issue Reporting
|
|
420
|
+
|
|
421
|
+
If you encounter any problems or have any suggestions during use, please provide feedback through the following methods:
|
|
422
|
+
|
|
423
|
+
- **GitHub Issues**: [https://gitee.com/qiuwenwu91/mm_redis/issues](https://gitee.com/qiuwenwu91/mm_redis/issues)
|
|
424
|
+
- **NPM**: [https://www.npmjs.com/package/mm_redis](https://www.npmjs.com/package/mm_redis)
|
|
425
|
+
|
|
426
|
+
## Acknowledgments
|
|
427
|
+
|
|
428
|
+
Thanks to all developers and users who have contributed to this project!
|
|
429
|
+
|
|
430
|
+
## Related Links
|
|
431
|
+
|
|
432
|
+
- **Redis Official Documentation**: [https://redis.io/documentation](https://redis.io/documentation)
|
|
433
|
+
- **Node Redis Client**: [https://github.com/redis/node-redis](https://github.com/redis/node-redis)
|
package/index.js
CHANGED
|
@@ -34,12 +34,10 @@ class Redis extends Base {
|
|
|
34
34
|
// 重试间隔(ms)
|
|
35
35
|
"retry_delay": 1000,
|
|
36
36
|
// 连接池配置
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"idle_timeout": 30000 // 空闲超时时间(ms)
|
|
42
|
-
}
|
|
37
|
+
"max_clients": 100, // 最大连接数
|
|
38
|
+
"min_idle": 5, // 最小空闲连接数
|
|
39
|
+
"max_idle": 20, // 最大空闲连接数
|
|
40
|
+
"idle_timeout": 30000 // 空闲超时时间(ms)
|
|
43
41
|
};
|
|
44
42
|
this.setConfig(config);
|
|
45
43
|
// 初始化连接池
|
|
@@ -87,15 +85,15 @@ Redis.prototype._delPrefix = function (key) {
|
|
|
87
85
|
* @return {Promise<Boolean>} 订阅结果
|
|
88
86
|
*/
|
|
89
87
|
Redis.prototype.subscribe = async function (channel, func) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
88
|
+
// 参数验证
|
|
89
|
+
if (!channel || typeof channel !== 'string') {
|
|
90
|
+
throw new TypeError('channel必须是非空字符串');
|
|
91
|
+
}
|
|
92
|
+
if (typeof func !== 'function') {
|
|
93
|
+
throw new TypeError('func必须是函数');
|
|
94
|
+
}
|
|
98
95
|
|
|
96
|
+
try {
|
|
99
97
|
await this.conn.subscribe(this._addPrefix(channel));
|
|
100
98
|
this.func_list[channel] = func;
|
|
101
99
|
return true;
|
|
@@ -111,12 +109,12 @@ Redis.prototype.subscribe = async function (channel, func) {
|
|
|
111
109
|
* @return {Promise<Boolean>} 取消订阅结果
|
|
112
110
|
*/
|
|
113
111
|
Redis.prototype.unsubscribe = async function (channel) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
112
|
+
// 参数验证
|
|
113
|
+
if (!channel || typeof channel !== 'string') {
|
|
114
|
+
throw new TypeError('channel必须是非空字符串');
|
|
115
|
+
}
|
|
119
116
|
|
|
117
|
+
try {
|
|
120
118
|
await this.conn.unsubscribe(this._addPrefix(channel));
|
|
121
119
|
delete this.func_list[channel];
|
|
122
120
|
return true;
|
|
@@ -133,15 +131,15 @@ Redis.prototype.unsubscribe = async function (channel) {
|
|
|
133
131
|
* @return {Promise<Number>} 接收消息的客户端数量
|
|
134
132
|
*/
|
|
135
133
|
Redis.prototype.publish = async function (channel, message) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
134
|
+
// 参数验证
|
|
135
|
+
if (!channel || typeof channel !== 'string') {
|
|
136
|
+
throw new TypeError('channel必须是非空字符串');
|
|
137
|
+
}
|
|
138
|
+
if (message === undefined || message === null) {
|
|
139
|
+
throw new TypeError('message不能为undefined或null');
|
|
140
|
+
}
|
|
144
141
|
|
|
142
|
+
try {
|
|
145
143
|
return await this.conn.publish(this._addPrefix(channel), message);
|
|
146
144
|
} catch (err) {
|
|
147
145
|
this.logger('error', `发布消息到频道 ${channel} 失败:`, err);
|
|
@@ -189,49 +187,11 @@ Redis.prototype.setConfig = function (config) {
|
|
|
189
187
|
obj = config.loadJson(__dirname);
|
|
190
188
|
} else if (config) {
|
|
191
189
|
// 合并配置参数
|
|
192
|
-
|
|
193
|
-
// 确保连接池配置的合并不会覆盖所有默认值
|
|
194
|
-
if (!this.config.pool) {
|
|
195
|
-
this.config.pool = {};
|
|
196
|
-
}
|
|
197
|
-
Object.assign(this.config.pool, config.pool);
|
|
198
|
-
// 删除config中的pool属性,避免后续Object.assign重复处理
|
|
199
|
-
const configCopy = {
|
|
200
|
-
...config
|
|
201
|
-
};
|
|
202
|
-
delete configCopy.pool;
|
|
203
|
-
Object.assign(this.config, configCopy);
|
|
204
|
-
} else {
|
|
205
|
-
Object.assign(this.config, config);
|
|
206
|
-
}
|
|
190
|
+
Object.assign(this.config, config);
|
|
207
191
|
} else {
|
|
208
192
|
return;
|
|
209
193
|
}
|
|
210
194
|
|
|
211
|
-
// 确保连接池配置存在且包含所有必要字段
|
|
212
|
-
if (!this.config.pool) {
|
|
213
|
-
this.config.pool = {};
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// 设置默认连接池配置,只覆盖未定义的字段
|
|
217
|
-
const defaultPoolConfig = {
|
|
218
|
-
max_clients: 100,
|
|
219
|
-
min_idle: 5,
|
|
220
|
-
max_idle: 20,
|
|
221
|
-
idle_timeout: 30000
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
for (const key in defaultPoolConfig) {
|
|
225
|
-
if (this.config.pool[key] === undefined) {
|
|
226
|
-
this.config.pool[key] = defaultPoolConfig[key];
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// 如果连接池已经初始化,更新其配置
|
|
231
|
-
if (this.connection_pool) {
|
|
232
|
-
this.connection_pool.config = this.config.pool;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
195
|
// 初始化连接池
|
|
236
196
|
this._initPool();
|
|
237
197
|
// this.logger('debug','配置已更新');
|
|
@@ -242,24 +202,16 @@ Redis.prototype.setConfig = function (config) {
|
|
|
242
202
|
// 添加连接池初始化方法
|
|
243
203
|
Redis.prototype._initPool = function () {
|
|
244
204
|
// 确保连接池配置存在
|
|
245
|
-
if (!this.config.pool) {
|
|
246
|
-
this.config.pool = {
|
|
247
|
-
max_clients: 100,
|
|
248
|
-
min_idle: 5,
|
|
249
|
-
max_idle: 20,
|
|
250
|
-
idle_timeout: 30000
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
205
|
|
|
254
206
|
// 初始化连接池相关属性
|
|
255
207
|
this.connection_pool = {
|
|
256
208
|
available: [],
|
|
257
209
|
in_use: new Set(),
|
|
258
|
-
config: this.config
|
|
210
|
+
config: this.config // 直接引用配置对象,确保同步更新
|
|
259
211
|
};
|
|
260
212
|
|
|
261
213
|
// 记录连接池初始化
|
|
262
|
-
// this.logger('debug','连接池已初始化,配置:', this.config
|
|
214
|
+
// this.logger('debug','连接池已初始化,配置:', this.config);
|
|
263
215
|
};
|
|
264
216
|
|
|
265
217
|
/**
|
|
@@ -552,12 +504,6 @@ Redis.prototype.isConnected = function () {
|
|
|
552
504
|
return this.connected && this.conn && this.conn.isReady;
|
|
553
505
|
};
|
|
554
506
|
|
|
555
|
-
/**
|
|
556
|
-
* @description 查询或是设置缓存过期时间
|
|
557
|
-
* @param {String} key 键
|
|
558
|
-
* @param {Number} seconds 秒, 为空则查询有效时长
|
|
559
|
-
* @return {Promise<Number|Boolean>} 查询时长或设置结果, 时长为-1表示永不过期
|
|
560
|
-
*/
|
|
561
507
|
/**
|
|
562
508
|
* 获取或设置键的过期时间
|
|
563
509
|
* @param {String} key 键名
|
|
@@ -565,11 +511,12 @@ Redis.prototype.isConnected = function () {
|
|
|
565
511
|
* @return {Promise<Number|Boolean>} 不传seconds时返回剩余过期时间(-1表示无过期设置,-2表示已过期),传seconds时返回是否设置成功
|
|
566
512
|
*/
|
|
567
513
|
Redis.prototype.ttl = async function (key, seconds) {
|
|
514
|
+
// 参数验证
|
|
515
|
+
if (!key || typeof key !== 'string') {
|
|
516
|
+
throw new TypeError('key必须是非空字符串');
|
|
517
|
+
}
|
|
518
|
+
|
|
568
519
|
try {
|
|
569
|
-
// 参数验证
|
|
570
|
-
if (!key || typeof key !== 'string') {
|
|
571
|
-
throw new TypeError('key必须是非空字符串');
|
|
572
|
-
}
|
|
573
520
|
|
|
574
521
|
const connection = await this._getConnection();
|
|
575
522
|
try {
|
|
@@ -620,16 +567,15 @@ Redis.prototype.ttl = async function (key, seconds) {
|
|
|
620
567
|
* @return {Promise<Number>} 计算后的结果
|
|
621
568
|
*/
|
|
622
569
|
Redis.prototype.addInt = async function (key, num, seconds = 0) {
|
|
570
|
+
// 参数验证
|
|
571
|
+
if (!key || typeof key !== 'string') {
|
|
572
|
+
throw new TypeError('key必须是非空字符串');
|
|
573
|
+
}
|
|
574
|
+
if (typeof num !== 'number' || isNaN(num) || !Number.isInteger(num)) {
|
|
575
|
+
throw new TypeError('num必须是有效整数');
|
|
576
|
+
}
|
|
577
|
+
|
|
623
578
|
try {
|
|
624
|
-
// 参数验证
|
|
625
|
-
if (!key || typeof key !== 'string') {
|
|
626
|
-
this.logger('error', '参数错误: 键必须是非空字符串');
|
|
627
|
-
return false;
|
|
628
|
-
}
|
|
629
|
-
if (typeof num !== 'number' || isNaN(num) || !Number.isInteger(num)) {
|
|
630
|
-
this.logger('error', '参数错误: 数值必须是有效整数');
|
|
631
|
-
return false;
|
|
632
|
-
}
|
|
633
579
|
|
|
634
580
|
// 触发前置事件,允许修改或阻止操作
|
|
635
581
|
const ctx = {
|
|
@@ -738,17 +684,18 @@ Redis.prototype.addInt = async function (key, num, seconds = 0) {
|
|
|
738
684
|
* @return {Promise<Number>} 计算后的结果
|
|
739
685
|
*/
|
|
740
686
|
Redis.prototype.addFloat = async function (key, num, seconds = 0) {
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
}
|
|
687
|
+
// 参数验证
|
|
688
|
+
if (!key || typeof key !== 'string') {
|
|
689
|
+
throw new TypeError('key必须是非空字符串');
|
|
690
|
+
}
|
|
746
691
|
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
692
|
+
// 验证num参数
|
|
693
|
+
num = parseFloat(num);
|
|
694
|
+
if (isNaN(num)) {
|
|
695
|
+
throw new TypeError('num必须是有效的数值');
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
try {
|
|
752
699
|
|
|
753
700
|
// 触发前置事件,允许修改或阻止操作
|
|
754
701
|
const ctx = {
|
|
@@ -2289,7 +2236,7 @@ Redis.prototype._recycleConnection = function (connection) {
|
|
|
2289
2236
|
* @description 删除哈希字段
|
|
2290
2237
|
* @param {String} key 键
|
|
2291
2238
|
* @param {String|Array<String>} fields 字段名或字段名数组
|
|
2292
|
-
* @return {Promise<
|
|
2239
|
+
* @return {Promise<Number>} 删除的字段数量
|
|
2293
2240
|
*/
|
|
2294
2241
|
Redis.prototype.hdel = async function (key, fields) {
|
|
2295
2242
|
try {
|
|
@@ -2321,23 +2268,24 @@ Redis.prototype.hdel = async function (key, fields) {
|
|
|
2321
2268
|
finalFields = [finalFields];
|
|
2322
2269
|
}
|
|
2323
2270
|
|
|
2271
|
+
let deletedCount = 0;
|
|
2324
2272
|
// 执行删除操作
|
|
2325
2273
|
if (typeof connection.hDel === 'function') {
|
|
2326
|
-
await connection.hDel(finalKey, finalFields);
|
|
2274
|
+
deletedCount = await connection.hDel(finalKey, finalFields);
|
|
2327
2275
|
} else if (typeof connection.execute === 'function') {
|
|
2328
|
-
await connection.execute('HDEL', [finalKey, ...finalFields]);
|
|
2276
|
+
deletedCount = await connection.execute('HDEL', [finalKey, ...finalFields]);
|
|
2329
2277
|
} else {
|
|
2330
2278
|
this.logger('error', '连接对象不支持hDel操作');
|
|
2331
|
-
|
|
2279
|
+
deletedCount = 0;
|
|
2332
2280
|
}
|
|
2333
2281
|
|
|
2334
2282
|
// 触发后置事件
|
|
2335
2283
|
this.emit('hdel:after', {
|
|
2336
2284
|
key: finalKey,
|
|
2337
2285
|
value: finalFields,
|
|
2338
|
-
|
|
2286
|
+
deletedCount
|
|
2339
2287
|
});
|
|
2340
|
-
return
|
|
2288
|
+
return deletedCount;
|
|
2341
2289
|
} finally {
|
|
2342
2290
|
this._recycleConnection(connection);
|
|
2343
2291
|
}
|
|
@@ -2349,7 +2297,7 @@ Redis.prototype.hdel = async function (key, fields) {
|
|
|
2349
2297
|
error: err
|
|
2350
2298
|
});
|
|
2351
2299
|
this.logger('error', `删除哈希字段失败 [${key}]:`, err);
|
|
2352
|
-
return
|
|
2300
|
+
return 0; // 与Redis语义保持一致,返回删除的字段数量
|
|
2353
2301
|
}
|
|
2354
2302
|
};
|
|
2355
2303
|
|
|
@@ -2543,7 +2491,7 @@ Redis.prototype._getConnection = function () {
|
|
|
2543
2491
|
// 如果连接数未达到上限,创建新连接
|
|
2544
2492
|
const totalConnections = this.connection_pool.available.length + this.connection_pool.in_use
|
|
2545
2493
|
.size;
|
|
2546
|
-
if (totalConnections < this.
|
|
2494
|
+
if (totalConnections < this.config.max_clients) {
|
|
2547
2495
|
this._createConnection().then(connection => {
|
|
2548
2496
|
this.connection_pool.in_use.add(connection);
|
|
2549
2497
|
resolve(connection);
|
|
@@ -2571,7 +2519,7 @@ Redis.prototype._releaseConnection = function (connection) {
|
|
|
2571
2519
|
this.connection_pool.in_use.delete(connection);
|
|
2572
2520
|
|
|
2573
2521
|
// 检查是否超过最大空闲连接数
|
|
2574
|
-
if (this.connection_pool.available.length < this.
|
|
2522
|
+
if (this.connection_pool.available.length < this.config.max_idle) {
|
|
2575
2523
|
this.connection_pool.available.push(connection);
|
|
2576
2524
|
} else {
|
|
2577
2525
|
// 否则关闭多余的连接
|
|
@@ -2646,7 +2594,7 @@ Redis.prototype.hset = async function (key, field, value) {
|
|
|
2646
2594
|
value: finalValue,
|
|
2647
2595
|
success: true
|
|
2648
2596
|
});
|
|
2649
|
-
return
|
|
2597
|
+
return true; // hSet操作成功返回true
|
|
2650
2598
|
} finally {
|
|
2651
2599
|
this._recycleConnection(connection);
|
|
2652
2600
|
}
|