Nodejs 中并发请求在同一个方法中,变量是共享的吗?

Nodejs 中并发请求在同一个方法中,变量是共享的吗?

如题,遇到一个问题,在一个回调方法中多次回调获取的请求尽然是第一次请求的数据,具体请看下文。

1.Tools.shortId()

//用来生成 8 位的数字 id
var redis = new IoRedis(config.redis.dev);

var lock = require(“redis-lock”)(redis, 50); exports.shortid = function (key, retries, callback) { var self = this;

if (typeof retries === "function" && !callback) {
    self.cb = retries;
    self.retries = 5;
} else if (typeof retries === "number") {
    self.retries = retries;
    self.cb = callback;
} else {
    throw new Error('illegal parameters');
}


//生成 id
var getId = function () {
    var num = new Array(8);
    var c;
    for (var i = 0; i < 8; i++) {
        num[i] = Math.floor(Math.random() * 9 + (i === 0 ? 1 : 0));
        c = num[i];
        for (var j = 0; j < i; j++) {
            if (num[j] === c) {
                i--;
                break
            }
        }
    }
    return num.toString().replace(/,/g, '');
};

var setbit = function (id, callback) {
    redis.setbit('shortid:' + key, id, 1, callback);
};

var getbit = function (id, callback) {
    redis.getbit('shortid:' + key, id, callback);
};

var retry = function (cb) {
    //获取锁
    lock('shortidt:lock:' + key, function (done) {
        var id = getId();
        getbit(id, function (err, ret) {
            if (err) {
                cb(err, null);
            } else {
                if (ret === 0) {
                    setbit(id, function (err, ret) {
                        if (err) {
                            cb(err, null);
                        } else {
                            done(function () {
                                cb(null, parseInt(id));
                            });
                        }
                    });
                } else {
                    cb(null, null);
                }
            }
        });

    });
};

var _num = 0;
var intervalId = setInterval(function () {
    retry(function (err, id) {
        if (!id) {
            if (_num === self.retries) {
                clearInterval(intervalId);
                self.cb(null, null);
            } else {
                _num = _num + 1;
            }
        } else {
            clearInterval(intervalId);
            self.cb(err, id);
        }
    });
}, 50);

};

2.module

//mongoose schema
var mongoose  = require('mongoose');
var Schema    = mongoose.Schema;

var UserSchema = new Schema({ loginid: { type: Schema.Types.ObjectId }, id: { type: Schema.Types.Number }, loginname: { type: String }, email: {type: String }, })

3.proxy

exports.saveUser = function (data, callback) {
    var user = new User(data);
Tool.shortid(SHORTID_KEY, 10, function (err, id) {
    if(err || !id) {
        console.error("failed to get shortid. > ", err);
        callback(err, id);
    }else{
        user.loginid = new mongoose.Types.ObjectId(id);
        user.id = id;

        console.log("email::" + user.email);
        user.save(callback);
    }
});

})

在同时处理多个请求时,发现输出的 email 结果总是第一个请求的 email 数据值。不知为何,还请各位帮忙解惑


6 回复

/**
* Created by chaclus on 2017/6/9.
*/

var IoRedis = require(‘ioredis’);
var redis = new IoRedis(dev: {
host: ‘127.0.0.1’,
port: 6379,
password: ‘root’
});

var lock = require(“redis-lock”)(redis, 50);
var shortid = function (key, retries, callback) {
var self = this;

if (typeof retries === “function” && !callback) {
self.cb = retries;
self.retries = 5;
} else if (typeof retries === “number”) {
self.retries = retries;
self.cb = callback;
} else {
throw new Error(‘illegal parameters’);
}


//生成 id
var getId = function () {
var num = new Array(8);
var c;
for (var i = 0; i < 8; i++) {
num[i] = Math.floor(Math.random() * 9 + (i === 0 ? 1 : 0));
c = num[i];
for (var j = 0; j < i; j++) {
if (num[j] === c) {
i–;
break
}
}
}
return num.toString().replace(/,/g, ‘’);
};

var setbit = function (id, callback) {
redis.setbit(‘shortid:’ + key, id, 1, callback);
};

var getbit = function (id, callback) {
redis.getbit(‘shortid:’ + key, id, callback);
};

var retry = function (cb) {
//获取锁
lock(‘app:shortidt:lock:’ + key, function (done) {
var id = getId();
getbit(id, function (err, ret) {
if (err) {
cb(err, null);
} else {
if (ret === 0) {
setbit(id, function (err, ret) {
if (err) {
cb(err, null);
} else {
done(function () {
cb(null, parseInt(id));
});
}
});
} else {
cb(null, null);
}
}
});

});
};

var _num = 0;
var intervalId = setInterval(function () {
retry(function (err, id) {
if (!id) {
if (_num === self.retries) {
clearInterval(intervalId);
self.cb(null, null);
} else {
_num = _num + 1;
}
} else {
clearInterval(intervalId);
self.cb(err, id);
}
});
}, 50);
};

var save = function (data) {

shortid(‘user’, 10, function (err, id) {
if(err) {
console.error(“err:”, err);
}else{
console.log("id: “+ id+”, email: " + data.email)
}
});
};


var user = [‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’];

user.forEach(function (user) {
save({name: user, email: user + “@gmail.com”});
});

这个填写 redis 可以直接调试。


我看你上来就 var self = this, 你有想过你这个地方 this === global 吗?

有个简单的方法是改成 var self = {}; 试试。。。

兄台,请收下我的膝盖,一语点醒梦中人。

谢谢你

在 Node.js 中,并发请求在同一个方法(或函数)中的变量共享情况取决于这些变量的作用域和生命周期。Node.js 是基于事件驱动和单线程的非阻塞 I/O 模型,这意味着它使用事件循环来处理异步操作。

局部变量

如果变量是在函数内部定义的(即局部变量),那么每个请求调用该函数时都会创建该变量的一个新实例,这些实例是独立的,不会共享。

function handleRequest(req, res) {
    let localVariable = "I am local";
    // 每个请求都有自己的 localVariable
    res.send(localVariable);
}

全局变量或闭包中的变量

如果变量是在函数外部定义的(即全局变量)或是在闭包中共享的,那么这些变量将在所有请求之间共享。这可能导致竞态条件和数据不一致。

let sharedVariable = 0;

function handleRequest(req, res) {
    sharedVariable++;
    res.send(sharedVariable.toString());
}
// 这里的 sharedVariable 将在所有请求之间共享,可能导致数据竞争

使用模块作用域

为了避免全局变量带来的问题,可以使用模块作用域来定义变量,这样它们将在模块内共享,但不会跨模块共享。

let moduleScopedVariable = 0;

module.exports = function(req, res) {
    moduleScopedVariable++;
    res.send(moduleScopedVariable.toString());
};

总之,在 Node.js 中,并发请求处理时,局部变量是独立的,而全局变量或闭包中的变量是共享的,需要谨慎处理以避免竞态条件。

回到顶部