Nodejs 写同一个变量的问题

Nodejs 写同一个变量的问题

用node建立一个http server,在每一个连接中都修改globaltest这个变量,在请求量很大的时候会不会出现修改同一片内存的问题?

代码如下:

var http = require('http');
var globalTest = 0;

var app = http.createServer(function (request, response){

globalTest++; console.log("–" + globalTest);

response.writeHead(200, { ‘Content-Length’: 0, ‘Content-Type’: ‘text/plain’ }); response.end();

}).listen(9001);

console.log(“start run server at port: 9001”);


5 回复

Node.js 中同一个变量的修改问题

在 Node.js 中使用全局变量(如 global 对象或直接定义的变量)时,需要注意多线程环境下的数据同步问题。虽然 Node.js 是单线程的运行模型,但在高并发情况下,多个请求可能会同时访问并修改同一个变量,这会导致数据竞争问题。

示例代码分析

var http = require('http');
var globalTest = 0;

var app = http.createServer(function (request, response) {
    globalTest++;
    console.log("--" + globalTest);

    response.writeHead(200, {
        'Content-Length': 0,
        'Content-Type': 'text/plain'
    });
    response.end();

}).listen(9001);

console.log("start run server at port: 9001");

问题描述

在这段代码中,每次 HTTP 请求都会增加 globalTest 变量的值。在请求量很大的时候,多个请求可能会同时尝试修改 globalTest,从而导致数据竞争问题。由于 Node.js 的事件循环机制,尽管它是单线程的,但多个请求可能几乎同时执行,从而导致读取和写入 globalTest 变量时出现冲突。

如何解决?

  1. 使用锁机制:可以引入锁机制来确保在任何时候只有一个请求能够修改 globalTest。例如,可以使用 async-mutex 库来实现互斥锁。

    const { Mutex } = require('async-mutex');
    
    const mutex = new Mutex();
    
    var http = require('http');
    var globalTest = 0;
    
    var app = http.createServer(async function (request, response) {
        const release = await mutex.acquire();
        try {
            globalTest++;
            console.log("--" + globalTest);
        } finally {
            release();
        }
    
        response.writeHead(200, {
            'Content-Length': 0,
            'Content-Type': 'text/plain'
        });
        response.end();
    
    }).listen(9001);
    
    console.log("start run server at port: 9001");
    
  2. 使用数据库或其他存储机制:如果需要持久化存储,可以考虑将 globalTest 存储在数据库中,每次请求时从数据库读取并更新,而不是直接修改内存中的变量。

  3. 使用原子操作:在某些场景下,可以使用原子操作来保证数据的一致性,但这通常需要底层的支持,比如 Redis 的 INCR 命令。

通过这些方法,可以有效避免多请求同时修改同一变量带来的数据竞争问题。


单线程

因为node是单进程单线程运行的,每一个function都会放入node的一个消息队列,每一个函数类似一个帧,每一时刻只有一个帧在运行,并且只有当前帧运行完以后,才会进入下一帧(function)的运行(这个可以从setTimeout这个函数的解释可以理解),并不会出现类似JAVA这种多线程资源竞争引起的并发问题。 所以我认为你上述说的案例中部会请求量很大的时候不会出现修改同一片内存的问题。

再补充一下: 因为”每一个function都会放入node的一个消息队列,每一个函数类似一个帧,每一时刻只有一个帧在运行,并且只有当前帧运行完以后,才会进入下一帧(function)的运行(这个可以从setTimeout这个函数的解释可以理解)“,所以一般写NODE代码时每一个function最好不要完成的任务过多,而导致该函数执行时间过长,而导致其他function延迟执行

在Node.js中,globalTest 变量是全局变量,所有请求都会共享这个变量。当你在一个HTTP服务器中对 globalTest 进行递增操作时,可能会遇到并发问题,特别是在高并发请求的情况下。

由于JavaScript在单线程环境中运行,通常情况下不会出现数据竞争问题。但在某些情况下,例如使用了某些多线程或异步操作,可能会导致数据不一致。

为了验证这个问题,我们可以稍微修改你的代码来展示可能出现的情况:

var http = require('http');

let globalTest = 0;

const app = http.createServer(function (request, response) {
  // 模拟一些耗时操作
  setTimeout(() => {
    globalTest++;
    console.log(`--${globalTest}`);
    response.writeHead(200, {
      'Content-Length': 0,
      'Content-Type': 'text/plain'
    });
    response.end();
  }, 0);
}).listen(9001, () => {
  console.log("Server running at port 9001");
});

在这个例子中,我们使用 setTimeout 模拟了一个耗时操作。虽然 setTimeout 的延迟时间为0,但它会使当前的事件循环进入下一个事件循环周期,这样可能会导致并发请求同时执行递增操作。

在高并发情况下,可能会出现如下情况:

  • 多个请求几乎同时到达,它们读取到相同的 globalTest 值。
  • 然后这些请求分别递增该值并写回,导致最终的值比预期的要小。

为了确保数据的一致性,可以考虑使用锁机制、原子操作或者数据库等方法来管理全局变量。

如果你只是想简单地测试并发问题,可以使用一些并发测试工具,如Apache JMeter或wrk,来模拟高并发场景。

回到顶部