node端导出excel-用请求排队来限流

需求

        有一个会执行luckySheet脚本并且导出excel的node接口,会在每天凌晨执行,但是文件过大时会内存溢出

        之前有用worker来实现多线程(主要是避免变量污染),但这样只能保证主线程不卡死,几个子线程合起来占用内存也很大,然后改用流的方式导出来优化占用内存过大的问题。

        但是exceljs插件用流的方式导出不支持导出图片,所以有图片就用流的方式导出,没图片还是用一开始的方式导出。为了继续优化这个状况,主管说:“反正是在晚上执行,我们搞个请求排队来限流,一个一个执行,只要白天用户能看到结果就行。”

        总结下来这个接口的特点就是:有一点并发量,并且单次请求处理占用资源大(cpu密集型),而且不用考虑时间成本

代码

        实现思路:

        在业务中间件之前使用一个中间件(就叫他“排队控制中间件”),用于控制什么时候执行业务处理中间件。

        我们需要在外部定义一个数组用于存放next(因为执行next就是去执行逻辑处理中间件),还有一个变量用于显示当前有无逻辑处理在进行。

        在“排队控制中间件”中,将next入队,并在没有业务中间件在执行的情况下尝试执行队列的第一个next;如果一个业务中间件执行完成,也会尝试去执行队列的第一个next。

代码如下:

globalThis.runQueue = []; //存放next的数组
globalThis.isProcessing = false; //用于显示当前是否有业务在处理

function processQueue() {
  if (globalThis.runQueue.length > 0 && !globalThis.isProcessing) {
    globalThis.isProcessing = true;
    const { req, res, next } = globalThis.runQueue.shift();
    next(); // 执行下一个中间件
  }
}

app.post('/gongqi/code', function(req, res, next){
  const isNeedLieUp= req.body?.params?.isNeedLieUp;
  if (isNeedLieUp) {//接口的参数来控制这个接口是否需要进入排队机制
    globalThis.runQueue.push({req, res, next}); // 将请求加入队列
    processQueue(); // 尝试处理队列
  } else {
    next(); // 不需要排队,直接执行下一个中间件
  }
}, async function (req, res, next) {
  // 这里是您的请求处理逻辑
  // 处理完成后,需要调用某个函数来触发队列的下一个处理,例如在响应发送后
  // 假设这里是异步操作,操作完成后应该调用processQueue()来检查并处理队列中的下一个请求
  // 为了示例,我们使用setTimeout来模拟异步操作
  setTimeout(() => {
    // 假设这里是处理完成后的逻辑
    res.send({ message: '处理完成' });
    globalThis.isProcessing = false;
    processQueue(); // 处理队列中的下一个请求
  }, 1000);
});

效果

        我用for循环模拟了同时10个请求(导出一个比较小的xlsx,在项目中的比较大,有些需要导出一年的数据),用pm2 monit看一下,有无请求排队的内存占用情况。

       1、 使用请求排队后内存情况如下:请求依次执行,每次请求内存占用峰值能达到5-6百多兆。

        2、不使用请求排队的情况如下:请求会“交替处理”,占用的内存能达到2G。

        如果并发量更大或者xlsx更复杂,对比更强烈,所以还是有点用的,哈哈哈。

思考

        js单线程,node在处理请求时也不会创建新的线程,我竟然认为在node中的请求默认是排队的。

        原来node可以异步执行i/o调用,事件循环又能使node在等待io时继续执行其他任务,而不是阻塞线程,所以就有了说node适合高并发和io密集型的任务。

        但在本需求中,导出excel属于cpu密集型的操作,还可以改进的是在导出excel时再使用worker创建多线程来处理任务(不止接口的多线程,而是在处理导出excel时再细分线程)。

其他概念

        接下来介绍下,写这个需求时遇到的一些知识,了解了解。

限流

        上面的请求排队应该也算限流吧,只是可能不入流,能用的前提条件比较多,哈哈。如果有用nginx,可以用设置请求速率和并发连接数来限流。在node中也有一些第三方限流库,比如“`express-rate-limit“。

        以下内容来自ai: 后端限流

后端限流是一种重要的系统保护机制,用于控制访问后端服务的速率,以防止过载,确保系统的稳定性和可用性。限流可以在不同的层次上实现,包括应用层、中间件层和网络层。以下是一些常见的后端限流策略和技术:

1. 固定窗口限流

固定窗口限流算法将时间分割成固定大小的窗口,每个窗口内允许的请求量有上限。当请求量达到上限时,新的请求会被拒绝,直到下一个时间窗口开始。这种方法实现简单,但可能会在窗口切换时出现请求量突增的情况。

2. 滑动窗口日志

滑动窗口算法是固定窗口的一种改进,它通过记录每个请求的时间戳,动态计算最近一段时间内的请求总量。这种方法可以更平滑地控制请求速率,但实现相对复杂,需要维护一个时间窗口内的所有请求记录。

3. 令牌桶算法

令牌桶算法使用一个令牌桶来控制请求的速率,系统以恒定速率向桶中添加令牌,处理请求时需要从桶中取出令牌。如果桶中没有足够的令牌,请求则被限流。这种方法可以允许一定程度的突发流量,同时保持长期的速率控制。

4. 漏桶算法

漏桶算法将请求放入一个固定容量的桶中,系统以恒定的速率从桶中取出请求进行处理。如果桶满了,则新的请求会被拒绝或排队。漏桶算法可以平滑地处理突发流量,但不允许突发流量的短期增加。

5. 基于队列的限流

基于队列的限流通过将请求排队,并控制处理队列中请求的速率来实现限流。这种方法可以用于实现更复杂的限流策略,如基于优先级的限流,但需要合理设计队列长度和处理策略,以避免过长的等待时间。

实现工具和库

Nginx:通过配置 Nginx 的 `limit_req` 模块和 `limit_conn` 模块,可以实现简单的请求速率和并发连接数限制。
HAProxy:支持基于各种条件进行复杂的流量控制和限流。
Guava RateLimiter:Google 的 Guava 库中包含了 RateLimiter 组件,提供了基于令牌桶算法的限流实现,适用于 Java 应用。
Node.js Rate Limiter:对于 Node.js 应用,有多个限流库可用,如 `express-rate-limit`,提供了基本的限流功能。

限流策略的选择和实现应根据具体的应用场景、性能要求和系统架构来决定。正确的限流策略可以有效地保护系统免受过载影响,确保服务的稳定性和响应性。

worker threads和child processes区别

        worker是在一个进程下开启多线程,每个线程有自己的执行栈和局部变量。可以共享一个进程的资源,但是子线程不能直接访问主线程变量,需要通过消息通信等方法获取。创建和销毁的成本较低。

        child processes则创建子进程,每个子进程有自己独立的内存空间和v8实例,父子进程也能通信,创建和销毁成本较大。

以下内容来自ai:  node的worker和child_process有什么区别

Node.js 中的 Worker Threads(工作线程)和 Child Processes(子进程)都允许在 Node.js 应用程序中执行并行操作,但它们在底层实现、使用场景和通信方式上有所不同。

### Worker Threads(工作线程)

- **实现方式**:Worker Threads 提供了一种在同一个进程内运行多个线程的能力。这意味着所有线程共享同一个进程的资源,但是每个线程有自己的执行栈和局部变量。
- **使用场景**:适用于需要并行执行计算密集型任务而不离开当前进程的场景。由于线程间可以共享一部分内存,这对于需要共享大量数据的任务来说非常高效。
- **通信方式**:线程之间可以通过 `MessageChannel` 和 `MessagePort` 进行通信,也可以通过共享内存(如 SharedArrayBuffer)直接交换数据。
- **优点**:创建和销毁成本相对较低,内存共享可以简化某些并行操作的数据交换。
- **缺点**:多线程编程可能会引入复杂性,如竞态条件和死锁等问题。

### Child Processes(子进程)

- **实现方式**:Child Processes 允许 Node.js 应用创建新的进程,每个子进程都有自己的 V8 实例和独立的内存空间。子进程可以执行 Node.js 脚本或任何在系统上可执行的文件。
- **使用场景**:适用于需要执行独立任务的场景,特别是那些可能需要执行外部应用或脚本的任务。子进程可以完全独立于父进程运行,不会共享内存或执行环境。
- **通信方式**:父进程和子进程之间主要通过 IPC(进程间通信)通道进行通信,可以发送消息和共享数据。也可以通过标准输入输出流(stdin、stdout、stderr)进行通信。
- **优点**:进程间完全隔离,可以利用多核 CPU 的优势,适合执行需要隔离或需要大量资源的任务。
- **缺点**:创建和销毁子进程的成本相对较高,进程间通信比线程间通信更复杂和开销更大。

### 总结

- **Worker Threads** 更适合执行计算密集型任务,特别是当这些任务需要访问共享数据时。
- **Child Processes** 更适合执行独立的、资源密集型任务,或需要执行系统命令和外部程序的任务。

选择使用 Worker Threads 还是 Child Processes 取决于具体的应用场景、性能要求和资源管理策略。

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MDC搭配ttl使用!!!

一、简介 MDC 介绍​ MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能。MDC 可以看成是一个与当前线程绑定的Map,可以往其中添加键值对。MDC 中包含的内容可以被…

使用yolov8 进行实例分割训练

1、基于windows 的ISAM标注 直接下载安装包,解压后即可使用 链接:https://pan.baidu.com/s/1u_6jk-7sj4CUK1DC0fDEXQ 提取码:c780 2、标注结果转yolo格式 通过ISAM标注后的json文件路径 原始json格式如下: ISAM.json 转 yolo.…

牛客2024 【牛客赛文X】春招冲刺 ONT34 加油站【中等 贪心 C++、Java、Go、PHP】

题目 题目链接: https://www.nowcoder.com/practice/a013a0691a0343aeb262ca1450d2fe4e 思路 贪心: 如果总的gas小于走完全程的cost,直接返回-1不需要再找了 如果确保了可以走完一圈之后,那么从index 0开始找, 当g…

【cygwin】工具安装apt-cyg

目录 下载安装查看是否安装成功安装软件 下载 git clone https://github.com/transcode-open/apt-cyg.git安装 cd apt-cyg mv apt-cyg /usr/local/bin/ 查看是否安装成功 apt-cyg --help安装软件 apt-cyg install nano

视频号小店怎么做?新手开店必备运营攻略,看这一篇就够了

大家好,我是电商笨笨熊 作为腾讯推出的电商项目,视频号小店在推出到现在一直都备受关注,同时也吸引了不少玩家入驻; 毕竟作为一个新平台、新市场,一个适合跑马圈地的红利平台,谁都想在这里分的一杯羹。 …

Linux debian gdb dump

1.开发背景 记录 debian 下应用程序崩溃调试方法 2.开发需求 程序越界可以定位到越界的位置附近 3.开发环境 debian 操作系统,如果不支持需要查看是否存在对应的可执行文件 4.实现步骤 4.1 设置 dump 输出大小 ulimit -c unlimited # 设置输出大小 生成core 文…

一个文生视频MoneyPrinterTurbo项目解析

最近抖音剪映发布了图文生成视频功能,同时百家号也有这个功能,这个可以看做是一个开源的实现,一起看看它的原理吧~ 一句话提示词 大模型生成文案 百家号生成视频效果 MoneyPrinterTurbo生成视频效果 天空为什么是蓝色的? 天空…

上位机图像处理和嵌入式模块部署(智能硬件的介绍)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 目前,用上位机软件虽然可以部署项目,但是它本身有自己的缺点,那就是稳定性差、价格贵。稳定性这部分&#xff0…

深度剖析扫雷游戏的各个知识点(2)

小伙伴们,大家好。这次继续上次的剖析扫雷游戏的知识点。 那么本次咱们主要是讲扫雷中的宏定义,也就是#define这些 首先#define是用来定义一个宏,后面就是类似于和变量一样的常量名,以及最后的数字就是它的值。 定义规则 #def…

数据结构——树和二叉树

目录 前言 一、树概念及结构 1.1树的概念 1.2 树的相关概念 ​编辑 1.3 树的表示 1.4 树的应用 2.二叉树概念及结构 2.1 二叉树概念 2.2 现实中的二叉树 2.3 特殊的二叉树 2.4 二叉树的性质 2.5 二叉树的存储结构 总结 前言 之前我们学习到的数据结构都是线性的…

Linux Makefile

1.开发背景 linux 下编译程序需要用到对应的 Makefile,用于编译应用程序。 2.开发需求 编写 Makefile 编译应用程序 1)支持多个源文件 2)支持多个头文件 3)支持只编译修改的文件,包括源文件和头文件 4)支持…

【Android Studio报错】:* What went wrong:Out of memory. Java heap space

项目场景: 今天,刚打开自己的安卓项目发现报错: 报错: * What went wrong: Out of memory. Java heap space Possible solution: - Check the JVM memory arguments defined for the gradle process in: gradle.properties in…

STM32G030F6P6TR ST意法

STM32G030F6P6TR是ST(意法半导体)一款基于高性能ArmCortex-M032位RISC内核,工作频率高达64MHz的32位MCU微控制器。代理销售ST(意法半导体)全系列IC电子元器件-中芯巨能为您提供STM32G030F6P6TR(ST 32位MCU)引脚图及中文参数介绍等内容。 STM32G030F6P6TR的中文参数 …

Python多态

1.多态 多态定义:多态(polymorphism)是指同一个方法调用由于对象不同可能会产生不同的行为 注意以下2点: 1.多态是方法的多态,属性没有多态。 2.多态的存在有2个必要条件:继承、方法重写 class Animal:de…

RabbitMQ入门实战

文章目录 RabbitMQ入门实战基本概念安装快速入门单向发送多消费者 RabbitMQ入门实战 官方:https://www.rabbitmq.com 基本概念 AMQP协议:https://www.rabbitmq.com/tutorials/amqp-concepts.html 定义:高级信息队列协议(Advanc…

ORA-600 ktsiseginfo1故障---惜分飞

oracle 9i的库在运行途中突然报ORA-600 kcbnew_3错误 Sun Mar 31 14:25:11 2024 Undo Segment 69 Onlined Sun Mar 31 14:25:11 2024 Created Undo Segment _SYSSMU69$ Sun Mar 31 14:25:11 2024 Created Undo Segment _SYSSMU70$ Undo Segment 70 Onlined Sun Mar 31 14:28:41…

开启Three.js之旅(会持续完善)

文章目录 Three.js必备构建项目场景Scene相机CameraPerspectiveCamera 渲染器WebGLRendererCSS3DRenderer 灯光LightAmbientLightDirectionalLight 平行光PointLight 加载器CacheFileLoaderLoaderGLTFLoaderRGBELoaderTextureLoader 材质MetarialMeshBasicMaterialMeshLambertM…

【C++程序员的自我修炼】拷贝构造函数

心存希冀 追光而遇目有繁星 沐光而行 目录 拷贝构造函数概念 拷贝构造的特征 无穷递归的解释 浅拷贝 总结: 深拷贝 拷贝构造函数典型调用场景 总结 契子✨ 在生活中总有很多琐事,不做不行做了又怕麻烦,有时候想要是有个和自己一模一样的人就…

机器学习和深度学习-- 李宏毅(笔记于个人理解)Day 21

Day 21 Self- Attention 选修部分 ​ 学完自适应 再回来看看 Sequence Labling 假如我们现在有一个需要读完全部句子才能解的问题, 那么red window 就需要变得是最大的(最长的句子); 其实这里大家有没有想过,这个玩意…

【机器学习】数据变换---小波变换特征提取及应用案列介绍

引言 在机器学习领域,数据变换是一种常见且重要的预处理步骤。通过对原始数据进行变换,我们可以提取出更有意义的特征,提高模型的性能。在众多数据变换方法中,小波变换是一种非常有效的方法,尤其适用于处理非平稳信号和…