## 緩存(Caching)
緩存是一項偉大而簡單的技術,可以幫助提高應用程序的性能。它充當臨時數據存儲,提供高性能的數據訪問。
### 安裝
我們首先需要安裝所需的包:
```bash
$ npm install cache-manager
$ npm install -D @types/cache-manager
```
### 內存緩存
`Nest`為各種緩存存儲提供程序提供了統一的 `API`。內置的是內存中的數據存儲。但是,您可以輕松地切換到更全面的解決方案,比如 `Redis` 。為了啟用緩存,首先導入 `CacheModule` 并調用它的 `register()` 方法。
```typescript
import { CacheModule, Module } from '@nestjs/common';
import { AppController } from './app.controller';
@Module({
imports: [CacheModule.register()],
controllers: [AppController],
})
export class ApplicationModule {}
```
### 與緩存存儲的交互
為了和緩存管理器實例進行交互,需要使用`CACHE_MANAGER`標記將其注入到你的類,如下所示:
```typescript
constructor(@Inject(CACHE_MANAGER) private cacheManager: Cache) {}
```
> `Cache`類是從`cache-manager`中導入的,而`CACHE_MANAGER`則是從`@nestjs/common`包中導入的。
`Cache`實例(來自cache-manager包)上的`get`方法被用來從緩存中檢索鍵值。如果該鍵在緩存中不存在,則返回null。
```typescript
const value = await this.cacheManager.get('key');
```
使用`set`方法將一個鍵值對添加到緩存中:
```typescript
await this.cacheManager.set('key', 'value');
```
緩存的默認過期時間是5秒。
你可以為特定的鍵手動指定一個TTL(過期時間,以秒為單位),如下所示:
```typescript
await this.cacheManager.set('key', 'value', { ttl: 1000 });
```
如果要讓緩存永不過期,請將配置的`ttl`屬性設置為`0`。
```typescript
await this.cacheManager.set('key', 'value', { ttl: 0 });
```
使用`del`方法從緩存中刪除一個鍵值對:
```typescript
await this.cacheManager.del('key');
```
使用`reset`方法清空整個緩存:
```typescript
await this.cacheManager.reset();
```
### 自動緩存響應
> 在 [GraphQL](https://docs.nestjs.com/graphql/quick-start) 應用中,攔截器針對每個字段解析器分別運行,因此,`CacheModule`(使用攔截器來緩存響應)將無法正常工作。
要啟用自動緩存響應,只需在想緩存數據的地方綁定`CacheInterceptor`。
```typescript
@Controller()
@UseInterceptors(CacheInterceptor)
export class AppController {
@Get()
findAll(): string[] {
return [];
}
}
```
> 警告: 只有使用 `GET` 方式聲明的節點會被緩存。此外,注入本機響應對象( `@Res()` )的 `HTTP` 服務器路由不能使用緩存攔截器。有關詳細信息,請參見[響應映射](https://docs.nestjs.com/interceptors#response-mapping)。
### 全局緩存
為了減少重復代碼量,可以將`CacheInterceptor`全局綁定到每個端點(endpoints):
```typescript
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()`方法。
```typescript
CacheModule.register({
ttl: 5, //秒
max: 10, //緩存中最大和最小數量
});
```
### 全局使用模塊[#](#use-module-globally)
當你想在其他模塊中使用 `CacheModule` 時,你需要導入它(這是任何 Nest 模塊的標準)。 或者,通過將選項對象的 `isGlobal` 屬性設置為` true` 來將其聲明為全局模塊,如下所示。 在這種情況下,一旦將 `CacheModule` 加載到根模塊(例如,`AppModule`)中,您就不需要在其他模塊中導入 `CacheModule`。
~~~typescript
CacheModule.register({
isGlobal: true,
});
~~~
### 全局緩存重載
使能全局緩存后,緩存入口存儲在基于路徑自動生成的`Cachekey`中。你可能需要基于每個方法重載特定的緩存設置(`@CacheKey()`和`@CacheTTL()`),允許為獨立控制器方法自定義緩存策略。這在使用[不同存儲緩存](https://docs.nestjs.com/techniques/caching#different-stores)時是最有意義的。
```typescript
@Controller()
export class AppController {
@CacheKey('custom_key')
@CacheTTL(20)
findAll(): string[] {
return [];
}
}
```
> `@CacheKey()`和`@CacheTTL()`裝飾器從`@nestjs/common`包導入。
`@CacheKey()`裝飾器可以有或者沒有一個對應的`@CacheTTL()`裝飾器,反之亦然。你可以選擇僅覆蓋`@CacheKey()`或`@CacheTTL()`。沒有用裝飾器覆蓋的設置將使用全局注冊的默認值(見[自定義緩存](https://docs.nestjs.com/techniques/caching#customize-caching))。
### WebSockets 和 微服務
顯然,您可以毫不費力地使用 `CacheInterceptor WebSocket` 訂閱者模式以及 `Microservice` 的模式(無論使用何種服務間的傳輸方法)。
> 譯者注: 微服務架構中服務之間的調用需要依賴某種通訊協議介質,在 `nest` 中不限制你是用消息隊列中間件,`RPC/gRPC` 協議或者對外公開 `API` 的 `HTTP` 協議。
```typescript
@CacheKey('events')
@UseInterceptors(CacheInterceptor)
@SubscribeMessage('events')
handleEvent(client: Client, data: string[]): Observable<string[]> {
return [];
}
```
然而,需要一個附加的`@CacheKey()`裝飾器來指定一個用于依次存儲并獲取緩存數據的鍵。注意,你不應該緩存所有的內容。永遠也不要去緩存那些用于實現業務邏輯也不是簡單地查詢數據的行為。
此外,你可以使用`@CacheTTL()`裝飾器來指定一個緩存過期時間(TTL),用于覆蓋全局默認的 TTL 值。
```typescript
@CacheTTL(10)
@UseInterceptors(CacheInterceptor)
@SubscribeMessage('events')
handleEvent(client: Client, data: string[]): Observable<string[]> {
return [];
}
```
> `@CacheTTL()`裝飾器可以使用或不使用相應的裝飾器`@CacheKey()`。
### 調整追蹤
默認地,`Nest`使用請求 URL(在一個`HTTP`app 中)或者緩存鍵(在`websockets`和`microservices`應用中,通過`@CacheKey()`裝飾器設置)來聯系緩存記錄和路徑。然而,有時你可能想要根據不同要素設置追蹤,例如`HTTP headers`(比如,確定合適`profile`路徑的`Authorization`)。
為了達到這個目的,創建一個`CacheInterceptor`的子類并覆蓋`trackBy()`方法。
```typescript
@Injectable()
class HttpCacheInterceptor extends CacheInterceptor {
trackBy(context: ExecutionContext): string | undefined {
return 'key';
}
}
```
### 不同的存儲
服務在底層使用[緩存管理器(cache-manager)](https://github.com/BryanDonovan/node-cache-manager)。`cache-manager`包支持一個寬范圍的可用存儲,例如,[Redis](https://github.com/dabroek/node-cache-manager-redis-store)存儲。一個完整的支持存儲列表見[這里](https://github.com/BryanDonovan/node-cache-manager#store-engines)。要設置`Redis`存儲,簡單地將該包和相應的選項傳遞給`register()`方法。
> 譯者注: 緩存方案庫目前可選的有 `redis, fs, mongodb, memcached` 等。
```typescript
import * as redisStore from 'cache-manager-redis-store';
import { CacheModule, Module } from '@nestjs/common';
import { AppController } from './app.controller';
@Module({
imports: [
CacheModule.register({
store: redisStore,
host: 'localhost',
port: 6379,
}),
],
controllers: [AppController],
})
export class ApplicationModule {}
```
### 異步配置
你可能想異步傳遞模塊選項來代替在編譯時靜態傳遞。在這種情況下,可以使用`registerAsync()`方法,它提供了不同的處理異步配置的方法。
一個方法是使用工廠函數:
```typescript
CacheModule.registerAsync({
useFactory: () => ({
ttl: 5,
}),
});
```
我們的工廠行為和其他異步模塊工廠一樣(它可以使用`inject`異步注入依賴)。
```typescript
CacheModule.registerAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
ttl: configService.getString('CACHE_TTL'),
}),
inject: [ConfigService],
});
```
此外,你也可以使用`useClass`方法:
```typescript
CacheModule.registerAsync({
useClass: CacheConfigService,
});
```
上述構造器將在`CacheModule`內部實例化`CacheConfigService`并用它來得到選項對象,`CacheConfigService`需要使用`CacheOptionsFactory`接口來提供配置選項:
```typescript
@Injectable()
class CacheConfigService implements CacheOptionsFactory {
createCacheOptions(): CacheModuleOptions {
return {
ttl: 5,
};
}
}
```
如果你希望使用在其他不同模塊中導入的現有的配置提供者,使用`useExisting`語法:
```typescript
CacheModule.registerAsync({
imports: [ConfigModule],
useExisting: ConfigService,
});
```
這和`useClass`工作模式相同,但有一個根本區別——`CacheModule`將查找導入的模塊來重用任何已經創建的`ConfigService`,以代替自己創實例化。
> `CacheModule#register` 和 `CacheModule#registerAsync` 和 `CacheOptionsFactory` 有一個可選的泛型(類型參數)來縮小特定于存儲的配置選項,使其類型安全。
### 示例[#](#example)
[此處](https://github.com/nestjs/nest/tree/master/sample/20-cache)提供了一個工作示例。
- 介紹
- 概述
- 第一步
- 控制器
- 提供者
- 模塊
- 中間件
- 異常過濾器
- 管道
- 守衛
- 攔截器
- 自定義裝飾器
- 基礎知識
- 自定義提供者
- 異步提供者
- 動態模塊
- 注入作用域
- 循環依賴
- 模塊參考
- 懶加載模塊
- 應用上下文
- 生命周期事件
- 跨平臺
- 測試
- 技術
- 數據庫
- Mongo
- 配置
- 驗證
- 緩存
- 序列化
- 版本控制
- 定時任務
- 隊列
- 日志
- Cookies
- 事件
- 壓縮
- 文件上傳
- 流式處理文件
- HTTP模塊
- Session(會話)
- MVC
- 性能(Fastify)
- 服務器端事件發送
- 安全
- 認證(Authentication)
- 授權(Authorization)
- 加密和散列
- Helmet
- CORS(跨域請求)
- CSRF保護
- 限速
- GraphQL
- 快速開始
- 解析器(resolvers)
- 變更(Mutations)
- 訂閱(Subscriptions)
- 標量(Scalars)
- 指令(directives)
- 接口(Interfaces)
- 聯合類型
- 枚舉(Enums)
- 字段中間件
- 映射類型
- 插件
- 復雜性
- 擴展
- CLI插件
- 生成SDL
- 其他功能
- 聯合服務
- 遷移指南
- Websocket
- 網關
- 異常過濾器
- 管道
- 守衛
- 攔截器
- 適配器
- 微服務
- 概述
- Redis
- MQTT
- NATS
- RabbitMQ
- Kafka
- gRPC
- 自定義傳輸器
- 異常過濾器
- 管道
- 守衛
- 攔截器
- 獨立應用
- Cli
- 概述
- 工作空間
- 庫
- 用法
- 腳本
- Openapi
- 介紹
- 類型和參數
- 操作
- 安全
- 映射類型
- 裝飾器
- CLI插件
- 其他特性
- 遷移指南
- 秘籍
- CRUD 生成器
- 熱重載
- MikroORM
- TypeORM
- Mongoose
- 序列化
- 路由模塊
- Swagger
- 健康檢查
- CQRS
- 文檔
- Prisma
- 靜態服務
- Nest Commander
- 問答
- Serverless
- HTTP 適配器
- 全局路由前綴
- 混合應用
- HTTPS 和多服務器
- 請求生命周期
- 常見錯誤
- 實例
- 遷移指南
- 發現
- 誰在使用Nest?