侧边栏壁纸
博主头像
MicroMatrix博主等级

曲则全,枉则直,洼则盈,敝则新,少则得,多则惑。是以圣人抱一为天下式。不自见,故明;不自是,故彰;不自伐,故有功;不自矜,故长。夫唯不争,故天下莫能与之争。古之所谓“曲则全”者,岂虚言哉!诚全而归之。

  • 累计撰写 80 篇文章
  • 累计创建 21 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

NestJs 集成Redis

蜗牛
2024-04-07 / 0 评论 / 0 点赞 / 2 阅读 / 5803 字 / 正在检测是否收录...

前言

在日常开发中,需要处理一些高并发的请求或者处理用户邮件认证等一些场景。那么就需要Redis了。

Redis 是一个开源的内存中数据结构存储系统,它可以用作数据库、缓存和消息中间件。

安装包

按照需要的包

pnpm install ioredis @nestjs-modules/ioredis @nestjs/common

这里我们使用 ​**ioredis**​,因为它是一个健壮的、功能全面的 Redis 客户端,与 **nest-modules**相关联。

编写Redis服务模块

新建一个文件夹Redis,然后里面新建redis.module.ts文件,写入如下内容。

import { Module } from '@nestjs/common';
import { RedisModule } from '@nestjs-modules/ioredis';
@Module({
  imports: [
    RedisModule.forRoot({
      type: 'single',
      url: '127.0.0.1',
      options: {
        port: 6379,
        password: 'password'
      }
    })
  ]
})
export class RedisCacheModule {}

之后在app.module.ts中导入

import { RedisCacheModule } from './redis/redis.module';
Module({
	imports:[
		 RedisCacheModule
	]
})
export class AppModule {}

使用案例

在需要使用到地方,例如我需要在users.service.ts中的接口使用

import { Injectable } from '@nestjs/common';
import { InjectRedis } from '@nestjs-modules/ioredis';
import Redis from 'ioredis';
@Injectable()
export class UsersService {
  constructor(
    @InjectRedis() private readonly redis: Redis
  ) {
	  async setUserPeerId(): Promise<string> {
	    // 使用 Redis 设置值
	    await this.redis.set('hello', 'Hello from Redis!');
	    // 使用 Redis 获取值
	    return this.redis.get('hello');
	  }
  }
}

然后在对应的controller层调用函数。

扩展:接口访问限制频率

为了防止有人故意刷接口,或者对过多的请求搞奔溃应用,这里使用Redis做接口的限制

定义限流拦截器

// api-rate-limiter.interceptor.ts
import {
  Injectable,
  NestInterceptor,
  ExecutionContext,
  CallHandler,
  HttpException,
  HttpStatus,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { InjectRedis, Redis } from '@nestjs-modules/ioredis';
import { tap } from 'rxjs/operators';

@Injectable()
export class ApiRateLimiterInterceptor implements NestInterceptor {
  constructor(@InjectRedis() private readonly redis: Redis) {}

  async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>> {
    const key = 'rate-limit:' + context.switchToHttp().getRequest().ip;
    const currentRequestCount = await this.redis.incr(key);

    if (currentRequestCount === 1) {
      // 设置 key 的超时时间
      await this.redis.expire(key, 60); // 限流周期为 60 秒
    }

    if (currentRequestCount > 10) {
      throw new HttpException('Too many requests', HttpStatus.TOO_MANY_REQUESTS);
    }

    return next.handle().pipe(
      tap(() => {
        // 在响应完成后,你可以在这里执行一些操作。
      }),
    );
  }
}

项目中使用限流拦截器

将这个拦截器中间件引入到你的应用中,可以在对应的控制器或全局应用中注册。

  1. 全局拦截

    import { ApiRateLimiterInterceptor } from './redis/api-rate-limiter.interceptor';
    import { APP_INTERCEPTOR } from '@nestjs/core';
    Module({
      providers: [
        {
          provide: APP_INTERCEPTOR,
          useClass: ApiRateLimiterInterceptor
        }
      ]
    })
    export class AppModule {}
    
    // - 拦截器(Interceptor):用于在方法执行之前或之后添加额外逻辑,它们可以操作由函数返回的结果或异常。
    // - 中间件(Middleware):通常用于执行请求前的一些操作,比如日志、请求解析等,是直接实现use方法的类。
    // 一些博客中将拦截器用中间件来引入。这是不合适的
    
  2. 局部拦截

    // user.controller.ts
    import { Controller, UseInterceptors } from '@nestjs/common';
    import { ApiRateLimiterInterceptor } from './api-rate-limiter.interceptor';
    
    @Controller('user')
    @UseInterceptors(ApiRateLimiterInterceptor) // 应用到UserController中的所有路由
    export class UserController {
      // ...
    }
    
    // 或者
    
    @Controller('user')
    export class UserController {
      @UseInterceptors(ApiRateLimiterInterceptor) // 仅应用到特定的路由处理程序
      @Get('some-path')
      async someMethod() {
        // ...
      }
      // ...
    }
    

    我这里为了统一方便就引入在全局上

0

评论区