來自不同語言背景的開發(fā)者,在學(xué)習(xí)Nest時可能預(yù)料不到在請求中幾乎所有內(nèi)容都是共享的。我們建立一個連接池到數(shù)據(jù)庫,在全局狀態(tài)下使用單例服務(wù)。 要記住Node.js并不遵循多線程下請求/響應(yīng)的無狀態(tài)模式。因此,在我們的應(yīng)用中使用單例是安全的。
然而,在需要考慮請求生命周期的情況下,存在邊緣情況.例如,在GraphQL應(yīng)用的預(yù)請求緩存中,以及請求追蹤和多租戶條件下,注入作用域提供了一個機(jī)制來獲取需要的提供者生命周期行為.
基本上,每個提供者都可以作為一個單例,被請求范圍限定,并切換到瞬態(tài)模式。請參見下表,以熟悉它們之間的區(qū)別。
DEFAULT | 每個提供者可以跨多個類共享。提供者生命周期嚴(yán)格綁定到應(yīng)用程序生命周期。一旦應(yīng)用程序啟動,所有提供程序都已實(shí)例化。默認(rèn)情況下使用單例范圍。 |
REQUEST | 在請求處理完成后,將為每個傳入請求和垃圾收集專門創(chuàng)建提供者的新實(shí)例 |
TRANSIENT | 臨時提供者不能在提供者之間共享。每當(dāng)其他提供者向 Nest 容器請求特定的臨時提供者時,該容器將創(chuàng)建一個新的專用實(shí)例 |
使用單例范圍始終是推薦的方法。請求之間共享提供者可以降低內(nèi)存消耗,從而提高應(yīng)用程序的性能(不需要每次實(shí)例化類)。
為了切換到另一個注入范圍,您必須向 @Injectable() 裝飾器傳遞一個選項對象。
import { Injectable, Scope } from '@nestjs/common';
@Injectable({ scope: Scope.REQUEST })
export class CatsService {}
在自定義提供者的情況下,您必須設(shè)置一個額外的范圍屬性。
{
provide: 'CACHE_MANAGER',
useClass: CacheManager,
scope: Scope.TRANSIENT,
}
Scope從@nestjs/common中導(dǎo)入。
網(wǎng)關(guān)不應(yīng)該使用請求范圍提供者,因為其必須作為單例提供。每個網(wǎng)關(guān)都封裝了一個socket并且不能多次實(shí)例化。
默認(rèn)使用單例范圍,并且不需要聲明。如果你想聲明一個單例范圍的提供者,在scope屬性中使用Scope.DEFAULT值。
當(dāng)涉及到控制器時,傳遞 ControllerOptions 對象
@Controller({
path: 'cats',
scope: Scope.REQUEST,
})
export class CatsController {}
網(wǎng)關(guān)永遠(yuǎn)不應(yīng)該依賴于請求范圍的提供者,因為它們充當(dāng)單例。一個網(wǎng)關(guān)封裝了一個真正的套接字,不能多次被實(shí)例化
必須非常謹(jǐn)慎地使用請求范圍的提供者。請記住,scope 實(shí)際上是在注入鏈中冒泡的。如果您的控制器依賴于一個請求范圍的提供者,這意味著您的控制器實(shí)際上也是請求范圍。
想象一下下面的鏈: CatsController <- CatsService <- CatsRepository 。如果您的 CatsService 是請求范圍的(從理論上講,其余的都是單例),那么 CatsController 也將成為請求范圍的(因為必須將請求范圍的實(shí)例注入到新創(chuàng)建的控制器中),而 CatsRepository 仍然是單例的。
在這種情況下,循環(huán)依賴關(guān)系將導(dǎo)致非常痛苦的副作用,因此,您當(dāng)然應(yīng)該避免創(chuàng)建它們
在 HTTP 應(yīng)用程序中(例如使用@nestjs/platform-express或@nestjs/platform-fastify),當(dāng)使用請求范圍提供者時,可能需要獲取原始的請求對象。這通過注入REQUEST對象實(shí)現(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é)議不同,該功能與微服務(wù)和 GraphQL 應(yīng)用程序略有不同。在 GraphQL 應(yīng)用程序中,可以注入 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中),以包含請求作為其屬性。
使用請求范圍的提供者將明顯影響應(yīng)用程序性能。即使 Nest 試圖緩存盡可能多的元數(shù)據(jù),它仍然必須為每個請求創(chuàng)建類的實(shí)例。因此,它將降低您的平均響應(yīng)時間和總體基準(zhǔn)測試結(jié)果。如果您的提供者不一定需要請求范圍,那么您應(yīng)該堅持使用單例范圍。
更多建議: