NestJS 注入作用域

2023-09-08 14:37 更新

來自不同語言背景的開發(fā)者,在學習Nest時可能預料不到在請求中幾乎所有內(nèi)容都是共享的。我們建立一個連接池到數(shù)據(jù)庫,在全局狀態(tài)下使用單例服務。 要記住Node.js并不遵循多線程下請求/響應的無狀態(tài)模式。因此,在我們的應用中使用單例是安全的。

然而,在需要考慮請求生命周期的情況下,存在邊緣情況.例如,在GraphQL應用的預請求緩存中,以及請求追蹤和多租戶條件下,注入作用域提供了一個機制來獲取需要的提供者生命周期行為.

提供者范圍

基本上,每個提供者都可以作為一個單例,被請求范圍限定,并切換到瞬態(tài)模式。請參見下表,以熟悉它們之間的區(qū)別。

DEFAULT每個提供者可以跨多個類共享。提供者生命周期嚴格綁定到應用程序生命周期。一旦應用程序啟動,所有提供程序都已實例化。默認情況下使用單例范圍。
REQUEST在請求處理完成后,將為每個傳入請求和垃圾收集專門創(chuàng)建提供者的新實例
TRANSIENT臨時提供者不能在提供者之間共享。每當其他提供者向 Nest 容器請求特定的臨時提供者時,該容器將創(chuàng)建一個新的專用實例

使用單例范圍始終是推薦的方法。請求之間共享提供者可以降低內(nèi)存消耗,從而提高應用程序的性能(不需要每次實例化類)。

使用 (Usage)

為了切換到另一個注入范圍,您必須向 @Injectable() 裝飾器傳遞一個選項對象。

import { Injectable, Scope } from '@nestjs/common';

@Injectable({ scope: Scope.REQUEST })
export class CatsService {}

在自定義提供者的情況下,您必須設置一個額外的范圍屬性。

{
  provide: 'CACHE_MANAGER',
  useClass: CacheManager,
  scope: Scope.TRANSIENT,
}

Scope從@nestjs/common中導入。

網(wǎng)關不應該使用請求范圍提供者,因為其必須作為單例提供。每個網(wǎng)關都封裝了一個socket并且不能多次實例化。

默認使用單例范圍,并且不需要聲明。如果你想聲明一個單例范圍的提供者,在scope屬性中使用Scope.DEFAULT值。

控制器范圍

當涉及到控制器時,傳遞 ControllerOptions 對象

@Controller({
  path: 'cats',
  scope: Scope.REQUEST,
})
export class CatsController {}

網(wǎng)關永遠不應該依賴于請求范圍的提供者,因為它們充當單例。一個網(wǎng)關封裝了一個真正的套接字,不能多次被實例化

所有請求注入

必須非常謹慎地使用請求范圍的提供者。請記住,scope 實際上是在注入鏈中冒泡的。如果您的控制器依賴于一個請求范圍的提供者,這意味著您的控制器實際上也是請求范圍。

想象一下下面的鏈: CatsController <- CatsService <- CatsRepository 。如果您的 CatsService 是請求范圍的(從理論上講,其余的都是單例),那么 CatsController 也將成為請求范圍的(因為必須將請求范圍的實例注入到新創(chuàng)建的控制器中),而 CatsRepository 仍然是單例的。

在這種情況下,循環(huán)依賴關系將導致非常痛苦的副作用,因此,您當然應該避免創(chuàng)建它們

請求提供者

在 HTTP 應用程序中(例如使用@nestjs/platform-express或@nestjs/platform-fastify),當使用請求范圍提供者時,可能需要獲取原始的請求對象。這通過注入REQUEST對象實現(xiàn):

import { Injectable, Scope, Inject } from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { Request } from 'express';

@Injectable({ scope: Scope.REQUEST })
export class CatsService {
  constructor(@Inject(REQUEST) private readonly request: Request) {}
}

由于底層平臺和協(xié)議不同,該功能與微服務和 GraphQL 應用程序略有不同。在 GraphQL 應用程序中,可以注入 CONTEXT來替代REQUEST。

import { Injectable, Scope, Inject } from '@nestjs/common';
import { CONTEXT } from '@nestjs/graphql';

@Injectable({ scope: Scope.REQUEST })
export class CatsService {
  constructor(@Inject(CONTEXT) private readonly context) {}
}

然后,您可以配置您的 context 值(在GraphQLModule中),以包含請求作為其屬性。

性能

使用請求范圍的提供者將明顯影響應用程序性能。即使 Nest 試圖緩存盡可能多的元數(shù)據(jù),它仍然必須為每個請求創(chuàng)建類的實例。因此,它將降低您的平均響應時間和總體基準測試結果。如果您的提供者不一定需要請求范圍,那么您應該堅持使用單例范圍。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號