@autofleet/rabbit 2.1.0 → 2.1.1
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/dist/index.d.ts +13 -5
- package/dist/index.js +41 -13
- package/dist/redis.d.ts +7 -0
- package/dist/redis.js +11 -0
- package/package.json +6 -2
- package/src/index.ts +53 -12
- package/src/redis.ts +15 -0
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { Options } from 'amqplib';
|
|
3
3
|
import { AmqpConnectionManager, ChannelWrapper } from 'amqp-connection-manager';
|
|
4
4
|
import { EventEmitter } from 'events';
|
|
5
|
+
import { RedisConfig } from './redis';
|
|
5
6
|
export interface IAfRabbitMq {
|
|
6
7
|
ack: any;
|
|
7
8
|
nack: any;
|
|
@@ -12,18 +13,23 @@ export interface IAfRabbitMq {
|
|
|
12
13
|
consumeFromExchange: any;
|
|
13
14
|
publish: any;
|
|
14
15
|
sendToQueue: any;
|
|
16
|
+
redisClient?: any;
|
|
15
17
|
}
|
|
16
18
|
interface ExchangesCache {
|
|
17
19
|
[key: string]: any;
|
|
18
20
|
}
|
|
21
|
+
declare type CustomMessageHeaders = {
|
|
22
|
+
redisTimestampValidationKey?: string;
|
|
23
|
+
};
|
|
19
24
|
export interface AfRabbitOptions {
|
|
20
25
|
reconnect: boolean;
|
|
21
26
|
}
|
|
22
27
|
declare class RabbitMq implements IAfRabbitMq {
|
|
23
28
|
static parseMsg(msg: any): any;
|
|
24
29
|
static validateName(type: string, name: string): void;
|
|
25
|
-
static getPublishOptions(): {
|
|
30
|
+
static getPublishOptions(customHeaders?: CustomMessageHeaders): {
|
|
26
31
|
timestamp: number;
|
|
32
|
+
headers: CustomMessageHeaders;
|
|
27
33
|
};
|
|
28
34
|
PUBLISH_TIMEOUT: number;
|
|
29
35
|
PUBLISH_ERROR_MSG: string;
|
|
@@ -35,8 +41,10 @@ declare class RabbitMq implements IAfRabbitMq {
|
|
|
35
41
|
creatingConnection: boolean;
|
|
36
42
|
exchanges: ExchangesCache;
|
|
37
43
|
options: AfRabbitOptions | undefined;
|
|
38
|
-
|
|
39
|
-
|
|
44
|
+
redisClient: any;
|
|
45
|
+
constructor(options?: AfRabbitOptions, redisConfig?: RedisConfig);
|
|
46
|
+
private shouldConsumeMessageByTimestamp;
|
|
47
|
+
ack: (channel: ChannelWrapper, shouldUpdateRedisTimestamp?: boolean) => (msg: any) => Promise<void>;
|
|
40
48
|
nack: (channel: ChannelWrapper, queue: string, options: any, deadQueueOptions: Options.AssertQueue) => (msg: any, { skipRetry, }?: any) => Promise<void>;
|
|
41
49
|
getConnection(): Promise<AmqpConnectionManager>;
|
|
42
50
|
getNewChannel(): Promise<ChannelWrapper>;
|
|
@@ -47,7 +55,7 @@ declare class RabbitMq implements IAfRabbitMq {
|
|
|
47
55
|
assertQueue(queue: string, options?: Options.AssertQueue): Promise<void>;
|
|
48
56
|
consume(queue: string, callback: (msg: any, ack: Function, nack: Function) => any, options?: any): Promise<void>;
|
|
49
57
|
consumeFromExchange(queue: string, exchange: string, callback: (msg: any, ack: Function, nack: Function) => any, options?: any): Promise<void>;
|
|
50
|
-
publish(exchange: string, content: any): Promise<unknown>;
|
|
51
|
-
sendToQueue(queue: string, content: any, options?: any): Promise<void>;
|
|
58
|
+
publish(exchange: string, content: any, customHeaders?: any): Promise<unknown>;
|
|
59
|
+
sendToQueue(queue: string, content: any, options?: any, customHeaders?: any): Promise<void>;
|
|
52
60
|
}
|
|
53
61
|
export default RabbitMq;
|
package/dist/index.js
CHANGED
|
@@ -39,6 +39,7 @@ const amqp_connection_manager_1 = require("amqp-connection-manager");
|
|
|
39
39
|
const events_1 = require("events");
|
|
40
40
|
const outbreak_1 = require("@autofleet/outbreak");
|
|
41
41
|
const rabbitError_1 = __importDefault(require("./rabbitError"));
|
|
42
|
+
const redis_1 = __importDefault(require("./redis"));
|
|
42
43
|
const DEFAULT_DEAD_TTL_TWO_DAYS = 60000 * 60 * 48;
|
|
43
44
|
const defaultOptions = {
|
|
44
45
|
limit: 1,
|
|
@@ -48,13 +49,26 @@ const defaultOptions = {
|
|
|
48
49
|
const assertExchangeFanout = (c, exchangeName) => c.assertExchange(exchangeName, 'fanout');
|
|
49
50
|
const connectionCreatedConst = 'connectionCreated';
|
|
50
51
|
class RabbitMq {
|
|
51
|
-
constructor(options) {
|
|
52
|
+
constructor(options, redisConfig) {
|
|
52
53
|
this.PUBLISH_TIMEOUT = Number(process.env.RABBITMQ_PUBLISH_TIMEOUT) || 60000;
|
|
53
54
|
this.PUBLISH_ERROR_MSG = `rabbit: publish timeout(${this.PUBLISH_TIMEOUT}ms) has pass, exchange: `;
|
|
54
55
|
this.DISCONNECT_MSG = 'rabbit: connection disconnect';
|
|
55
56
|
this.RESCONNECT_MSG = 'rabbit: reconnecting';
|
|
56
|
-
this.
|
|
57
|
+
this.shouldConsumeMessageByTimestamp = (msg) => __awaiter(this, void 0, void 0, function* () {
|
|
58
|
+
const { properties: { timestamp, headers } } = msg;
|
|
59
|
+
if (timestamp && (headers === null || headers === void 0 ? void 0 : headers.redisTimestampValidationKey) && this.redisClient) {
|
|
60
|
+
const lastMessageTimestamp = yield this.redisClient.getAsync(headers.redisTimestampValidationKey);
|
|
61
|
+
return !lastMessageTimestamp || !moment_1.default(parseInt(lastMessageTimestamp, 10)).isAfter(moment_1.default(timestamp));
|
|
62
|
+
}
|
|
63
|
+
return true;
|
|
64
|
+
});
|
|
65
|
+
this.ack = (channel, shouldUpdateRedisTimestamp = false) => (msg) => __awaiter(this, void 0, void 0, function* () {
|
|
57
66
|
yield channel.ack(msg);
|
|
67
|
+
const { properties: { timestamp, headers } } = msg;
|
|
68
|
+
if (shouldUpdateRedisTimestamp && timestamp && (headers === null || headers === void 0 ? void 0 : headers.redisTimestampValidationKey) && this.redisClient) {
|
|
69
|
+
const parsedTimestamp = parseInt(timestamp, 10);
|
|
70
|
+
yield this.redisClient.setAsync(headers.redisTimestampValidationKey, parsedTimestamp);
|
|
71
|
+
}
|
|
58
72
|
});
|
|
59
73
|
this.nack = (channel, queue, options, deadQueueOptions) => (msg, { skipRetry = false, } = {}) => __awaiter(this, void 0, void 0, function* () {
|
|
60
74
|
if (channel) {
|
|
@@ -78,6 +92,7 @@ class RabbitMq {
|
|
|
78
92
|
this.creatingConnection = false;
|
|
79
93
|
this.exchanges = {};
|
|
80
94
|
this.options = options;
|
|
95
|
+
this.redisClient = redisConfig && redis_1.default(redisConfig);
|
|
81
96
|
}
|
|
82
97
|
static parseMsg(msg) {
|
|
83
98
|
let { content } = msg;
|
|
@@ -93,9 +108,10 @@ class RabbitMq {
|
|
|
93
108
|
throw new rabbitError_1.default(`error while using ${type} with no name`);
|
|
94
109
|
}
|
|
95
110
|
}
|
|
96
|
-
static getPublishOptions() {
|
|
111
|
+
static getPublishOptions(customHeaders = {}) {
|
|
97
112
|
return {
|
|
98
113
|
timestamp: moment_1.default().unix() * 1000,
|
|
114
|
+
headers: customHeaders,
|
|
99
115
|
};
|
|
100
116
|
}
|
|
101
117
|
getConnection() {
|
|
@@ -200,10 +216,16 @@ class RabbitMq {
|
|
|
200
216
|
yield c.assertQueue(queue);
|
|
201
217
|
yield c.prefetch(limit, true);
|
|
202
218
|
return Promise.all([
|
|
203
|
-
c.consume(queue, (msg) => {
|
|
219
|
+
c.consume(queue, (msg) => __awaiter(this, void 0, void 0, function* () {
|
|
220
|
+
const parsedMessage = RabbitMq.parseMsg(msg);
|
|
204
221
|
outbreak_1.newTrace(outbreak_1.traceTypes.RABBIT);
|
|
205
|
-
|
|
206
|
-
|
|
222
|
+
const shouldConsume = yield this.shouldConsumeMessageByTimestamp(parsedMessage);
|
|
223
|
+
if (!shouldConsume) {
|
|
224
|
+
this.ack(channel);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
callback(parsedMessage, this.ack(channel, true), this.nack(channel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }));
|
|
228
|
+
})),
|
|
207
229
|
]);
|
|
208
230
|
}));
|
|
209
231
|
});
|
|
@@ -222,22 +244,28 @@ class RabbitMq {
|
|
|
222
244
|
yield c.prefetch(limit, true);
|
|
223
245
|
return Promise.all([
|
|
224
246
|
c.bindQueue(queue, exchange, ''),
|
|
225
|
-
c.consume(queue, (msg) => {
|
|
247
|
+
c.consume(queue, (msg) => __awaiter(this, void 0, void 0, function* () {
|
|
248
|
+
const parsedMessage = RabbitMq.parseMsg(msg);
|
|
226
249
|
outbreak_1.newTrace(outbreak_1.traceTypes.RABBIT);
|
|
227
|
-
|
|
228
|
-
|
|
250
|
+
const shouldConsume = yield this.shouldConsumeMessageByTimestamp(parsedMessage);
|
|
251
|
+
if (!shouldConsume) {
|
|
252
|
+
this.ack(channel);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
callback(parsedMessage, this.ack(channel, true), this.nack(channel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }));
|
|
256
|
+
})),
|
|
229
257
|
]);
|
|
230
258
|
}));
|
|
231
259
|
});
|
|
232
260
|
}
|
|
233
|
-
publish(exchange, content) {
|
|
261
|
+
publish(exchange, content, customHeaders) {
|
|
234
262
|
return __awaiter(this, void 0, void 0, function* () {
|
|
235
263
|
RabbitMq.validateName('exchange', exchange);
|
|
236
264
|
const channel = yield this.assertChannel();
|
|
237
265
|
yield this.assertExchange(exchange);
|
|
238
266
|
return new bluebird.Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
239
267
|
try {
|
|
240
|
-
yield channel.publish(exchange, '', Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions());
|
|
268
|
+
yield channel.publish(exchange, '', Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
|
|
241
269
|
return resolve();
|
|
242
270
|
}
|
|
243
271
|
catch (e) {
|
|
@@ -246,12 +274,12 @@ class RabbitMq {
|
|
|
246
274
|
})).timeout(this.PUBLISH_TIMEOUT, `${this.PUBLISH_ERROR_MSG}${exchange}`);
|
|
247
275
|
});
|
|
248
276
|
}
|
|
249
|
-
sendToQueue(queue, content, options) {
|
|
277
|
+
sendToQueue(queue, content, options, customHeaders) {
|
|
250
278
|
return __awaiter(this, void 0, void 0, function* () {
|
|
251
279
|
RabbitMq.validateName('queue', queue);
|
|
252
280
|
const channel = yield this.assertChannel();
|
|
253
281
|
yield this.assertQueue(queue, options);
|
|
254
|
-
return channel.sendToQueue(queue, Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions());
|
|
282
|
+
return channel.sendToQueue(queue, Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
|
|
255
283
|
});
|
|
256
284
|
}
|
|
257
285
|
}
|
package/dist/redis.d.ts
ADDED
package/dist/redis.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const redis_1 = __importDefault(require("redis"));
|
|
7
|
+
const bluebird_1 = __importDefault(require("bluebird"));
|
|
8
|
+
bluebird_1.default.promisifyAll(redis_1.default.RedisClient.prototype);
|
|
9
|
+
bluebird_1.default.promisifyAll(redis_1.default.Multi.prototype);
|
|
10
|
+
const getRedisInstance = (config) => redis_1.default.createClient(config);
|
|
11
|
+
exports.default = getRedisInstance;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@autofleet/rabbit",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -17,10 +17,14 @@
|
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@types/amqp-connection-manager": "^2.0.10",
|
|
19
19
|
"@types/amqplib": "^0.5.16",
|
|
20
|
+
"@types/redis": "^2.8.32",
|
|
21
|
+
"@types/uuid": "^8.3.3",
|
|
20
22
|
"amqp-connection-manager": "^3.2.1",
|
|
21
23
|
"amqplib": "^0.6.0",
|
|
22
24
|
"bluebird": "^3.7.2",
|
|
23
|
-
"moment": "^2.29.1"
|
|
25
|
+
"moment": "^2.29.1",
|
|
26
|
+
"redis": "^3.1.2",
|
|
27
|
+
"uuid": "^8.3.2"
|
|
24
28
|
},
|
|
25
29
|
"devDependencies": {
|
|
26
30
|
"@autofleet/outbreak": "*",
|
package/src/index.ts
CHANGED
|
@@ -6,7 +6,9 @@ import { ConfirmChannel, Options } from 'amqplib';
|
|
|
6
6
|
import { AmqpConnectionManager, ChannelWrapper, connect } from 'amqp-connection-manager';
|
|
7
7
|
import { EventEmitter } from 'events';
|
|
8
8
|
import { newTrace, traceTypes } from '@autofleet/outbreak';
|
|
9
|
+
import { RedisClient } from 'redis';
|
|
9
10
|
import RabbitError from './rabbitError';
|
|
11
|
+
import getRedisInstance, { RedisConfig } from './redis';
|
|
10
12
|
|
|
11
13
|
export interface IAfRabbitMq {
|
|
12
14
|
ack: any;
|
|
@@ -18,14 +20,19 @@ export interface IAfRabbitMq {
|
|
|
18
20
|
consumeFromExchange: any;
|
|
19
21
|
publish: any;
|
|
20
22
|
sendToQueue: any;
|
|
23
|
+
redisClient?: any;
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
interface ExchangesCache {
|
|
24
27
|
[key: string]: any
|
|
25
28
|
}
|
|
26
29
|
|
|
30
|
+
type CustomMessageHeaders = {
|
|
31
|
+
redisTimestampValidationKey?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
27
34
|
export interface AfRabbitOptions {
|
|
28
|
-
reconnect: boolean
|
|
35
|
+
reconnect: boolean;
|
|
29
36
|
}
|
|
30
37
|
|
|
31
38
|
const DEFAULT_DEAD_TTL_TWO_DAYS = 60000 * 60 * 48;
|
|
@@ -60,9 +67,10 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
60
67
|
}
|
|
61
68
|
}
|
|
62
69
|
|
|
63
|
-
static getPublishOptions() {
|
|
70
|
+
static getPublishOptions(customHeaders: CustomMessageHeaders = {}) {
|
|
64
71
|
return {
|
|
65
72
|
timestamp: moment().unix() * 1000,
|
|
73
|
+
headers: customHeaders,
|
|
66
74
|
};
|
|
67
75
|
}
|
|
68
76
|
|
|
@@ -86,17 +94,34 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
86
94
|
|
|
87
95
|
options: AfRabbitOptions | undefined;
|
|
88
96
|
|
|
89
|
-
|
|
97
|
+
redisClient: any;
|
|
98
|
+
|
|
99
|
+
constructor(options?: AfRabbitOptions, redisConfig?: RedisConfig) {
|
|
90
100
|
this.em = new EventEmitter();
|
|
91
101
|
this.channel = null;
|
|
92
102
|
this.connection = null;
|
|
93
103
|
this.creatingConnection = false;
|
|
94
104
|
this.exchanges = {};
|
|
95
105
|
this.options = options;
|
|
106
|
+
this.redisClient = redisConfig && getRedisInstance(redisConfig);
|
|
96
107
|
}
|
|
97
108
|
|
|
98
|
-
|
|
109
|
+
private shouldConsumeMessageByTimestamp = async (msg: any) => {
|
|
110
|
+
const { properties: { timestamp, headers } } = msg;
|
|
111
|
+
if (timestamp && headers?.redisTimestampValidationKey && this.redisClient) {
|
|
112
|
+
const lastMessageTimestamp = await this.redisClient.getAsync(headers.redisTimestampValidationKey);
|
|
113
|
+
return !lastMessageTimestamp || !moment(parseInt(lastMessageTimestamp, 10)).isAfter(moment(timestamp));
|
|
114
|
+
}
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
public ack = (channel: ChannelWrapper, shouldUpdateRedisTimestamp = false) => async (msg: any) => {
|
|
99
119
|
await channel.ack(msg);
|
|
120
|
+
const { properties: { timestamp, headers } } = msg;
|
|
121
|
+
if (shouldUpdateRedisTimestamp && timestamp && headers?.redisTimestampValidationKey && this.redisClient) {
|
|
122
|
+
const parsedTimestamp = parseInt(timestamp, 10);
|
|
123
|
+
await this.redisClient.setAsync(headers.redisTimestampValidationKey, parsedTimestamp);
|
|
124
|
+
}
|
|
100
125
|
}
|
|
101
126
|
|
|
102
127
|
public nack = (
|
|
@@ -223,9 +248,15 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
223
248
|
return Promise.all([
|
|
224
249
|
c.consume(
|
|
225
250
|
queue,
|
|
226
|
-
(msg: any) => {
|
|
251
|
+
async (msg: any) => {
|
|
252
|
+
const parsedMessage = RabbitMq.parseMsg(msg);
|
|
227
253
|
newTrace(traceTypes.RABBIT);
|
|
228
|
-
|
|
254
|
+
const shouldConsume = await this.shouldConsumeMessageByTimestamp(parsedMessage);
|
|
255
|
+
if (!shouldConsume) {
|
|
256
|
+
this.ack(channel);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
callback(parsedMessage, this.ack(channel, true), this.nack(channel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }));
|
|
229
260
|
},
|
|
230
261
|
),
|
|
231
262
|
]);
|
|
@@ -248,22 +279,30 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
248
279
|
c.bindQueue(queue, exchange, ''),
|
|
249
280
|
c.consume(
|
|
250
281
|
queue,
|
|
251
|
-
(msg: any) => {
|
|
282
|
+
async (msg: any) => {
|
|
283
|
+
const parsedMessage = RabbitMq.parseMsg(msg);
|
|
252
284
|
newTrace(traceTypes.RABBIT);
|
|
253
|
-
|
|
285
|
+
const shouldConsume = await this.shouldConsumeMessageByTimestamp(parsedMessage);
|
|
286
|
+
if (!shouldConsume) {
|
|
287
|
+
this.ack(channel);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
callback(parsedMessage, this.ack(channel, true), this.nack(channel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }));
|
|
254
291
|
},
|
|
255
292
|
),
|
|
256
293
|
]);
|
|
257
294
|
});
|
|
258
295
|
}
|
|
259
296
|
|
|
260
|
-
async publish(exchange: string, content: any) {
|
|
297
|
+
async publish(exchange: string, content: any, customHeaders?: any) {
|
|
261
298
|
RabbitMq.validateName('exchange', exchange);
|
|
262
299
|
const channel: ChannelWrapper = await this.assertChannel();
|
|
263
300
|
await this.assertExchange(exchange);
|
|
264
301
|
return new bluebird.Promise(async (resolve, reject) => {
|
|
265
302
|
try {
|
|
266
|
-
await channel.publish(exchange, '',
|
|
303
|
+
await channel.publish(exchange, '',
|
|
304
|
+
Buffer.from(JSON.stringify(content)),
|
|
305
|
+
RabbitMq.getPublishOptions(customHeaders));
|
|
267
306
|
return resolve();
|
|
268
307
|
} catch (e) {
|
|
269
308
|
reject(e);
|
|
@@ -271,11 +310,13 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
271
310
|
}).timeout(this.PUBLISH_TIMEOUT, `${this.PUBLISH_ERROR_MSG}${exchange}`);
|
|
272
311
|
}
|
|
273
312
|
|
|
274
|
-
async sendToQueue(queue: string, content: any, options?: any) {
|
|
313
|
+
async sendToQueue(queue: string, content: any, options?: any, customHeaders?: any) {
|
|
275
314
|
RabbitMq.validateName('queue', queue);
|
|
276
315
|
const channel: ChannelWrapper = await this.assertChannel();
|
|
277
316
|
await this.assertQueue(queue, options);
|
|
278
|
-
return channel.sendToQueue(queue,
|
|
317
|
+
return channel.sendToQueue(queue,
|
|
318
|
+
Buffer.from(JSON.stringify(content)),
|
|
319
|
+
RabbitMq.getPublishOptions(customHeaders));
|
|
279
320
|
}
|
|
280
321
|
}
|
|
281
322
|
|
package/src/redis.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import redis from 'redis';
|
|
2
|
+
import bluebird from 'bluebird';
|
|
3
|
+
|
|
4
|
+
bluebird.promisifyAll(redis.RedisClient.prototype);
|
|
5
|
+
bluebird.promisifyAll(redis.Multi.prototype);
|
|
6
|
+
|
|
7
|
+
export type RedisConfig = {
|
|
8
|
+
host: string;
|
|
9
|
+
port: number | undefined;
|
|
10
|
+
prefix?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const getRedisInstance = (config: RedisConfig): any => redis.createClient(config);
|
|
14
|
+
|
|
15
|
+
export default getRedisInstance;
|