2016-08-05 11 views
2

Ich habe eine Webanwendung basierend auf NodeJS, Express und Jade 4.13.4.NodeJS/Express: Concurrency-Problem beim Zugriff auf res.locals

index.js

var express = require('express') 
    , app = express() 
    , companyroutes = require('./routes/company') 
(...) 
app.use('/company/', companyroutes) 

Routen/company.js

var express = require('express') 
    , router = express.Router() 

router.all('/:id*', function(req, res, next) { 
    res.locals.companyUri = '/company/' + req.params.id; 

    async.parallel([ 
    // company data 
    function(done){ 
     api.getCompany(req.params.id, req.user, done) 
    }, 
    // user follows data 
    function(done){ 
     api.follows(req.params.id, req.user, 'company', done)) 
    } 
    ], 
    function(err, result){ 
    if (err){ 
     return next(err) 
    } 

    res.locals.company = result[0] 
    res.locals.userFollows = result[1] 

    company.getCompanyOverview(res.locals.company, function(err, results){ 
     if (err) { 
     return next(err) 
     } 
     debug('render company overview') 
     res.render('company-overview', results) 
    }) 

    }) 

}); 

module.exports = router; 

Wie Sie sehen können, habe ich eine einzige: In index.js Ich habe eine Route wie folgt aufgebaut Route, die als Middleware verwendet wird, die folgende Schritte ausführt:

  • bereichern res.locals mit einigen allgemeinen Unternehmensdaten (company und userFollows aus zwei asynchronen, parallelen Anrufen an eine API erhalten)
  • weiteren Anruf zu company.getCompanyOverview macht, die ein paar mehr Anrufe an den API async.parallel einwickelt. Die empfangenen Daten werden in results gespeichert.
  • Rendern Sie die Jade Ansicht company-overview mit results.

So weit so gut. Bei einem einzelnen Aufruf an http://localhost:3000/company/companyA ruft alle Daten von der API und zeigt die Daten für companyA korrekt durch Rendern der Vorlage.

Bei gleichzeitigen Anforderungen unterscheidet sich das Verhalten jedoch. Ich benutze Apachebench unter OSX einige Lasten auf dem Server zu setzen, indem eine andere Firma anfordernden:

brew install parallel 
echo /company/companyB | parallel 'ab -c 10 -n 5000 http://localhost:3000{}' 

Wenn die Website http://localhost:3000/company/companyA mit einem Browser zugreift, werden die Basisdaten für companyB statt companyA gezeigt.

Die Express Docs klar sagen, dass res.locals auf den Lebenszyklus einer einzigen Anfrage scoped ist:

Ein Objekt, das Antwort lokale Variablen scoped auf die Anforderung enthält und daher nur zur Ansicht (n) gemacht während dieser Anfrage/Antwort-Zyklus (falls vorhanden).

Für mich sieht es jedoch wie res.locals wird durch die Anforderungen von ApacheBench überschrieben. Dies ist auch der Fall, wenn AB von einer anderen Maschine läuft!

Ich habe schon viel zu viel Zeit in dieses investiert und ich verstehe es immer noch nicht. Ist es ein Fehler in Express oder mache ich etwas falsch?

Versionen verwendet

$ node -v 
v4.2.2 

$ npm list express 
[email protected] /Users/dani/Documents/mh-web 
├─┬ [email protected] 
│ └─┬ [email protected] 
│ └─┬ [email protected] 
│  └── [email protected] 
└── [email protected] 

$ npm list jade 
[email protected] /Users/dani/Documents/mh-web 
├── [email protected] 
└─┬ [email protected] 
    └── [email protected] 
+0

Probieren Sie 'req' und' page-result' aus, um für jede Anfrage A und B wie z.B. 'console.log (req.params.id, result [0], result [1], req.app.render ('Firmenübersicht', results))'. Wenn gedruckte Daten korrekt sind, dann kann es Probleme im Browser-Cache geben, sonst können Sie sehen, wo das Problem beginnt. –

+0

Dies wird fast immer von einer Variablen verursacht, die Sie nicht deklariert haben, was sie zu einer globalen Variable macht (die bei jeder Anfrage überschrieben wird). Dies ist kein Fehler in Express. – robertklep

Antwort

1

ich den gleichen Fehler erfahren. Irgendwann sah es so aus, als wäre es darauf zurückzuführen, wie ich res.local eingestellt habe. hatte ich einen einfachen Operator =

res.local.VALUE = req.VALUE. 

‚=‘ zuweisen keine Variable wie man erwarten würde, es stattdessen macht nur einen Verweis auf req.VALUE, was bedeutet, wie die gleichzeitige Anforderung und req.VALUE Updates kommt , könnte der Wert ausgetauscht werden.

res.local.VALUE = _.clone(req.VALUE) //using underscore 

so wurde res.local.VALUE nicht mehr gebunden zu erf

aber war es eigenes Objekt ist:

Ich löste dies durch das Objekt zu klonen.

+1

Für jede Anfrage sind sowohl 'req' als auch' res' _vollständig neue_ Objekte, sie werden nicht für andere Anfragen wiederverwendet, so dass sie sich nicht gegenseitig stören können. Sie haben wahrscheinlich das Symptom, aber nicht das zugrunde liegende Problem gelöst. – robertklep

+0

Das habe ich mir auch gedacht. Um fair zu sein, beinhaltete meine Lösung auch die Beseitigung einiger unnötiger Variablen, die schlecht deklariert werden könnten – user3036885