nestjs 连接redis
1、安装
npm i ioredis
2、创建redisService(其实就是定义了一个类,把new Redis返回的实例进行了绑定 然后导出)
import { ConfigService } from "@nestjs/config";
import Redis from "ioredis";
export class RedisService {
private redisClient: any;
constructor(private readonly configService: ConfigService) {
this.redisClient = new Redis({
host: configService.get("redis.host"),
port: configService.get("redis.port"),
db: configService.get("redis.db"),
password: configService.get("redis.password"),
// 设置重连次数
retryStrategy: (times: number) => {
if (times > 3) {
return null;
}
}
})
}
// 当模块销毁时
OnModuleDestroy() {
// 断开连接
this.redisClient.quit()
}
getClient() {
return this.redisClient
}
/* --------------------- string 相关 -------------------------- */
/**
*
* @param key 存储 key 值
* @param val key 对应的 val
* @param ttl 可选,过期时间,单位 毫秒
*/
async set(key: string, val: any, ttl?: number): Promise<'OK' | null> {
const data = JSON.stringify(val);
if (!ttl) return await this.redisClient.set(key, data);
return await this.redisClient.set(key, data, 'PX', ttl);
}
async mget(keys: string[]): Promise<any[]> {
if (!keys) return [];
const list = await this.redisClient.mget(keys);
return list.map((item) => JSON.parse(item));
}
/**
* 返回对应 value
* @param key
*/
async get(key: string): Promise<any> {
if (!key || key === '*') return null;
const res = await this.redisClient.get(key);
return JSON.parse(res);
}
async del(keys: string | string[]): Promise<number> {
if (!keys || keys === '*') return 0;
if (typeof keys === 'string') keys = [keys];
return await this.redisClient.del(...keys);
}
async ttl(key: string): Promise<number | null> {
if (!key) return null;
return await this.redisClient.ttl(key);
}
/**
* 获取对象keys
* @param key
*/
async keys(key?: string) {
return await this.redisClient.keys(key);
}
/* ----------------------- hash ----------------------- */
/**
* hash 设置 key 下单个 field value
* @param key
* @param field 属性
* @param value 值
*/
async hset(key: string, field: string, value: string): Promise<string | number | null> {
if (!key || !field) return null;
return await this.redisClient.hset(key, field, value);
}
/**
* hash 设置 key 下多个 field value
* @param key
* @param data
* @params expire 单位 秒
*/
async hmset(key: string, data: Record<string, string | number | boolean>, expire?: number): Promise<number | any> {
if (!key || !data) return 0;
const result = await this.redisClient.hmset(key, data);
if (expire) {
await this.redisClient.expire(key, expire);
}
return result;
}
/**
* hash 获取单个 field 的 value
* @param key
* @param field
*/
async hget(key: string, field: string): Promise<number | string | null> {
if (!key || !field) return 0;
return await this.redisClient.hget(key, field);
}
/**
* hash 获取 key 下所有field 的 value
* @param key
*/
async hvals(key: string): Promise<string[]> {
if (!key) return [];
return await this.redisClient.hvals(key);
}
async hGetAll(key: string): Promise<Record<string, string>> {
return await this.redisClient.hgetall(key);
}
/**
* hash 删除 key 下 一个或多个 fields value
* @param key
* @param fields
*/
async hdel(key: string, fields: string | string[]): Promise<string[] | number> {
if (!key || fields.length === 0) return 0;
return await this.redisClient.hdel(key, ...fields);
}
/**
* hash 删除 key 下所有 fields value
* @param key
*/
async hdelAll(key: string): Promise<string[] | number> {
if (!key) return 0;
const fields = await this.redisClient.hkeys(key);
if (fields.length === 0) return 0;
return await this.hdel(key, fields);
}
/* ----------- list 相关操作 ------------------ */
/**
* 获取列表长度
* @param key
*/
async lLength(key: string): Promise<number> {
if (!key) return 0;
return await this.redisClient.llen(key);
}
/**
* 通过索引设置列表元素的值
* @param key
* @param index
* @param val
*/
async lSet(key: string, index: number, val: string): Promise<'OK' | null> {
if (!key || index < 0) return null;
return await this.redisClient.lset(key, index, val);
}
/**
* 通过索引获取 列表中的元素
* @param key
* @param index
*/
async lIndex(key: string, index: number): Promise<string | null> {
if (!key || index < 0) return null;
return await this.redisClient.lindex(key, index);
}
/**
* 获取列表指定范围内的元素
* @param key
* @param start 开始位置, 0 是开始位置
* @param stop 结束位置, -1 返回所有
*/
async lRange(key: string, start: number, stop: number): Promise<string[] | null> {
if (!key) return null;
return await this.redisClient.lrange(key, start, stop);
}
/**
* 将一个或多个值插入到列表头部
* @param key
* @param val
*/
async lLeftPush(key: string, ...val: string[]): Promise<number> {
if (!key) return 0;
return await this.redisClient.lpush(key, ...val);
}
/**
* 将一个值或多个值插入到已存在的列表头部
* @param key
* @param val
*/
async lLeftPushIfPresent(key: string, ...val: string[]): Promise<number> {
if (!key) return 0;
return await this.redisClient.lpushx(key, ...val);
}
/**
* 如果 pivot 存在,则在 pivot 前面添加
* @param key
* @param pivot
* @param val
*/
async lLeftInsert(key: string, pivot: string, val: string): Promise<number> {
if (!key || !pivot) return 0;
return await this.redisClient.linsert(key, 'BEFORE', pivot, val);
}
/**
* 如果 pivot 存在,则在 pivot 后面添加
* @param key
* @param pivot
* @param val
*/
async lRightInsert(key: string, pivot: string, val: string): Promise<number> {
if (!key || !pivot) return 0;
return await this.redisClient.linsert(key, 'AFTER', pivot, val);
}
/**
* 在列表中添加一个或多个值
* @param key
* @param val
*/
async lRightPush(key: string, ...val: string[]): Promise<number> {
if (!key) return 0;
return await this.redisClient.lpush(key, ...val);
}
/**
* 为已存在的列表添加一个或多个值
* @param key
* @param val
*/
async lRightPushIfPresent(key: string, ...val: string[]): Promise<number> {
if (!key) return 0;
return await this.redisClient.rpushx(key, ...val);
}
/**
* 移除并获取列表第一个元素
* @param key
*/
async lLeftPop(key: string): Promise<string> {
if (!key) return '';
const result = await this.redisClient.blpop(key);
return result.length > 0 ? result[0] : '';
}
/**
* 移除并获取列表最后一个元素
* @param key
*/
async lRightPop(key: string): Promise<string> {
if (!key) return '';
const result = await this.redisClient.brpop(key);
return result.length > 0 ? result[0] : '';
}
/**
* 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除
* @param key
* @param start
* @param stop
*/
async lTrim(key: string, start: number, stop: number): Promise<'OK' | null> {
if (!key) return null;
return await this.redisClient.ltrim(key, start, stop);
}
/**
* 移除列表元素
* @param key
* @param count
* count > 0 :从表头开始向表尾搜索,移除与 value 相等的元素,数量为 count;
* count < 0 :从表尾开始向表头搜索,移除与 value 相等的元素,数量为 count 的绝对值;
* count = 0 : 移除表中所有与 value 相等的值
* @param val
*/
async lRemove(key: string, count: number, val: string): Promise<number> {
if (!key) return 0;
return await this.redisClient.lrem(key, count, val);
}
/**
* 移除列表最后一个元素,并将该元素添加到另一个裂膏并返回
* 如果列表没有元素会阻塞队列直到等待超时或发现可弹出元素为止
* @param sourceKey
* @param destinationKey
* @param timeout
*/
async lPoplPush(sourceKey: string, destinationKey: string, timeout: number): Promise<string> {
if (!sourceKey || !destinationKey) return '';
return await this.redisClient.brpoplpush(sourceKey, destinationKey, timeout);
}
/**
* 删除全部缓存
* @returns
*/
async reset() {
const keys = await this.redisClient.keys('*');
return this.redisClient.del(keys);
}
}
3、全局注入
就是用工厂函数实例化RedisService,然后导出
import { Global, Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm';
import configurationService from '../config/index';
import { join } from 'path';
import { ThrottlerGuard, ThrottlerModule } from '@nestjs/throttler';
import { APP_GUARD } from '@nestjs/core';
import { JwtModule } from '@nestjs/jwt';
import { JwtAuthGuard } from '../utils/auth/jwt-auth.guard';
import { JwtAuthStrategy } from '../utils/auth/jwt-auth.strategy';
import { RedisService } from 'src/utils/redisService';
@Global()
@Module({
imports: [
ConfigModule.forRoot({
cache: true,
load: [configurationService],
isGlobal: true,
}),
TypeOrmModule.forRootAsync({
inject: [ConfigService],
useFactory: (config: ConfigService) => {
return ({
type: "mysql",
autoLoadEntities: true, //自动加载实体
timezone: '+08:00',
...config.get("db.mysql"),
} as TypeOrmModuleOptions)
}
}),
JwtModule.registerAsync({
inject: [ConfigService],
useFactory: (config: ConfigService) => {
return {
secret: config.get("jwt.secretkey"),
signOptions: {
expiresIn: config.get("jwt.expiresin"),
},
}
}
}),
ThrottlerModule.forRoot([
{
name: 'short',
ttl: 1000,
limit: 1,
}
])
],
providers: [
JwtAuthStrategy,
{
provide: APP_GUARD,
useClass: ThrottlerGuard,
},
{
provide: APP_GUARD,
useClass: JwtAuthGuard,
},
{
provide: RedisService,
inject: [ConfigService],
useFactory: (config: ConfigService) => {
return new RedisService(config)
},
}
],
exports: [
TypeOrmModule,
JwtAuthStrategy,
JwtModule,
RedisService
],
})
export class ShareModule { }
4、使用
import { Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { RedisService } from 'src/utils/redisService';
@Injectable()
export class UsersService {
constructor(
private readonly redisService: RedisService
) { }
async create(createUserDto: CreateUserDto) {
await this.redisService.set("测试",1);
const test = await this.redisService.get("测试");
return test;
}
findAll() {
return `This action returns all users`;
}
findOne(id: number) {
return `This action returns a #${id} user`;
}
update(id: number, updateUserDto: UpdateUserDto) {
return `This action updates a #${id} user`;
}
remove(id: number) {
return `This action removes a #${id} user`;
}
}