我的大部分延迟都是由mongoDB user.fineOne引起的,但主要问题是我似乎无法找到代码中的位置.
在下图中,您可以看到调用我的API的get / all / proposal端点的跟踪详细信息.它首先是14个方法调用,它们是我的server.js中的中间件,之后是一个中间件:认证它有MongoDB用户findOne和那里的延迟.
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 / proposal上运行MongoDB上的User.findOne调用.最初我认为是isLoggedIn中间件,我检查用户是否在会话中(Passport.js)但是你可以看到isLoggedIn Middleware只需要0.0222(ms).
同样的问题出现在多个API端点上,即get / caseStudy,它始终是user.findOne,下面是另一个例子:
谁能帮我解决这个问题.如果您需要更多详细信息,请告诉我.
更新:
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 },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 },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,it would just be called 'local' passport.use('local-login',new LocalStrategy({ // by default,we will override with email usernameField : 'email',passwordField : 'password',passReqToCallback : true // allows us to pass back the entire request to the callback },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 },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,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,'Oops! Wrong password.')); // create the loginMessage and save it to session as flashdata if(user.accountVerified == 'false') return done(null,'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',res) { res.render('proposals.ejs',{ user : req.user // get the user out of session and pass to template }); });
第2步:
proposal页面有一个angular.js控制器/工厂,它在页面加载时调用以下函数来获取数据.
// ========================================================================= // 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 / proposal路由
// ======================= // GET All Proposals ===== // ======================= app.get('/all/proposals',proposal){ if(err){ console.log(err); }else{ console.log("All Proposals " + proposal); res.json(proposal); } }); });
解决方法
/* * 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 },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 ...
我看到您在两个护照本地策略中搜索电子邮件字段,因此您可以通过在该字段上创建索引来提高性能.
要尝试优化LOCAL LOGIN findOne查询,您可以尝试在电子邮件字段中为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: '',saveUninitialized: false }));
至
app.use(session({ secret: '',resave: true,saveUninitialized: true }));
我还设法在Express JS文档中找到关于resave和saveUninitalized属性的这些注释:
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
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,
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.