深入理解Nest的REQUEST范围和TRANSIENT范围

news2024/11/26 12:24:54

深入理解Nest的REQUEST范围和TRANSIENT范围

  • 单例模式
  • REQUEST范围
    • 控制器的REQUEST范围
    • REQUEST范围的冒泡特性
    • 场景
  • TRANSIENT范围
    • 例外
    • 场景
  • 总结

单例模式

单例模式是指在整个程序执行期间,程序内的类都会实例化,且与应用程序生命周期直接相关,例如有个服务类名为CatsService,在程序开启之时就会被创建实例(以依赖注入形式为例),自创建至程序结束,有且仅有一个CatsService 的实例

假设有如下代码:

// cats.modules.ts
@Controller('cats')
export class CatsController {
  constructor(private catsService: CatsService) {}

  @Post()
  async create(@Body() createCatDto: CreateCatDto) {
    this.catsService.create(createCatDto);
  }

  @Get()
  async findAll(): Promise<Cat[]> {
    return this.catsService.findAll();
  }
}

当程序启动时,由于依赖注入Nest会自动解析和实例化CatsService,也就是代码中的this.catsServicethis为控制器本身)。实例对象在内存中具有唯一地址,该地址用于标识当前的实例对象。而单例模式的意义,就在于不管请求多少次,所发生的行为都是在当前的实例对象身上的。与之相对应的,是非单例模式的每次请求都会在内存中新建一个实例对象,所造成的问题之一就是对内存造成了负担

所以如果发生了Post请求,创建了一只猫,那么通过Get请求,可以获取猫的数组

注意:这里忽略了Catservice代码实现

Nest中,默认为单例模式,且官方推荐使用单例模式

而如果是非单例模式会怎么样呢?即标题所提到的REQUEST范围和TRANSIENT范围

三种不同的范围

REQUEST范围

REQUEST范围是指在每次请求时创建新的实例,并且在请求处理完成后,对实例进行垃圾回收,在请求期间实例的状态是共享的。REQUEST范围的另外一个关键的点是请求的生命周期,在一个请求的生命周期中,实例的状态是共享的

实现的方式有两种,第一是在@Injectable()添加{ scope: Scope.REQUEST },代码如下:

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

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

第二种是在@Module中标记,代码如下:

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

一个请求的生命周期非常简单,就是从客户端发送请求,接着服务器接受请求并处理和返回结果

如何理解状态共享?假设存在控制器,同时对一个服务类 注入了两次,那么在一个请求上下文中,这两个服务类创建出来的实例是同一个,也就是说在请求期间实例的状态是共享的,代码如下:

// app.service.ts
import { Injectable,Scope } from '@nestjs/common';

@Injectable({ scope: Scope.REQUEST }) // REQUEST范围
export class RequestService {
  private instanceId = Math.random().toString(36).substring(7); // 生成一个唯一ID

  getInstanceId() {
    return this.instanceId;
  }
}
// abb.service.ts 另外一个服务
import { Injectable } from '@nestjs/common';
import { RequestService } from './app.service'; // 引用了app.servcie.ts里的服务

@Injectable()
export class AdditionalService {
  constructor(private readonly requestService: RequestService) {} // app.servcie.ts里的服务

  getAdditionalId(): string {
    return this.requestService.getInstanceId();
  }
}
// app.controller.ts
import { Controller, Get,Request } from '@nestjs/common';
import { RequestService } from './app.service';
import {AdditionalService} from './add.service'

@Controller('id')
export class IdController {
  constructor(
    private readonly requestService: RequestService,
    private readonly additionalService: AdditionalService,
  ) {}

  @Get()
  getUniqueIds(@Request() req): any {
    const idFromController = this.requestService.getInstanceId();
    const idFromAdditionalService = this.additionalService.getAdditionalId();

    return {
      idFromController,
      idFromAdditionalService,
    };
  }
}

输出情况如下:
输出案例
也就说明this.requestServicethis.additionalService都是指向同一个实例,状态都是共享的(值都指向同一个地址)

另一个例子说明每次请求的实例不一样,代码如下:

@Controller('request')
export class RequestController {
  constructor(private readonly requestService1: RequestService) {}

  @Get()
  getExample() {
    return { // 每次返回的Id将是不同的
      instanceId1: this.requestService1.getInstanceId(), // 获取实例Id
    };
  }
}

两次请求结果如下:
第一次请求
第二次请求
由此可见,两次请求都是创建了新的实例

控制器的REQUEST范围

可以在控制器中添加REQUEST范围,代码如下:

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

假设在CatsController中包含CatsServiceDogsService,那么都会添加上REQUEST范围

REQUEST范围的冒泡特性

假设存在CatsController <- CatsService <- CatsRepository依赖结构的代码:

// cats.repository.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class CatsRepository {
  private cats: string[] = [];

  addCat(name: string) {
    this.cats.push(name);
  }

  getCats() {
    return this.cats;
  }
}

// cats.service.ts
import { Injectable, Scope } from '@nestjs/common';
import { CatsRepository } from './cats.repository';

@Injectable({ scope: Scope.REQUEST }) // 设置了范围
export class CatsService {
  constructor(private readonly catsRepository: CatsRepository) {} // 注入了CatsRepository

  addCat(name: string) {
    this.catsRepository.addCat(name);
  }

  getCats() {
    return this.catsRepository.getCats();
  }
}

// cats.controller.ts
import { Controller, Get, Req, Res } from '@nestjs/common';
import { Request, Response } from 'express';
import { CatsService } from './cats.service';

@Controller('cats')
export class CatsController {
  constructor(private readonly catsService: CatsService) {} // 注入了CatsService

  @Get()
  async getCats(@Req() req: Request, @Res() res: Response) {
    this.catsService.addCat(`Cat-${Math.random().toString(36).substr(2, 5)}`);
    const cats = this.catsService.getCats();
    res.json({ cats });
  }
}

在代码中,CatsService添加了REQUEST范围,由于CatsController 注入了CatsService,所以CatsController 也变为REQUEST范围,而CatsRepository不受影响

TRANSIENT不遵循此规则

场景

待完善…

TRANSIENT范围

TRANSIENT范围与REQUEST范围的区别在于,其在每次注入的时候创建新实例,而REQUEST是在请求时创建新实例(在同一个HTTP请求中,无论服务被注入多少次,都将使用相同的实例)

还是以上面关于获取实例Id的代码为例,仅仅修改范围参数,按其特性,两次注入所创建的实例不属于同一个,代码如下:

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

@Injectable({ scope: Scope.TRANSIENT })
export class RequestService {
  private instanceId = Math.random().toString(36).substring(7);

  getInstanceId() {
    return this.instanceId;
  }
}

输出的结果如下:
输出案例
可看到返回的值已经不一样了

例外

在同一个请求上下文中,构造函数中多次注入同一服务,不管任何范围,状态都是共享的,代码如下:

import { Controller, Get } from '@nestjs/common';
import { RequestService } from './app.service';

@Controller('request')
export class RequestController {
  constructor(
    private readonly requestService1: RequestService, 
    private readonly requestService2: RequestService) {} // 注入两次

  @Get()
  getExample() {
    return { // 返回的Id将会是同一个
      instanceId1: this.requestService1.getInstanceId(),
      instanceId2: this.requestService2.getInstanceId(),
    };
  }
}

场景

待完善…

总结

关键在于理解注入创建实例同一请求上下文请求生命周期的概念

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2219738.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

javax.el.PropertyNotFoundException: Property ‘XXX‘ not found on type XXX(类的路径)

捣鼓了半小时的bug 在网上找了好多方案,都没有解决 其中一个佬的解决方案:异常&#xff1a;javax.el.PropertyNotFoundException: Property xxx not found on type java.lang.String-CSDN博客 但是还是没有解决我的问题 最终解决方法,在jsp文件头部导入了类包(第三行我导入…

【Nginx系列】Nginx配置超时时间

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

MySQL日期类型选择建议

我们平时开发中不可避免的就是要存储时间&#xff0c;比如我们要记录操作表中这条记录的时间、记录转账的交易时间、记录出发时间、用户下单时间等等。你会发现时间这个东西与我们开发的联系还是非常紧密的&#xff0c;用的好与不好会给我们的业务甚至功能带来很大的影响。所以…

深入拆解TomcatJetty(二)

深入拆解Tomcat&Jetty&#xff08;二&#xff09; 专栏地址&#xff1a;https://time.geekbang.org/column/intro/100027701 1、Tomcat支持的IO模型和应用层协议 IO模型&#xff1a; NIO&#xff1a;非阻塞 I/O&#xff0c;采用 Java NIO 类库实现。NIO2&#xff1a;异…

Cyber RT 之 Timer Component 实践(apollo 9.0)

实验内容 Component 是 Cyber RT 提供的用来构建功能模块的基础类&#xff0c;Component 有两种类型&#xff0c;分别为 Component 和 TimerComponent。 相较于 Component&#xff0c;TimerComponent 不提供消息融合&#xff0c;也不由消息触发运行&#xff0c;而是由系统定时…

UE5 gameplay学习 蓝图0 level blueprint

首先在左上角这个位置可以创建一个这个蓝图 我理解这个蓝图适合做全局事件规划啥的 在场景选中一个物体&#xff0c;右侧面板拿到他&#xff0c;直接拖入蓝图&#xff0c;就能操作他了 这里获取到了这个物体&#xff0c;在gamebegin的时候把Z加了500 执行播放的时候能看见他从…

Windows API 一 ----起步

目录 1.介绍主函数入口参数。 2. 简单介绍 Windows.h 这个头文件 小结&#xff0c;也聊一聊 1.介绍主函数入口参数。 第一个参数: HINSTANCE 类型的 参数&#xff0c; 称为“实例句柄“&#xff0c;这个参数唯一标志了我们写的这个程序。 第二个参数&#xff1a; HINSTANCE…

poisson过程——随机模拟(Python和R实现)

Python实现 exponential()使用&#xff0c;自动poisson过程实现。 import numpy as np import matplotlib.pyplot as plt# Parameters lambda_rate 5 # rate parameter (events per time unit) T 10 # total time# Generate Poisson process times np.random.exponential(…

k8s系列-Rancher 上操作的k8s容器网络配置总结

Rancher 上操作的k8s容器网络配置总结 要在 Rancher 中配置Spring Boot 应用 ykhd-zhjgyw-xpwfxfjfl 服务&#xff0c;正确的配置方式如下&#xff1a; 1. 应用程序监听端口 在 application.yaml 文件中&#xff0c;配置的应用监听端口是 10001&#xff0c;并且应用的上下文…

Mycat 详细介绍及入门实战,解决数据库性能问题

一、基本原理 1、数据分片 &#xff08;1&#xff09;、水平分片 Mycat 将一个大表的数据按照一定的规则拆分成多个小表&#xff0c;分布在不同的数据库节点上。例如&#xff0c;可以根据某个字段的值进行哈希取模&#xff0c;将数据均匀的分布到不同的节点上。 这样做的好处…

美摄科技云服务解决方案,方案成熟,接入简单

美摄科技作为视频处理领域的先锋&#xff0c;凭借其强大的技术实力和深厚的行业经验&#xff0c;推出了成熟的云服务解决方案&#xff0c;为轻量化视频制作开辟了全新的道路。 一、成熟方案&#xff0c;接入无忧 美摄科技云服务解决方案的最大亮点在于其成熟度和易用性。我们…

RabbitMQ 入门(四)SpringAMQP五种消息类型(Work Queue)

一、WorkQueue(工作消息队列) Work queues&#xff0c;也被称为&#xff08;Task queues&#xff09;&#xff0c;任务模型。简单来说就是让多个消费者绑定到一个队列&#xff0c;共同消费队列中的消息。 当消息处理比较耗时的时候&#xff0c;可能生产消息的速度会远远大于…

react里实现左右拉伸实战

封装组件&#xff1a; 新建一个resizeBox.tsx文件写上代码如下&#xff1a; import React, { ReactNode, useState, useEffect, useRef } from react; import styles from "./resizeBox.less"; interface ResizableBoxProps {/*** 盒子的宽度*/widthNum?: number;…

比较相同机器上 redis和mysql分别单独承载的 最大连接数量

在相同的机器上&#xff0c;Redis 和 MySQL 的最大连接数量会受到硬件配置&#xff08;如 CPU、内存、网络等&#xff09;、配置参数和应用场景的影响。以下是对 Redis 和 MySQL 在单机环境下最大连接数的比较&#xff1a; Redis 最大连接数量 默认配置&#xff1a; Redis 默…

【2024最新版】网络安全学习路线-适合入门小白

首先说明&#xff0c;我是一名CTF的web手&#xff0c;这是我自己亲身学习网络安全的路线&#xff0c;希望能够帮到大家&#xff0c;我虽然不是大牛&#xff0c;但我也希望能够帮助一些网安小白找到自己学习的方向&#xff0c;后面有就业的详细安全技术要求&#xff0c;如果真想…

yolov8实例分隔

1.查看显卡型号 2.在https://en.wikipedia.org/wiki/CUDA上查看显卡算力&#xff0c;这里显卡为1650&#xff0c;算力为7.5 3.查看显卡算力对应的cuda版本 4slurm上该怎么办&#xff1f; 查看slurm上计算节点cuda版本 查看cuda版本 srun -A 2022099 -J job1 -p Gnode --…

【Echarts 实战指南】解锁动态历史曲线之谜

在工作中&#xff0c;大家是否曾遇到过这样一种需求呢&#xff1f;需获取设备最近 10 分钟的历史数据。设备实时数据每 2 秒推送一次&#xff0c;且要把历史数据曲线变成动态变化的状态。倘若设备最近 10 分钟的历史数据为 20 个点&#xff0c;那么现在每 2 秒就要将最前面的点…

Java爬虫:获取直播带货数据的实战指南

在当今数字化时代&#xff0c;直播带货已成为电商领域的新热点&#xff0c;通过直播平台展示商品并进行销售&#xff0c;有效促进了产品的曝光和销售量的提升。然而&#xff0c;如何在直播带货过程中进行数据分析和评估效果&#xff0c;成为了摆在商家面前的一个重要问题。本文…

工业相机有哪些应用场景

工业相机具有高性能、高稳定性和高可靠性的特点&#xff0c;因此在众多工业领域都有广泛的应用场景。以下是朗观视觉小编总结的一些典型的应用场景&#xff1a; 机器视觉与自动化&#xff1a; 工业相机在机器视觉系统中起着核心作用&#xff0c;用于捕捉和分析物体的图像&#…

【Linux】从多线程同步到生产者消费者模型:多线程编程实践

目录 1.线程的同步 1.1.为什么需要线程的同步&#xff1f; 2.2.条件变量的接口函数 2.生产消费模型 2.1 什么是生产消费模型 2.2.生产者消费者模型优点 2.3.为何要使用生产者消费者模型 3.基于BlockingQueue的生产者消费者模型 3.1为什么要将if判断变成while&#xff…