nodejs 解决高并发

在Node.js中解决高并发的问题,可以采取以下几种策略:

1. 异步非阻塞I/O: Node.js的异步非阻塞I/O模型让其在处理高并发请求时具有优势。在接收到请求后,Node.js可以立即接受其他请求,而不需要等待当前请求的I/O操作完成,从而提高并发处理能力。

// 引入http模块
const http = require('http');// 创建一个 HTTP 服务器
const server = http.createServer((req, res) => {// 模拟异步的I/O操作,比如数据库查询或文件读取setTimeout(() => {// 服务器向客户端返回消息res.writeHead(200);res.end('Hello Worldn');}, 100);
});// 监听端口
server.listen(8000, () => {console.log('Server is running at http://localhost:8000/');
});
  1. 首先,我们导入了Node.js的内置http模块。

  2. 使用http.createServer()创建一个新的HTTP服务器。这个函数的参数是一个回调函数,每当服务器接收到新的请求时,这个回调函数就会被调用。

  3. 在回调函数中,我们用setTimeout()模拟了一个异步的I/O操作。这个操作用时100毫秒,模拟了例如读取数据库或读取文件这样的耗时操作。

  4. 当这个异步操作完成,我们用res.writeHead(200)设置了HTTP响应的状态码为200,表示请求成功。然后,用res.end(‘Hello Worldn’)结束HTTP响应,并返回内容"Hello Worldn"。

  5. 最后,我们让服务器在8000端口上监听,当服务器开始运行后,会在控制台打印出"Server is running at http://localhost:8000/"。

这是Node.js如何以异步非阻塞的方式来处理高并发请求的一种典型方式。尽管JavaScript是单线程的,但Node.js的这种机制可以让它在接收到新请求的同时,继续处理其他的请求,不需要等待当前请求的I/O操作完成,从而能够处理大量并发请求。

2. 事件驱动: Node.js的事件驱动模型可以将CPU密集型操作和I/O操作分离,让CPU和I/O可以并行工作,从而提高处理效率。

const http = require('http');
const events = require('events');
const emitter = new events.EventEmitter();// 创建一个HTTP服务器
const server = http.createServer((req, res) => {// 异步事件触发和监听emitter.on('data_received', () => {res.writeHead(200);res.end('Hello Worldn');});// 模拟异步I/O操作,比如数据库查询或文件读取setTimeout(() => {emitter.emit('data_received');}, 100);
});// 监听端口
server.listen(8000, () => {console.log('Server is running at http://localhost:8000/');
}); 
  • 首先,我们使用Node.js内置的http模块创建了一个HTTP服务器,它会对初始的每个请求建立一个新的事件监听器。

  • 我们定义了一个事件监听器,它会在’data_received’事件发生时向客户端返回HTTP响应。这里的事件可以模拟如数据库查询、文件读取等异步I/O操作。

  • 用setTimeout模拟了一个异步操作,它在100毫秒后触发’data_received’事件,此事件被监听并执行了响应的返回。此时,可以接受其他的请求,因此是非阻塞的。

  • 服务器监听8000端口运行,当服务器开始运行后,会在控制台输出’Server is running at http://localhost:8000/'。

这就是Node.js使用事件驱动模型处理高并发请求的基本方式。

3. 使用中间件控制并发: 可以使用如eventproxy、async等中间件对并发进行控制

// 导入所需模块
var eventproxy = require('eventproxy');
var superagent = require('superagent');
var cheerio = require('cheerio');
var url = require('url');// 目标网站
var cnodeUrl = 'https://test.org/';superagent.get(cnodeUrl).end(function (err, res) {if (err) { return console.error(err); }var topicUrls = [];var $ = cheerio.load(res.text);$('#topic_list .topic_title').each(function (idx, element) {var $element = $(element);var href = url.resolve(cnodeUrl, $element.attr('href'));topicUrls.push(href);});// 得到一个 eventproxy 实例var ep = new eventproxy();// 命令 ep 重复监听 topicUrls.length 次(在这里也就是 40 次)topic_html 事件再行动ep.after('topic_html', topicUrls.length, function (topics) {topics = topics.map(function (topicPair) {var topicUrl = topicPair[0];var topicHtml = topicPair[1];var $ = cheerio.load(topicHtml);return ({title: $('.topic_full_title').text().trim(),href: topicUrl,comment1: $('.reply_content').eq(0).text().trim(),});});console.log('final:');console.log(topics);});topicUrls.forEach(function (topicUrl) {superagent.get(topicUrl).end(function (err, res) {console.log('fetch ' + topicUrl + ' successful');ep.emit('topic_html', [topicUrl, res.text]);});});});

这段代码一步步解析如下:

  1. 使用 superagent 获取测试页面信息。
  2. cheerio 解析出所有需要并发爬取的主题 URL 并存入数组 topicUrls。
  3. 使用 eventproxy 控制这次并发爬取,首先通过 after 方法,监听长度等于 topicUrls.length 的 topic_html 事件,当所有数据爬取成功后执行回调。
  4. 在回调中,用 cheerio 解析每个主题页面的结构,取出需要的数据。
  5. 最后,在 forEach 循环中,针对每个 URL 使用 superagent 发送请求,请求结束后调用 ep.emit 来通知 eventproxy 数据已返回。
  6. 使用如 async.mapLimit 或 async.queue 等函数来限制并发数量时,可以确保任何时刻只有一定数量的函数在执行。一旦一个函数执行完成,async 就会从队列中取出下一个任务开始执行,从而保证避免了 “饥饿” 问题,即无法得到处理的请求等待过久。
    限制并发不仅能防止应用用尽系统资源,还能使应用处于一个我们可以更好管理和理解的状态,提高系统的稳定性和效率。

4. 服务器集群: Node.js提供了cluster模块,可以创建一个主进程和多个工作进程,主进程接收到请求后将其分发给工作进程处理,从而实现负载均衡。
在 Node.js 中,我们可以通过 cluster 模块实现服务器集群化。以下是一个使用 cluster 创建主进程和多个工作进程的代码示例:

var cluster = require("cluster");
var numCPUs = require("os").cpus().length;if (cluster.isMaster) {let primes = [];for (let i = 0; i < numCPUs; i++) {const worker = cluster.fork();worker.send({});  // 向子进程发送数据}cluster.on("exit", function(worker) {console.log("进程" + worker.process.pid + "结束");});cluster.on("message", function(worker) {worker.kill();  // 当主进程收到消息时,关闭子进程});} else {process.on("message", (msg) => {process.send({data: "结束"});  // 在子进程中,发送消息给主进程});
}
  1. 如果当前进程是主进程(通过 cluster.isMaster 判断),那么创建子进程(使用 cluster.fork()方法),数量等同于 CPU 核数(通过 os.cpus().length 获得)。

  2. 然后在主进程中监听 ‘exit’ 事件,当任何一个工作进程关闭的时候,这个事件会被触发。

  3. 主进程也会监听 ‘message’ 事件,当接收到子进程发送的消息后,主进程会关闭对应的子进程。

  4. 如果当前进程是子进程,那么就处理来自主进程的消息(通过 process.on(‘message’) 方法),并在处理完成后向主进程发送消息。

5. 利用反向代理服务器: 使用像Nginx这样的反向代理服务器,可以帮助分发请求,提供静态文件服务,缓存内容等,从而减轻Node.js服务器的压力。

Nginx 可以充当反向代理服务器,将客户端请求转发到后端的多个服务器上,并将响应返回给客户端。Nginx的工作原理如下:

  • 客户端发送请求:当客户端(例如浏览器)需要访问某个Web服务时,它会向Nginx服务器发送HTTP请求。这个请求包括了要访问的URL和其他相关信息。
  • Nginx接收请求:Nginx服务器接收到客户端发送的请求后,会解析请求中的URL和其他信息。
  • 转发请求:Nginx根据配置规则,将接收到的客户端请求转发给后端的Web服务器。这个过程可能是基于轮询、加权轮询、IP hash等负载均衡策略来选择的。
  • 后端服务器处理请求:后端的Web服务器接收到Nginx转发过来的请求后,会处理这个请求,并生成相应的响应。
  • 返回响应:后端服务器将处理后的响应返回给Nginx服务器。Nginx服务器可能会对这个响应进行一些处理,例如缓存、压缩等。
  • Nginx转发响应:Nginx将后端服务器返回的响应转发给客户端。这样,客户端就得到了它需要的数据。

在整个过程中,Nginx作为反向代理服务器,起到了一个中间人的角色。它接收客户端的请求,转发给后端服务器,再将后端服务器的响应返回给客户端。这样,客户端并不知道它实际上访问的是哪个后端服务器,从而实现了隐藏后端服务器、负载均衡、缓存加速等功能。同时,由于Nginx的高性能和稳定性,它可以处理大量的并发请求,提高整个Web服务的性能和可靠性。

Nginx反向代理配置

一个示例配置,用于将请求反向代理到本地的 8080 端口:

http {server {listen 80;server_name localhost; # 你的域名location / {proxy_pass http://127.0.0.1:8080;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;}}
}

这个配置文件的含义是:当用户访问你的服务器(假设你的服务器 IP 是 yourserverip)的 80 端口时,Nginx 会将请求转发到本地的 8080 端口。

  • listen 80 表示 Nginx 监听 80 端口。
  • location / 表示匹配所有 URI 的 location 块。就是说,这个 location 块会处理所有发送到 Nginx 服务器的请求,不管 URI 是什么。
  • proxy_pass http://127.0.0.1:8080 表示将所有请求转发到本地的 8080 端口,proxy_pass 就是用来设置反向代理的目标服务器地址和端口。
  • proxy_set_header 用于设置转发请求的 HTTP 头部信息。

以下是对 proxy_set_header 的一些解释:

  • proxy_set_header Host $host; 这个指令用于设置 HTTP 请求头部中的 Host 字段,将客户端请求中的 Host 头部信息传递给后端服务器。这样后端服务器就能知道客户端请求的原始 Host 信息。
  • proxy_set_header X-Real-IP $remote_addr; 这个指令用于设置 HTTP 头部 X-Real-IP字段,将客户端的真实 IP 地址传递给后端服务器。这有助于后端服务器获取客户端的真实 IP 地址。
  • proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 这个指令用于设置 HTTP 头部 X-Forwarded-For字段,将客户端的原始 IP 地址列表传递给后端服务器。这个头部通常用于记录经过的代理服务器的 IP 地址,以便后端服务器跟踪请求的来源。

需要注意的是,当请求到达 Nginx 时,Nginx 会根据 location 块的配置顺序和匹配规则来决定使用哪个 location 块来处理请求。你可以使用多个 location 块来根据不同的路径进行不同的转发。

以上就是文章全部内容了,如果喜欢这篇文章的话,还希望三连支持一下,感谢!

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

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

相关文章

AIGC产业链上下游解析及常见名词

文章目录 AIGC上游产业链 - 基础层AIGC中游产业链 - 大模型层与工具层AIGC下游产业链 - 应用层AIGC产业链常见的名词表 在上一章节为大家介绍了 “大模型的不足与解决方案” &#xff0c;这一小节呢为大家针对AIGC的相关产业进行一个拆解&#xff0c;以及相关的一些专业名词做出…

【编程题-错题集】奇数位丢弃(模拟 - 规律)

牛客对应题目链接&#xff1a;奇数位丢弃_牛客题霸_牛客网 (nowcoder.com) 一、分析题目 通过⼀两个例子的模拟&#xff0c;可以发现&#xff1a;每次起始删除的下标都是 2 的次方。根据这个规律&#xff0c;找到最后⼀次删除的起始位置的下标即可。 二、代码 #include <io…

什么是渗透测试

什么是渗透测试渗透测试就是模拟真实黑客的攻击手法对目标网站或主机进行全面的安全评估&#xff0c;与黑客攻击不一样的是&#xff0c;渗透测试的目的是尽可能多地发现安全漏洞&#xff0c;而真实黑客攻击只要发现一处入侵点即可以进入目标系统。 一名优秀的渗透测试工程师也…

感知觉训练:解锁独立生活的钥匙

在日新月异的科技时代&#xff0c;一款名为“蝙蝠避障”的辅助软件以其独到之处&#xff0c;为盲人朋友的日常生活平添了诸多便利&#xff0c;不仅实现了实时避障&#xff0c;还通过拍照识别功能扩展了信息获取的边界。然而&#xff0c;科技辅助之外&#xff0c;提升盲人朋友的…

python桌球游戏

以下是一个简单的桌球游戏的示例代码&#xff1a; import pygame import random # 初始化pygame pygame.init() # 设置屏幕大小和标题 screen_width 800 screen_height 500 screen pygame.display.set_mode((screen_width, screen_height)) pygame.display.set_cap…

Android 深入系统源码探讨 Activity、Window 和 View 的关系与实践

文章目录 1、概括2、Android Window 设计2.1、Window 类2.2、PhoneWindow2.3、WindowManager2.4、ViewRootImpl2.5、DecorView 3、Android Activity 设计3.1、Activity的基本概念3.2.、Activity的生命周期3.3、Activity的内部结构 4、Android View 设计4.1、View的基本概念4.2、…

LangChain 0.2 - 构建RAG应用

本文翻译整理自&#xff1a;Build a Retrieval Augmented Generation (RAG) App https://python.langchain.com/v0.2/docs/tutorials/rag/ 文章目录 一、项目说明什么是 RAG &#xff1f;概念索引检索和[生成 二、预览三、详细演练1.索引&#xff1a;加载2. 索引&#xff1a;拆…

C++中的类型查询:探索typeid和type_info

前言 在编程的世界里&#xff0c;了解和操作数据类型是至关重要的。Python 通过内置的 type() 函数提供了一种简单的方式来查询变量的类型。然而&#xff0c;在 C 这种静态类型语言中&#xff0c;类型信息通常是在编译时就已经确定的。尽管如此&#xff0c;C 标准库还是提供了…

陈丽:人工智能赋能教育创新发展

5月20日&#xff0c;在顾明远先生莅临科大讯飞考察指导高端咨询会暨“人工智能与未来教育”的主题研讨会上&#xff0c;北京师范大学原副校长、中国教育技术协会副会长陈丽教授作了题为《人工智能赋能教育创新发展》的主旨报告。 &#xff08;以下内容根据陈丽教授在研讨会上的…

期权课程之第三节【什么是ITM,ATM,OTM】

我们可以根据正股行权价和股价关系 对期权进行一个分类 ITM 全称为In-the-money&#xff1a;行权后可以获得收益的期权 根据call和put &#xff0c;又可以分为两类 ITM call &#xff1a; 行权价< 股价 &#xff0c;IMT Put &#xff1a;行权价>股价 比如这个时候&a…

React 中的响应时间详解

使用 React 可以在 JSX 中添加 事件处理函数。其中事件处理函数为自定义函数&#xff0c;它将在响应交互&#xff08;如点击、悬停、表单输入框获得焦点等&#xff09;时触发。 添加事件处理函数 如需添加一个事件处理函数&#xff0c;你需要先定义一个函数&#xff0c;然后…

如何使用性能监控工具分析JVM性能瓶颈

1、jConsole&#xff1a; jConsole是JDK自带的Java监控和管理控制台。它提供了一个图形用户界面&#xff08;GUI&#xff09;&#xff0c;用于监控和管理Java应用程序的性能和资源消耗。 使用方法&#xff1a;打开jdk\bin\jconsole.exe&#xff0c;连接到正在运行的Java进程&a…

第十一章:C# 判断

判断语句是控制程序执行流程的基础,用于根据条件执行不同的代码块。C#提供了多种判断语句,包括if语句、if-else语句、else-if语句和switch语句。掌握这些判断语句有助于编写具有逻辑判断能力的程序。 1. if 语句 if语句用于根据条件执行特定的代码块。如果条件为真(true)…

分享免费的手机清理软件app,一款国外开发的手机清理神器,让手机再战两年!

手机内存越来越大&#xff0c;软件却越来越占地方&#xff0c;就像微信这家伙&#xff0c;轻轻松松就吃了十几个G&#xff01; 害得阿星8128G的手机&#xff0c;本来想换新的&#xff0c;结果用了这款Avast Cleanup软件&#xff0c;瞬间感觉手机还能再战两年&#xff01; 注意…

云上聚智共创未来 | 移动云的项目实战,10分钟让你获得高度可玩的个人博客网站

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 引入 随着互联网的发展各种以前看起来离我们比较遥远的词越来越近了&#xff0c;比如 云服务、大数据、区块链、容器这些听起来…

VMware ESXi 7.0U3q macOS Unlocker OEM BIOS 集成网卡驱动和 NVMe 驱动 (集成驱动版)

VMware ESXi 7.0U3q macOS Unlocker & OEM BIOS 集成网卡驱动和 NVMe 驱动 (集成驱动版) ESXi 7 U3 标准版集成 Intel 网卡、Realtek USB 网卡 和 NVMe 驱动 请访问原文链接&#xff1a;https://sysin.org/blog/vmware-esxi-7-u3-sysin/&#xff0c;查看最新版。原创作品…

Android network — 进程指定网络发包

Android network — 进程指定网络发包 0. 前言1. 进程绑定网络1.1 App进程绑定网络1.2 Native进程绑定网络 2. 源码原理分析2.1 申请网络requestNetwork2.2 绑定网络 BindProcessToNetwork 3. 总结 0. 前言 在android 中&#xff0c;一个app使用网络&#xff0c;需要在manifest…

uniapp+vue3小程序版后台管理|uniapp小程序后台OA应用

基于uni-appvue3piniauni-ui等技术构建小程序版后台管理系统UniVue3Admin。实现了栅格化卡片布局引擎、多屏滑动管理、自定义桌面壁纸/小部件、内置常用表格/表单/编辑器及图表等功能。 uniappvite5pinia2跨端仿ios后台OA模板【小程序版】 uni-vue3-wechat基于vue3uniapp高仿微…

X-CSV-Reader:一个使用Rust实现CSV命令行读取器

&#x1f388;效果演示 ⚡️快速上手 依赖导入&#xff1a; cargo add csv读取实现&#xff1a; use std::error::Error; use std::fs::File; use std::path::Path;fn read_csv<P: AsRef<Path>>(filename: P) -> Result<(), Box<dyn Error>> {le…

【Java面试】二、Redis篇(中)

文章目录 1、Redis持久化1.1 RDB1.2 AOF1.3 RDB与AOF的对比 2、数据过期策略&#xff08;删除策略&#xff09;2.1 惰性删除2.2 定期删除 3、数据淘汰策略4、主从复制4.1 主从全量同步4.2 增量同步 5、哨兵模式5.1 服务状态监控5.2 哨兵选主规则5.3 哨兵模式下&#xff0c;Redi…