节点如何在并行function中使用路由

我正在尝试使节点渲染更快。

那么我想使用并行。

那么如何在并行path中放置函数呢?

之前

var app = express(); var index = require('./routes/index')(); var auth = require('./routes/auth')(); app.use('/',index); app.use('/auth/',auth); 

之后(我正在尝试这个)

 var app = express(); var index = require('./routes/index')(); var auth = require('./routes/auth')(); function parallel(middlewares){ return function (req, res, next){ async.each(middlewares,function(mw,cb){ mw(req,res,cb); },next); }; }; app.use(parallel([ ['/',index], ['/auth/',auth], [others here] ])); 

我find了一个方法来做到这一点。 它带有一些注意事项,主要是因为Express是围绕顺序中间件devise的,但通过遵循一套指导原则,您可以使其工作得很好。

问题陈述

  1. 我们想要传入一组中间件,并让它们并行运行(或者与它们的asynchronous操作所允许的并行运行)。 如果你在中间件中有多个独立的asynchronous事件,这应该能够更快地得到最终结果(这几乎是完成这个任务的关键)。
  2. 我们希望能够在app.use('/product/:id', fn)传入典型的路由path(包含所有通配符和特殊字符app.use('/product/:id', fn) ,然后只执行与当前请求匹配的路由“并行”与彼此
  3. 我们希望expression自己做所有的路由匹配,所以我们不必重新实现或复制任何一个,所以支持所有Express通常支持的路由匹配。
  4. 我们希望支持像req.params这样的路由参数,尽pipe对于每个中间件来说这些参数可能是不同的(在中间件中使用它并不常见,但仍然是Expressdevise的一部分)。

devisescheme

我们创build自己的路由器对象。 对于这个路由器对象,我们在开始的时候添加了一个“开始”标记中间件(所以我们可以看到这个路由器何时开始路由),然后为每个并行中间件处理程序添加一个占位符中间件,我们在最后添加了另一个“结束”标记中间件(所以我们可以看到在这个路由器上完成路由的时间)。 “开始”和“结束”路线匹配所有的路线,所以他们总是被调用。 其他路由具有为它们传递的path,所以它们可能会或可能不会被请求,取决于它们是否匹配当前path。

这个路由器被添加到路由堆栈app.use(router) 。 通过这种方式,常规的Express引擎将为此路由器执行所有路由,并决定哪些路由与当前请求path匹配。 但是,当它find匹配的路由path时,不是执行调节器中间件function,而是执行我们的占位符中间件。 当它执行占位符中间件的时候,我们会看到“开始”中间件,任何其他与我们将在列表中捕获的路由相匹配的中间件,然后是“结束”中间件。 当我们获得“最终”中间件时,我们将捕获与当前路由匹配的中间件列表,然后我们可以并行执行那些实际的中间件。 当所有这些中间件都完成后,我们再调用next()来让我们的路由器继续其余的路由。

所以,总之,我们插入虚拟路由处理程序的实际路由path,让快速呼叫我们的假路由处理程序,如果path匹配作为告诉我们哪些路线匹配当前path的手段。 然后,我们采取匹配路线的列表,并将其设置为并行执行。 通过这种方式,Express可以完成告诉我们哪些路由符合当前请求path的所有工作。

履行

因此,为了实现这个,我定义了一个新的app.useParallel()并且为属于该特定中间件路由定义的req.param的中间件函数添加了第四个参数。

 // pass an array of path, fn pairs // ['/', func1, '/somePath', func2] app.useParallel = function(array) { // create a router that will be inserted only for route matching let router = express.Router(); // insert route at beginning to make start of routes getting called router.use(function(req, res, next) { req.routeList = []; next(); }); // let the router have dummy route handlers with all the right paths // so we can use it to see which paths it will match for (let r of array) { router.use(r[0], function(req, res, next) { // for each route that actually gets called (and thus must have matched the path), // save the corresponding callback function and a copy of the req.params req.routeList.push({fn: r[1], params: Object.assign({}, req.params)}); next(); }); } // now insert route at end of router that matches all routes to know when we're done router.use(function(req, res, next) { let routeList = req.routeList; if (routeList && routeList.length) { // now we are ready here to execute the route handlers in req.routeList in parallel let len = routeList.length; let doneCnt = 0; let nextCalled = false; for (let middleware of routeList) { middleware.fn(req, res, function(err) { ++doneCnt; if (err) { // make sure we only call next() once if (!nextCalled) { nextCalled = true; next(err); } } else { if (doneCnt === len && !nextCalled) { next(); } } }, middleware.params); } } else { next(); } }); // insert this router in the chain app.use(router); } 

然后,这是这样使用的:

 function test1(req, res, next, params) { // some async operation that calls next() when done next(); } // similar definitions for test2(), test3() and test4() app.parallel([['/', test1], ['/', test2], ['/:id', test3], ['/test', test4]]); 

限制

并行运行多个中间件可能会对中间件产生一些限制 – 如果您正在设置并行操作,所有这些中间件似乎都会有所期待。 以下是一些限制:

  1. 如果任何处理程序使用asynchronous调用并稍后完成,您将交错执行这些处理程序。 由于node.js仍然是单线程的,所以不会并行执行纯同步的中间件处理程序。 如果它们是同步的,它们仍然会被同步执行。
  2. 每个并行中间件处理程序的初始同步部分(在等待asynchronous响应期间返回之前)仍以正确的顺序调用。
  3. 如果任何中间件调用next(err) ,则第一个中间件将是唯一被处理的 – 其他中间件将被忽略。
  4. req对象在所有的并行中间件function之间共享。 因此,如果在中间件中写入req对象的asynchronous操作,则必须注意使用它的任何竞争条件。 它当然可以用作存储独立属性的地方(对于每个中间件都是不同的),但是由于执行顺序是不可预测的,所以不能期望两个并行中间件顺序访问相同的属性(一个设置它,另一个读取设置的内容) 。 所以,如果每个并行中间件只读取标准属性并且只写入它自己的属性,那么你是最安全的。
  5. 因为req对象是共享的,所以每个中间件都不能像正常的中间件那样有自己的req.param的值。 因此,不要使用req.param 。 相反,每个并行中间件都传递了第四个参数,即param对象。 这允许每个并行中间件都有自己的参数对象。
  6. 如果任何中间件实际上发送了一个响应(而不是为以后的路由处理程序设置reqvariables),那么你需要知道它是活泼的。 一般来说,我不认为你会使用并行中间件来实际发送响应,但我可以想象一些罕见的情况,你只需要第一个find答案的中间件来发送响应。 如果多次尝试发送回复,您将收到一个关于多个回复的警告(Express会为您收到)。 这里没有被封锁。
  7. 不言而喻,这些并行处理程序中的任何asynchronous代码都是以任意顺序完成的。 不要并行执行需要任何特定顺序的处理程序。
  8. 不支持在并行中间件中使用req.route属性。

最小的testing到目前为止

我没有详尽地testing过这个,但是我确实在一个示例应用程序中运行,它使用随机计时器在四个并行中间件中的每一个中调用next() 。 路线匹配工程。 paramsfunction的作品。 中间件似乎并行运行,并按随机顺序(按其随机定时器)完成。

所有并行路由处理程序在继续进行路由之前完成。

并行处理过程中使用的唯一状态存储在req对象上(对于每个请求应该是唯一的)或者closures,所以它应该是安全的,从多个并行请求到同时在运行的服务器的并发请求(尽pipe我没有在有大量并行请求的服务器上进行确认)。