跳转至

缓存

缓存是一种伟大而简单的技术,可以帮助你提高应用程序的性能 它充当临时数据存储,提供高性能数据访问。

安装

首先安装所需的软件包:

$ npm install cache-manager
$ npm install -D @types/cache-manager

内存缓存

Nest 为各种缓存存储提供器提供了统一的 API 内置的是内存中的数据存储 不过,你可以很容易地切换到一个更全面的解决方案,比如 Redis。

为了启用缓存,导入CacheModule并调用它的register()方法。

1
2
3
4
5
6
7
8
import { CacheModule, Module } from`@nestjs/common';
import { AppController } from`./app.controller';

@Module({
  imports: [CacheModule.register()],
  controllers: [AppController],
})
export class AppModule {}

与缓存存储交互

要与缓存管理器实例交互,使用CACHE_MANAGER令牌将其注入到你的类中,如下所示:

constructor(@Inject(CACHE_MANAGER) private cacheManager: Cache) {}

Hint

Cache类是从cache-manager导入的,而CACHE_MANAGER令牌是从@nestjs/common包导入的。

Cache实例的get方法(来自cache-manager包)用于从缓存中检索项 如果该项在缓存中不存在,则返回null

const value = await this.cacheManager.get('key');

要向缓存中添加一个项,请使用set方法:

await this.cacheManager.set('key', 'value');

缓存的默认过期时间为 5 秒。

您可以手动为这个特定的密钥指定一个 TTL(以秒为单位的过期时间),如下所示:

await this.cacheManager.set('key', 'value', { ttl: 1000 });

缓存的默认过期时间为 5 秒。

您可以手动为这个特定的密钥指定一个 TTL(以秒为单位的过期时间),如下所示:

await this.cacheManager.set('key', 'value', { ttl: 0 });

要从缓存中删除一个项,使用del方法:

await this.cacheManager.del('key');

要清除整个缓存,请使用reset方法:

await this.cacheManager.reset();

自动缓存响应

Warning

GraphQL应用程序中,拦截器是为每个字段解析器单独执行的

因此,CacheModule(使用拦截器来缓存响应)将无法正常工作。

要启用自动缓存响应,只需将CacheInterceptor绑定在你想要缓存数据的地方。

1
2
3
4
5
6
7
8
@Controller()
@UseInterceptors(CacheInterceptor)
export class AppController {
  @Get()
  findAll(): string[] {
    return [];
  }
}

Warning

只缓存GET端点

此外,注入本机响应对象(@Res())的 HTTP 服务器路由不能使用缓存拦截器 请参阅响应映射了解更多详细信息。

为了减少所需样板文件的数量,你可以全局绑定CacheInterceptor到所有端点:

import { CacheModule, Module, CacheInterceptor } from`@nestjs/common';
import { AppController } from`./app.controller';
import { APP_INTERCEPTOR } from`@nestjs/core';

@Module({
  imports: [CacheModule.register()],
  controllers: [AppController],
  providers: [
    {
      provide: APP_INTERCEPTOR,
      useClass: CacheInterceptor,
    },
  ],
})
export class AppModule {}

定制高速缓存

所有缓存数据都有自己的过期时间(TTL) 要自定义默认值,将选项对象传递给register()方法。

1
2
3
4
CacheModule.register({
  ttl: 5, // seconds
  max: 10, // maximum number of items in cache
});

在全局范围内使用模块

当你想在其他模块中使用CacheModule时,你需要导入它(就像任何 Nest 模块一样) 或者,通过设置 options 对象的isGlobal属性为true来声明它为global 模块,如下所示 在这种情况下,一旦CacheModule被加载到根模块(例如AppModule),你就不需要在其他模块中导入它了。

1
2
3
CacheModule.register({
  isGlobal: true,
});

全局缓存覆盖

当全局缓存被启用时,缓存条目被存储在一个CacheKey下,这个CacheKey是根据路由路径自动生成的 你可以在每个方法的基础上重写某些缓存设置(@CacheKey()@CacheTTL()),允许为每个控制器方法定制缓存策略 在使用[不同的缓存存储]时这可能是最相关的。(https://docs.nestjs.com/techniques/caching#different-stores)

1
2
3
4
5
6
7
8
@Controller()
export class AppController {
  @CacheKey('custom_key')
  @CacheTTL(20)
  findAll(): string[] {
    return [];
  }
}

Hint

@CacheKey()@CacheTTL()装饰器是从@nestjs/common包中导入的。

@CacheKey()修饰符可以和对应的@CacheTTL()修饰符一起使用,也可以不使用,反之亦然 可以选择只覆盖@CacheKey()或只覆盖@CacheTTL() 没有被装饰器覆盖的设置将使用全局注册的默认值(参见自定义缓存)。

WebSockets 和 Microservices

你也可以将CacheInterceptor应用到 WebSocket 订阅者和 Microservice 的模式中(不管使用的传输方法是什么)。

1
2
3
4
5
6
@CacheKey('events')
@UseInterceptors(CacheInterceptor)
@SubscribeMessage('events')
handleEvent(client: Client, data: string[]): Observable<string[]> {
  return [];
}
1
2
3
4
5
6
@CacheKey('events')
@UseInterceptors(CacheInterceptor)
@SubscribeMessage('events')
handleEvent(client, data) {
  return [];
}

然而,额外的@CacheKey()修饰符是必需的,以便指定一个用于随后存储和检索缓存数据的键 此外,请注意,您 不应该缓存所有内容 执行某些业务操作而不是简单地查询数据的操作永远不应该被缓存。

此外,您可以使用@CacheTTL()装饰器指定缓存过期时间(TTL),它将覆盖全局默认 TTL 值。

1
2
3
4
5
6
@CacheTTL(10)
@UseInterceptors(CacheInterceptor)
@SubscribeMessage('events')
handleEvent(client: Client, data: string[]): Observable<string[]> {
  return [];
}
1
2
3
4
5
6
@CacheTTL(10)
@UseInterceptors(CacheInterceptor)
@SubscribeMessage('events')
handleEvent(client, data) {
  return [];
}

Hint

@CacheTTL()修饰符可以与对应的@CacheKey()修饰符一起使用,也可以不使用。

调整跟踪

默认情况下,Nest 使用请求 URL(在 HTTP 应用程序中)或缓存键(在 websockets 和微服务应用程序中,通过@CacheKey()装饰器设置)来将缓存记录与端点关联起来 然而,有时您可能希望基于不同的因素设置跟踪,例如,使用 HTTP 头(例如 '授权'以正确标识'配置文件'端点)。

为了实现这一点,创建一个CacheInterceptor的子类,并覆盖trackBy()方法。

1
2
3
4
5
6
@Injectable()
class HttpCacheInterceptor extends CacheInterceptor {
  trackBy(context: ExecutionContext): string | undefined {
    return`key';
  }
}

不同的商店

该服务在内部利用了cache-manager cache-manager包支持广泛的有用的存储,例如Redis store 支持的商店的完整列表可以获得此处 要设置 Redis store,只需将包连同相应的选项传递给register()方法。

import type { ClientOpts as RedisClientOpts } from`redis';
import * as redisStore from`cache-manager-redis-store';
import { CacheModule, Module } from`@nestjs/common';
import { AppController } from`./app.controller';

@Module({
  imports: [
    CacheModule.register<RedisClientOpts>({
      store: redisStore,
      // Store-specific configuration:
      host:`localhost',
      port: 6379,
    }),
  ],
  controllers: [AppController],
})
export class AppModule {}

异步的配置

你可能想要异步传递模块选项,而不是在编译时静态传递 在这种情况下,使用registerAsync()方法,它提供了几种处理异步配置的方法。

一种方法是使用工厂函数:

1
2
3
4
5
CacheModule.registerAsync({
  useFactory: () => ({
    ttl: 5,
  }),
});

我们的工厂行为与所有其他异步模块工厂一样(它可以是async,并能够通过inject注入依赖)。

1
2
3
4
5
6
7
CacheModule.registerAsync({
  imports: [ConfigModule],
  useFactory: async (configService: ConfigService) => ({
    ttl: configService.get('CACHE_TTL'),
  }),
  inject: [ConfigService],
});

或者,你可以使用useClass方法:

1
2
3
CacheModule.registerAsync({
  useClass: CacheConfigService,
});

上面的构造将在CacheModule中实例化CacheConfigService,并使用它来获取选项对象 为了提供配置选项,CacheConfigService必须实现CacheOptionsFactory接口:

1
2
3
4
5
6
7
8
@Injectable()
class CacheConfigService implements CacheOptionsFactory {
  createCacheOptions(): CacheModuleOptions {
    return {
      ttl: 5,
    };
  }
}

如果你希望使用从不同模块导入的现有配置提供程序,请使用useExisting语法:

1
2
3
4
CacheModule.registerAsync({
  imports: [ConfigModule],
  useExisting: ConfigService,
});

它的工作原理与useClass相同,但有一个关键的区别——CacheModule将查找导入的模块来重用任何已经创建的ConfigService,而不是实例化它自己的模块。

Hint

CacheModule#registerCacheModule#registerAsyncCacheOptionsFactory有一个可选的泛型(类型参数)来缩小特定于存储的配置选项,使其类型安全。

例子

此处提供了一个工作示例。