M2LOrder服务端性能优化:Node.js高并发网关开发实践

news2026/3/20 5:46:08
M2LOrder服务端性能优化Node.js高并发网关开发实践最近在做一个情感分析服务我们内部叫它M2LOrder用户量上来之后原来的服务直接暴露给客户端动不动就扛不住了。响应慢、超时甚至偶尔直接挂掉用户体验直线下降。痛定思痛我们决定在服务前面加一层API网关专门用来扛流量、做缓存、管队列。今天这篇文章我就来聊聊怎么用Node.js从零开始搭建一个能应对高并发的API网关。整个过程不复杂但每一步都挺关键。我会手把手带你走一遍从环境搭建到核心功能实现最后还会分享一些我们趟过的坑和优化心得。如果你也在为服务性能发愁或者想学习Node.js在高并发场景下的实战这篇内容应该能帮到你。1. 环境准备与项目初始化工欲善其事必先利其器。我们先来把开发环境准备好并创建一个干净的Node.js项目。1.1 Node.js安装与环境配置首先确保你的机器上安装了Node.js。我推荐使用长期支持版LTS稳定性更好。你可以去Node.js官网下载安装包或者用nvmNode Version Manager这样的版本管理工具方便切换不同版本。安装完成后打开终端用下面这个命令检查一下是否安装成功node --version npm --version如果能看到版本号比如v18.x.x和9.x.x说明安装没问题。接下来我们创建一个新的项目目录并初始化。找个你喜欢的位置执行mkdir m2lorder-gateway cd m2lorder-gateway npm init -y这行命令会创建一个叫m2lorder-gateway的文件夹并生成一个默认的package.json文件里面记录了项目的基本信息和依赖。1.2 安装核心依赖包我们这个网关主要需要几个核心的包。我们来一个个安装npm install express axios npm install bull ioredis npm install dotenv npm install --save-dev nodemon简单解释一下每个包是干嘛的express: 一个非常流行的Node.js Web框架用来快速搭建我们的HTTP服务器和路由。axios: 一个基于Promise的HTTP客户端我们用它来向后端的M2LOrder情感分析服务发起请求。bull和ioredis: Bull是一个强大的Node.js队列库基于Redis。我们用它来管理高并发下的请求队列。ioredis是它的Redis客户端。dotenv: 用来管理环境变量比如数据库连接地址、端口号这些敏感或易变的信息我们不希望硬编码在代码里。nodemon: 一个开发工具它会监视文件变化并自动重启Node.js应用省去我们手动重启的麻烦。安装完成后你的package.json文件里的dependencies和devDependencies部分应该已经更新了。2. 搭建基础网关服务环境准备好了现在开始写代码。我们先搭建一个最基础的Express服务器让它能接收请求并转发给后端服务。2.1 创建基础服务器文件在项目根目录下创建一个app.js文件作为我们应用的入口。// app.js require(dotenv).config(); // 加载环境变量 const express require(express); const axios require(axios); const app express(); const PORT process.env.PORT || 3000; // 从环境变量读取端口默认3000 // 中间件解析JSON格式的请求体 app.use(express.json()); // 这是我们的后端情感分析服务地址示例实际请替换 const BACKEND_SERVICE_URL process.env.BACKEND_URL || http://localhost:8080/api/analyze; // 定义一个简单的健康检查端点 app.get(/health, (req, res) { res.status(200).json({ status: Gateway is healthy }); }); // 核心网关路由接收情感分析请求 app.post(/api/analyze, async (req, res) { try { console.log([Gateway] 收到请求开始转发至后端服务...); // 1. 获取客户端传来的文本数据 const { text } req.body; if (!text) { return res.status(400).json({ error: 请求中缺少 text 字段 }); } // 2. 使用axios将请求转发给真正的后端服务 const backendResponse await axios.post(BACKEND_SERVICE_URL, { text }); // 3. 将后端服务的响应原样返回给客户端 console.log([Gateway] 请求处理成功。); res.status(backendResponse.status).json(backendResponse.data); } catch (error) { console.error([Gateway] 请求处理失败:, error.message); // 处理错误例如后端服务不可用或超时 if (error.response) { // 后端服务返回了错误状态码 res.status(error.response.status).json(error.response.data); } else if (error.request) { // 请求发出了但没有收到响应如网络超时 res.status(502).json({ error: 后端服务无响应 }); } else { // 其他错误如代码错误 res.status(500).json({ error: 网关内部错误 }); } } }); // 启动服务器 app.listen(PORT, () { console.log( M2LOrder API网关已启动监听端口: ${PORT}); console.log( 健康检查地址: http://localhost:${PORT}/health); });这个文件做了几件事引入了必要的包并初始化Express应用。定义了一个/health接口用于检查网关本身是否存活。定义了一个核心的/api/analyze接口。它接收客户端发来的文本然后通过axios转发给配置好的后端服务地址最后将结果返回给客户端。包含了基本的错误处理逻辑。2.2 配置环境变量与启动在项目根目录创建一个.env文件用来存放配置# .env PORT3001 BACKEND_URLhttp://your-real-backend.com/api/analyze REDIS_URLredis://localhost:6379现在修改package.json中的scripts部分方便我们启动{ scripts: { start: node app.js, dev: nodemon app.js } }在终端运行npm run dev你应该能看到成功启动的日志。用Postman或curl测试一下/health和/api/analyze接口这个最基础的网关就能工作了。3. 实现请求队列与并发控制基础转发有了但直接转发无法应对突发的高并发。如果一瞬间涌来一万个请求后端服务会瞬间被打垮。解决办法就是引入队列把请求先收下来排好队再按后端能处理的速度一个个发过去。3.1 集成Redis与Bull队列首先确保你的本地或某个服务器上运行着Redis。然后我们创建一个专门管理队列的模块。新建一个文件queue/analyzeQueue.js// queue/analyzeQueue.js const Queue require(bull); const axios require(axios); // 创建队列实例命名为 sentiment-analysis并连接Redis const analyzeQueue new Queue(sentiment-analysis, process.env.REDIS_URL || redis://localhost:6379); // 定义这个队列要处理的任务 analyzeQueue.process(async (job) { // job.data 包含了客户端发来的数据 const { text, requestId } job.data; console.log([Queue] 开始处理任务 ${job.id}, 请求ID: ${requestId}); try { // 这里模拟调用后端服务实际替换为你的后端API调用 const backendResponse await axios.post(process.env.BACKEND_URL, { text }); // 返回处理成功的结果 return { success: true, data: backendResponse.data, jobId: job.id }; } catch (error) { console.error([Queue] 任务 ${job.id} 处理失败:, error.message); // 如果失败可以抛出错误Bull会根据配置进行重试 throw new Error(后端服务调用失败: ${error.message}); } }); // 监听队列事件可选用于监控 analyzeQueue.on(completed, (job, result) { console.log([Queue] 任务 ${job.id} 已完成结果:, result.success); }); analyzeQueue.on(failed, (job, err) { console.error([Queue] 任务 ${job.id} 失败原因:, err.message); }); module.exports analyzeQueue;3.2 修改网关路由以使用队列现在修改app.js中的/api/analyze路由让它不再直接转发而是将任务推入队列。// app.js (修改部分) const analyzeQueue require(./queue/analyzeQueue); app.post(/api/analyze, async (req, res) { const { text } req.body; if (!text) { return res.status(400).json({ error: 请求中缺少 text 字段 }); } // 为每个请求生成一个唯一ID方便追踪 const requestId req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}; console.log([Gateway] 收到请求 ${requestId}加入处理队列。); try { // 将任务添加到队列并设置一些选项如超时时间、重试次数 const job await analyzeQueue.add({ text, requestId }, { attempts: 3, // 失败后重试3次 timeout: 30000 // 任务超时时间30秒 }); // 立即响应客户端告知请求已接受正在处理 res.status(202).json({ // 202 Accepted 状态码很合适 message: 请求已接受正在处理中, requestId: requestId, jobId: job.id, checkStatusUrl: /api/job/${job.id}/status // 提供一个查询进度的URL }); } catch (error) { console.error([Gateway] 无法将请求 ${requestId} 加入队列:, error); res.status(503).json({ error: 服务繁忙请稍后重试 }); } }); // 新增查询任务状态的接口 app.get(/api/job/:jobId/status, async (req, res) { const job await analyzeQueue.getJob(req.params.jobId); if (!job) { return res.status(404).json({ error: 未找到该任务 }); } const state await job.getState(); // 获取任务当前状态waiting, active, completed, failed等 const result job.returnvalue; // 如果完成了这里会有结果 res.json({ jobId: job.id, state: state, result: result, progress: job.progress() // 进度如果有设置的话 }); });这样一来网关的响应速度就非常快了只是把任务丢进Redis队列真正耗时的分析工作被异步处理。客户端收到“已接受”的响应后可以通过返回的jobId轮询状态接口来获取最终结果。4. 集成缓存提升响应速度对于情感分析很多请求可能是重复或相似的比如热门评论。每次都走队列等后端处理太浪费。我们可以引入缓存把高频或相同的请求结果存起来下次直接返回。4.1 使用Redis作为缓存层我们继续用Redis它不仅做队列存储也做缓存。创建一个缓存模块cache/redisCache.js// cache/redisCache.js const Redis require(ioredis); class RedisCache { constructor() { this.client new Redis(process.env.REDIS_URL || redis://localhost:6379); this.defaultTTL 3600; // 默认缓存1小时秒 } // 生成一个基于文本内容的缓存键 generateKey(text) { // 简单使用哈希实际可根据需要调整如加前缀、处理超长文本 return sentiment:${require(crypto).createHash(md5).update(text).digest(hex)}; } // 获取缓存 async get(text) { const key this.generateKey(text); const cached await this.client.get(key); return cached ? JSON.parse(cached) : null; } // 设置缓存 async set(text, data, ttl this.defaultTTL) { const key this.generateKey(text); await this.client.setex(key, ttl, JSON.stringify(data)); } // 删除缓存可选 async del(text) { const key this.generateKey(text); await this.client.del(key); } } module.exports new RedisCache();4.2 在网关路由中应用缓存再次修改app.js中的/api/analyze路由加入缓存逻辑。// app.js (修改部分) const redisCache require(./cache/redisCache); app.post(/api/analyze, async (req, res) { const { text } req.body; if (!text) { return res.status(400).json({ error: 请求中缺少 text 字段 }); } const requestId req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}; // --- 新增缓存检查 --- console.log([Gateway] 收到请求 ${requestId}检查缓存...); const cachedResult await redisCache.get(text); if (cachedResult) { console.log([Gateway] 请求 ${requestId} 命中缓存直接返回。); return res.json({ ...cachedResult, cached: true, // 标记结果来自缓存 requestId: requestId }); } // --- 缓存检查结束 --- console.log([Gateway] 请求 ${requestId} 未命中缓存加入处理队列。); try { const job await analyzeQueue.add({ text, requestId }, { attempts: 3, timeout: 30000 }); // 监听这个任务的完成事件以便将结果存入缓存 job.finished().then(async (result) { if (result result.success) { console.log([Gateway] 任务 ${job.id} 完成结果存入缓存。); // 将成功的分析结果缓存起来 await redisCache.set(text, result.data); } }).catch(err { console.error([Gateway] 任务 ${job.id} 完成但缓存失败:, err); }); res.status(202).json({ message: 请求已接受正在处理中, requestId: requestId, jobId: job.id, checkStatusUrl: /api/job/${job.id}/status }); } catch (error) { console.error([Gateway] 无法将请求 ${requestId} 加入队列:, error); res.status(503).json({ error: 服务繁忙请稍后重试 }); } });现在整个流程就智能多了先查缓存有就直接返回没有才进队列处理处理完再把结果缓存起来。对于热点数据响应速度会是质的飞跃。5. 性能监控与优化建议网关搭好了但怎么知道它运行得好不好呢我们需要一些监控手段。5.1 添加基础性能监控我们可以添加一个简单的监控端点并记录一些基本日志。安装一个轻量级监控中间件npm install express-status-monitor在app.js中启用它// app.js (顶部引入) const statusMonitor require(express-status-monitor); // 在引入路由之前使用中间件 app.use(statusMonitor());这样访问http://localhost:你的端口/status就能看到一个实时监控面板可以看到请求频率、响应时间、内存使用等情况。另外我们可以在关键位置添加更详细的日志比如记录每个请求的耗时。你可以使用console.time和console.timeEnd或者更专业的日志库如winston或pino。5.2 一些实用的优化建议在实际运行中我们还总结了几点经验队列并发度控制Bull队列的process函数可以设置并发数。如果你的后端服务只能同时处理5个请求就不要让队列同时派发10个任务过去。可以在创建队列时设置new Queue(name, redisUrl, { limiter: { max: 5, duration: 1000 } });这表示每秒最多处理5个。缓存策略优化不是所有结果都值得缓存。比如只缓存分析成功的、且文本长度适中的结果。对于负面或敏感内容可能不适合长期缓存。缓存时间TTL也可以根据业务动态调整。网关水平扩展Node.js网关本身是无状态的。当流量巨大时你可以轻松地启动多个网关实例前面用Nginx或云负载均衡器做分流。只需要确保它们连接的是同一个Redis实例用于共享队列和缓存。优雅关闭在服务器需要重启或关闭时应该让正在处理的请求完成而不是直接断开。Express和Bull都提供了相应的钩子函数来实现优雅关闭。设置超时与重试我们在队列任务和axios请求中都设置了超时。对于网络不稳定的场景合理的重试机制就像我们设置的attempts: 3能提高整体成功率。6. 总结走完这一趟一个具备基本高并发处理能力的Node.js API网关就搭建起来了。它核心做了三件事用队列削峰填谷避免后端被冲垮用缓存加速响应提升用户体验用异步解耦让网关本身快速响应。代码看起来不少但拆解开来每一步都很清晰。从最基础的Express服务器到引入Bull管理异步任务再到用Redis做缓存最后考虑监控和优化。这种架构模式不仅适用于情感分析服务对于其他计算密集型或容易成为瓶颈的后端服务加一层这样的网关往往能起到立竿见影的效果。当然这只是一个起点。在生产环境中你还需要考虑更多比如更完善的日志收集、链路追踪、报警机制、安全性限流、鉴权等等。但希望这个实践能给你提供一个清晰的思路和可用的代码骨架。接下来你可以根据自己业务的实际情况在上面添砖加瓦了。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。

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

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

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…