node.js多线程-为什么我的所有工作人员都响应一个传入请求? [英] node.js multithreading - why all my workers respond to one incoming request?

查看:121
本文介绍了node.js多线程-为什么我的所有工作人员都响应一个传入请求?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用node + express作为我的Web服务器,并且cluster为4 workers.

I'm using node + express as my web server, and has a cluster of 4 workers.

问题是,所有工作人员实际上都响应了传入的请求.假设每个工作人员都在同一个Web应用程序中使用一个具有api来记录当前worker的进程ID的Web应用程序,当我使用浏览器访问该api时,所有工作人员的进程ID都会被记录.

The problem is, an incoming request is actually responded by all the workers. Say each worker serves the same web app which has an api to log the current worker's process id, when I use a browser to access this api, all the workers' process id got logged.

这是express服务器的index.jscluster部分的代码:

Here's the code of cluster part, in index.js of express server:

if (cluster.isMaster) {
  console.log(`Master cluster setting up ${numWorkers} workers...`);

  for (let i = 0; i < numWorkers; i++) {
    cluster.fork();
  }
} else {
  // webpack configs...

  app.get('/test_connection', (req, res) => res.send('ok'));
  app.use('/js', express.static(`${__dirname}/assets/js`));
  app.get('*', (req, res) => {
    res.sendFile(path.join(__dirname, '..', 'app', 'index.html'));
  });
  app.listen(3000);
}

并且,在应用程序的路由中,有一个控制器,假设当用户单击一个按钮时,Web重定向到某个页面,该页面触发controller来注销当前进程ID,

And, in the app's routes, there's a controller, let's say when the user clicks a button, the web redirects to some page that triggers a controller to log out current process id with:

// inside the controller
console.log(`Connected via ${process.pid}`);

奇怪的是,当用户单击该按钮时,原来所有4个工作进程的ID都被注销了.那不应该只是一个响应传入请求的工作人员的ID吗?

The weird thing is, when user clicks on that button, turns out all 4 worker's process ids got logged out. Shouldn't that be only one worker's id which respond to the incoming request?

这是我的整个服务器根目录:

Here's my entire server root:

const express = require('express');
const path = require('path');
const mongoose = require('mongoose');
mongoose.Promise = require('bluebird');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const webpackDevConfig = require('../webpack/webpack.config.dev');

const mongodb = require('./db/mongo/constants');
const transactionDB = require('./db/postgres');
const config = require('./config/appConfig');
const app = require('./config/express');
const ENV = require('./config/appConfig').ENV;
// const compression = require('compression');
const cluster = require('cluster');
const numWorkers = require('os').cpus().length;

if (cluster.isMaster) {
  console.log(`Master cluster setting up ${numWorkers} workers...`);

  for (let i = 0; i < numWorkers; i++) {
    cluster.fork();
  }

  cluster.on('online', worker => {
    console.log(`Worker ${worker.process.pid} is online`);
  });

  cluster.on('exit', (worker, code, signal) => {
    console.log(`Worker ${worker.process.pid} died with code ${code}, and signal: ${signal}`);
    console.log('Starting a new worker');
    cluster.fork();
  });
} else {
    const compiler = webpack(webpackDevConfig);
    app.use(webpackDevMiddleware(compiler, {
      noInfo: true,
      publicPath: webpackDevConfig.output.publicPath,
    }));
    app.use(webpackHotMiddleware(compiler));

    app.get('/test_connection', (req, res) => res.send('ok'));
    app.use('/fonts', express.static(`${__dirname}/assets/fonts`));
    app.use('/styles', express.static(`${__dirname}/assets/styles`));
    app.use('/images', express.static(`${__dirname}/assets/images`));
    app.use('/js', express.static(`${__dirname}/assets/js`));
    app.get('*', (req, res) => {
      res.sendFile(path.join(__dirname, '..', 'app', 'index.html'));
    });

  // connect to MongoDB
  mongoose.connect(mongodb[ENV].db, (err) => {
    // connect to PostgreSQL
    transactionDB.sequelize
      .sync()
      .then(() => {
        const server = app.listen(app.get('port'));
      });
  });
}

通过const app = require('./config/express')行,该应用将具有路由设置,包括控制器内容.

By the line const app = require('./config/express') the app will have routes settings, including the controller thing.

顺便说一句,我删除了socket.io部分,但是客户端仍然尝试每3秒验证一次令牌,不知道这是否重要.

Btw I commented out socket.io part but the client still tries to validate token every 3 secs, don't know if that matters.

推荐答案

在创建服务器后,您将进行分叉.创建服务器之前,您需要先进行分叉.这样做的原因是,在fork上,每个进程都继承所有打开的文件,然后所有文件都侦听同一服务器.放置所有代码,我可以告诉您您需要移动什么.

You are forking after you have already created the server. You need to fork before you create the server. The reason for this is that on fork, each process inherits all the open files and all then listen to the same server. Place all your code and I can tell you what you need to move around.

我相信这是启动服务器的原因.您要做的是将所有内容放在代码的else(不是master)分支的顶部.

This line, I believe, is what starts your server. What you'll want to do is place everything that you have at the top inside the else (not master) branch of your code.

const app = require('./config/express');

从本质上讲,您的主组件仅应处理旋转子级并对其进行管理.或者,您可以使用现有的插件来处理此问题.例如,快速群集.参见此处: https://www.npmjs.com/package/express-cluster

Essentially, your master component should only handle spinning up the children and managing them. Alternatively, you can use an existing drop-in to handle this. For example, express-cluster. See here: https://www.npmjs.com/package/express-cluster

最后一点,您将要确保每个工作进程都在不同的端口上.如果不是,它们将获得端口重用错误.这就是为什么我建议使用现有库的原因,但是在我工作的地方,我们确实是自己编写的.除非您有充分的理由自己做,否则请使用现有的东西.

And as a last note, you will want to make sure that each worker is on a different port. If they aren't, they will get port reuse errors. This is why I'd suggest using an existing library, though, where I work we did write this ourselves. Unless you have a good reason to do it yourself, use what already exists.

这篇关于node.js多线程-为什么我的所有工作人员都响应一个传入请求?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆