【问题标题】:How to scale NodeJS linearly on multiple cores?如何在多核上线性扩展 NodeJS?
【发布时间】:2014-02-10 05:50:19
【问题描述】:

我正在对 NodeJS 与 Java 进行快速性能测试。选择的简单用例是查询 MySQL 数据库中的单个表。初步结果如下:

Platform                      | DB Connections | CPU Usage | Memory Usage  | Requests/second
==============================|================|===========|===============|================
Node 0.10/MySQL               | 20             |  34%      |  57M          | 1295
JBoss EAP 6.2/JPA             | 20             | 100%      | 525M          | 4622
Spring 3.2.6/JDBC/Tomcat 7.0  | 20             | 100%      | 860M          | 4275

请注意,Node 的 CPU 和内存使用率远低于 Java,但吞吐量也只有三分之一!然后我意识到 Java 正在使用我 CPU 上的所有四个内核,而 Node 只在一个内核上运行。所以我更改了节点代码以合并集群模块,现在它正在利用所有四个内核。以下是新结果:

Platform                      | DB Connections | CPU Usage | Memory Usage  | Requests/second
==============================|================|===========|===============|================
Node 0.10/MySQL (quad core)   | 20 (5 x 4)     | 100%      | 228M (57 x 4) | 2213

请注意,CPU 和内存使用率现在按比例增加,但吞吐量仅增加了 70%。我预计会增加四倍,超过 Java 吞吐量。我该如何解释差异?我可以做些什么来线性增加吞吐量?

下面是使用多核的代码:

if (Cluster.isMaster) {
    var numCPUs = require("os").cpus().length;
    for (var i = 0; i < numCPUs; i++) {
        Cluster.fork();
    }

    Cluster.on("exit", function(worker, code, signal) {
        Cluster.fork();
    });
}
else {
    // Create an express app
    var app = Express();
    app.use(Express.json());
    app.use(enableCORS);
    app.use(Express.urlencoded());

    // Add routes

    // GET /orders
    app.get('/orders', OrderResource.findAll);

    // Create an http server and give it the
    // express app to handle http requests
    var server = Http.createServer(app);
    server.listen(8080, function() {
        console.log('Listening on port 8080');
    });
}

我正在使用 node-mysql 驱动程序来查询数据库。连接池设置为每个核心 5 个连接,但这没有区别。如果我将此数字设置为 1 或 20,我将获得大致相同的吞吐量!

var pool = Mysql.createPool({
    host: 'localhost',
    user: 'bfoms_javaee',
    password: 'bfoms_javaee',
    database: 'bfoms_javaee',
    connectionLimit: 5
});

exports.findAll = function(req, res) {
    pool.query('SELECT * FROM orders WHERE symbol="GOOG"', function(err, rows, fields) {
        if (err) throw err;
        res.send(rows);
    });
};

【问题讨论】:

  • 你可以试试NODE_ENV=productiongroups.google.com/forum/#!topic/express-js/fqtr1Carr0E
  • 另外,您是否正确汇集连接?这是建议的方式var mysql = require('mysql'); var pool = mysql.createPool(...); pool.getConnection(function(err, connection) { // Use the connection connection.query( 'SELECT something FROM sometable', function(err, rows) { // And done with the connection. connection.release(); // Don't use the connection here, it has been returned to the pool. }); });github.com/felixge/node-mysql
  • 是的,我正确使用了池。我展示的代码只是你所拥有的代码的捷径(我已经尝试过两种方式)。在此 node-mysql 问题上对此进行了广泛澄清:github.com/felixge/node-mysql/issues/712
  • 设置 NODE_ENV=production 没有区别 - 所有数字都保持不变。
  • 我很想看看您是否找到了提高性能的方法。请随时更新。

标签: java spring node.js jakarta-ee node-mysql


【解决方案1】:

尝试设置环境变量export NODE_CLUSTER_SCHED_POLICY="rr"。根据this blog post

【讨论】:

  • 那篇文章谈论的是 NodeJS 版本 0.12,它在 nodejs.org 上尚不可用。它还说循环算法不会影响 Windows 上的性能,这是我进行测试的地方。
  • 是的,但这实际上也适用于最新版本的 v0.10。但是,它特别补偿了 linux 内核调度程序的一个方面,因此在 Windows 上没有必要。
【解决方案2】:

据我所知,您不仅在比较平台,还比较框架。您可能想要移除框架效果并实现一个普通的 HTTP 服务器。例如,Express 应用程序中的所有这些中间件都会增加延迟。另外,您是否确保 Java 库不会缓存频繁请求的数据,从而显着提高性能?

其他需要考虑的事情是 Node 中的内置 http 模块(因此,任何构建在它之上的库,包括 node-mysql)通过 Agent 类维护一个内部连接池(不要与MySQL 连接池),以便它可以利用 HTTP keep-alives。当您对同一服务器运行多个请求而不是打开 TCP 连接、发出 HTTP 请求、获得响应、关闭 TCP 连接并重复时,这有助于提高性能。因此,TCP 连接可以被重用。

默认情况下,HTTP 代理只会同时打开 5 个与单个主机的连接,例如您的 MySQL 服务器。您可以按如下方式轻松更改:

var http = require('http');
http.globalAgent.maxSockets = 20;

考虑到这些变化,看看你能得到什么改进。

其他想法是通过在连接打开和关闭时检查 MySQL 日志来验证 MySQL 连接池是否正确使用。如果经常打开,可能需要增加node-mysql中的idle timeout值。

【讨论】:

  • 感谢您的想法。我目前专注于其他事情,但有待尝试这些建议。
猜你喜欢
  • 2010-09-05
  • 2012-04-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-14
  • 2010-12-31
  • 1970-01-01
相关资源
最近更新 更多