gpt4 book ai didi

javascript - MongoDB 使用未知的 findOne 方法导致大延迟 - New Relic

转载 作者:IT老高 更新时间:2023-10-28 13:22:02 25 4
gpt4 key购买 nike

我设置了 newrelic 以更好地了解我的应用存在哪些瓶颈,但我发现了一个我似乎无法弄清楚的问题。

我的大部分延迟是由 mongoDB user.fineOne 引起的,但主要问题是我似乎无法找到代码中发生的位置。

在下图中,您可以看到调用我的 API 的 get/all/proposal 端点的跟踪详细信息。它首先是 14 个方法调用,它们是我 server.js 中的中间件,然后是 Middleware: Authenticate,其中包含 MongoDB Users findOne,这就是延迟所在。

enter image description here

获取/全部/提案的代码:

app.get('/all/proposals',isLoggedIn,function(req, res) {
Proposal.find().sort({proposalNo: -1}).limit(5).exec(function(err,proposal){
if(err){
console.log(err);
}else{
console.log("All Proposals " + proposal);
res.json(proposal);
}
});
});

现在我看不到我在 get/all/proposals 上在 MongoDB 上运行 User.findOne 调用。最初我以为是 isLoggedIn 中间件,我在其中检查用户是否在 session 中(Passport.js),但正如您所见,isLoggedIn Middleware 只需要 0.0222(毫秒)。

同样的问题出现在多个 API 端点上,即 get/caseStudy 并且它总是 user.findOne 下面的另一个示例:

enter image description here

谁能帮我解决这个问题。如果您需要更多详细信息,请告诉我,我猜您会的。

更新:Server.js 代码

 // set up ======================================================================

require('newrelic');
var express = require('express');
var app = express(); // create our app w/ express
var server = require('http').createServer(app);
var mongoose = require('mongoose'); // mongoose for mongodb
var port = process.env.PORT || 8080; // set the port
var database = require('./config/db'); // load the database config
var morgan = require('morgan'); // log requests to the console (express4)
var bodyParser = require('body-parser'); // pull information from HTML POST (express4)
var methodOverride = require('method-override'); // simulate DELETE and PUT (express4)
var passport = require('passport');
var flash = require('connect-flash');
var session = require('express-session');
var cookieParser = require('cookie-parser');
var compression = require('compression');
var nodemailer = require('nodemailer');
var busboy = require("connect-busboy");

// configuration ===============================================================


mongoose.connect(database.url); // connect to mongoDB database on modulus.io
require('./config/passport')(passport);
app.use(express.static(__dirname + '/public'));
app.use(express.static(__dirname + '/views')); // set the static files location /public/img will be /img for users
app.use(busboy());
app.use(compression()); //use compression
app.use(morgan('dev')); // log every request to the console
app.use(bodyParser.urlencoded({'extended': true})); // parse application/x-www-form-urlencoded
app.use(bodyParser.json()); // parse application/json
app.use(bodyParser.json({ type: 'application/vnd.api+json' })); // parse application/vnd.api+json as json
app.use(methodOverride());
app.use(cookieParser()); // read cookies (needed for auth)
app.set('view engine', 'ejs'); // set up ejs for templating

// required for passport
app.use(session({ secret: '', resave: false, saveUninitialized: false })); // session secret
app.use(passport.initialize());
app.use(passport.session()); // persistent login sessions
app.use(flash()); // use connect-flash for flash messages stored in session

// routes ======================================================================

require('./routes/index.js')(app, passport); // load our routes and pass in our app and fully configured passport
//require('./routes/knowledgeBase/index.js')(app, passport);
require('./routes/bios/index.js')(app, passport);

// listen (start app with node server.js) ======================================

app.listen(port);
console.log("App listening on port " + port);

更新 2:Passport.js

// config/passport.js

// load all the things we need
var LocalStrategy = require('passport-local').Strategy;
var crypto = require("crypto");
var api_key = '';
var domain = '';
var mailgun = require('mailgun-js')({apiKey: api_key, domain: domain});
// load up the user model
var User = require('../app/models/user');

// expose this function to our app using module.exports
module.exports = function(passport) {

// =========================================================================
// passport session setup ==================================================
// =========================================================================
// required for persistent login sessions
// passport needs ability to serialize and unserialize users out of session

// used to serialize the user for the session
passport.serializeUser(function(user, done) {
done(null, user.id);
});

// used to deserialize the user
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});

// =========================================================================
// LOCAL SIGNUP ============================================================
// =========================================================================
// we are using named strategies since we have one for login and one for signup
// by default, if there was no name, it would just be called 'local'

passport.use('local-signup', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
firstNameField: 'firstName',
lastNameField: 'lastName',
usernameField: 'email',
passwordField: 'password',
jobTitleField: 'jobTitle',
startDateField: 'startDate',
passReqToCallback: true // allows us to pass back the entire request to the callback
},

function(req, email, password, done) {

// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
User.findOne({
'email': email
}, function(err, user) {
// if there are any errors, return the error
if (err)
return done(err);

// check to see if theres already a user with that email
if (user) {
return done(null, false, {
message: 'That email is already taken.'
});
}
else {

var token = crypto.randomBytes().toString();
// if there is no user with that email
// create the user
var newUser = new User();

// set the user's local credentials
newUser.firstName = req.body.firstName;
newUser.lastName = req.body.lastName;
newUser.email = email;
newUser.password = newUser.generateHash(password); // use the generateHash function in our user model
newUser.jobTitle = req.body.jobTitle;
newUser.startDate = req.body.startDate;
newUser.birthday = req.body.birthday;
newUser.region = req.body.region;
newUser.sector = req.body.sector;
newUser.accountConfirmationToken = token;
newUser.accountConfirmationTokenExpires = Date.now() + 3600000;
newUser.accountVerified = 'false';



// save the user
newUser.save(function(err) {
if (err)
throw err;
else {
return done(null, newUser);
}
});
}

});

}));

// =========================================================================
// LOCAL LOGIN =============================================================
// =========================================================================
// we are using named strategies since we have one for login and one for signup
// by default, if there was no name, it would just be called 'local'

passport.use('local-login', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true // allows us to pass back the entire request to the callback
},
function(req, email, password, done) { // callback with email and password from our form

// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
User.findOne({ 'email' : email }, function(err, user) {
// if there are any errors, return the error before anything else
if (err)
return done(err);

// if no user is found, return the message
if (!user)
return done(null, false, req.flash('loginMessage', 'No user found.')); // req.flash is the way to set flashdata using connect-flash

// if the user is found but the password is wrong
if (!user.validPassword(password))
return done(null, false, req.flash('loginMessage', 'Oops! Wrong password.')); // create the loginMessage and save it to session as flashdata

if(user.accountVerified == 'false')
return done(null, false, req.flash('loginMessage', 'Looks like you have not verified your account after registeration.'));
else
user.lastLogin = Date.now();
user.save(function(err) {
if (err)
throw err;
else {
// all is well, return successful user
return done(null, user);
}
});
});

}));

};

更新 3:isLoggedIn 函数

 // route middleware to make sure a user is logged in
function isLoggedIn(req, res, next) {

// if user is authenticated in the session, carry on
if (req.isAuthenticated())
return next();

// if they aren't redirect them to the home page
res.redirect('/');
}

更新 4:获取提案的步骤

第 1 步:首先加载提案页面

 app.get('/proposals',isLoggedIn,function(req, res) {
res.render('proposals.ejs', {
user : req.user // get the user out of session and pass to template
});
});

第 2 步:提案页面有一个 angular.js Controller /工厂,它在页面加载时调用以下函数来获取数据。

// =========================================================================
// FUNCTIONS TO BE RUN WHEN THE PAGE FIRST LOADS TO POPULATE FRONT-END =====
// =========================================================================
$scope.intialize = function() {
$scope.getAllSectors();
$scope.getLatestProposals();
}

// ===============================
// GET LATEST *5* PROPOSALS =====
// ===============================
factory.getLatestProposals = function() {

return $http.get('/all/proposals')
.then(function(response) {
//promise is fulfilled
deferred.resolve(response.data);

console.log("readched the filtered project service!");

//promise is returned
// return deferred.promise;
return response.data;

}, function(response) {
deferred.reject(response);

//promise is returned
return deferred.promise;
});
};

第 3 步:/all/proposals 路由被调用

 // =======================
// GET All Proposals =====
// =======================
app.get('/all/proposals',isLoggedIn,function(req, res) {
Proposal.find().sort({proposalNo: -1}).limit(5).exec(function(err,proposal){
if(err){
console.log(err);
}else{
console.log("All Proposals " + proposal);
res.json(proposal);
}
});
});

最佳答案

查看您提供的代码后,您的性能日志中显示的 .findOne() 似乎是搜索用户并对其进行身份验证时执行的代码。

因此,性能瓶颈似乎出现在以下 2 个查询之一中:

/*
* LOCAL LOGIN
*/
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
User.findOne({ 'email' : email }, function(err, user) {
...


/*
* LOCAL SIGNUP
*/
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
User.findOne({
'email': email
...

我发现在您的两个护照本地策略中,您都在针对 email 字段进行搜索,因此您可以通过在该字段上创建索引来提高性能。

要尝试进一步优化 LOCAL LOGIN findOne 查询,您可以尝试在 email 字段中为 users 集合添加索引,如果您还没有:

// This index will optimize queries that search against the {email} field
db.users.createIndex({ email: 1});

更新 #1

我找到了一个相关的 Stack Overflow topic这可能是您的性能问题的答案 - 您应该在 express.js 配置中更新以下行:

app.use(session({ secret: '',  resave: false, saveUninitialized: false }));

app.use(session({ secret: '',  resave: true, saveUninitialized: true }));

我还设法在 Express JS 文档中找到了有关 resavesaveUninitialized 属性的这些注释:

saveUninitialized

Forces a session that is "uninitialized" to be saved to the store. A session is uninitialized when it is new but not modified. Choosing false is useful for implementing login sessions, reducing server storage usage, or complying with laws that require permission before setting a cookie. Choosing false will also help with race conditions where a client makes multiple parallel requests without a session.

The default value is true, but using the default has been deprecated, as the default will change in the future. Please research into this setting and choose what is appropriate to your use-case.

Note if you are using Session in conjunction with PassportJS, Passport will add an empty Passport object to the session for use after a user is authenticated, which will be treated as a modification to the session, causing it to be saved. This has been fixed in PassportJS 0.3.0

resave

Forces the session to be saved back to the session store, even if the session was never modified during the request. Depending on your store this may be necessary, but it can also create race conditions where a client makes two parallel requests to your server and changes made to the session in one request may get overwritten when the other request ends, even if it made no changes (this behavior also depends on what store you're using).

The default value is true, but using the default has been deprecated, as the default will change in the future. Please research into this setting and choose what is appropriate to your use-case. Typically, you'll want false.

How do I know if this is necessary for my store? The best way to know is to check with your store if it implements the touch method. If it does, then you can safely set resave: false. If it does not implement the touch method and your store sets an expiration date on stored sessions, then you likely need resave: true.

关于javascript - MongoDB 使用未知的 findOne 方法导致大延迟 - New Relic,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39992976/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com