Как масштабировать NodeJS линейно на нескольких ядрах?

Я делаю быстрый тест производительности для 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 Использование ЦП и памяти намного ниже, чем Java, но пропускная способность тоже около трети! Затем я понял, что Java использует все четыре ядра на моем процессоре, тогда как Node работает только на одном ядре. Поэтому я изменил код 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

Обратите внимание, что процессор и использование памяти теперь выросли пропорционально, но пропускная способность только увеличилась на 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);
    });
};

Ответы

Ответ 1

Из того, что я вижу, вы не сравниваете только платформы, а также фреймворки. Вероятно, вы хотите удалить эффект структуры и реализовать простой HTTP-сервер. Например, все те средние в приложении Express добавляют к задержке. Кроме того, вы убедились, что библиотеки Java не кэшируют часто запрашиваемые данные, что значительно улучшит производительность?

Другие вещи, которые следует учитывать, это то, что встроенный модуль http в Node (таким образом, любая библиотека, построенная поверх него, включая node-mysql), поддерживает внутренний пул соединений через класс агента (не путать с пулом соединений MySQL), чтобы он мог использовать HTTP-авиалисты. Это помогает повысить производительность при запуске множества запросов на один и тот же сервер вместо открытия TCP-соединения, создания HTTP-запроса, получения ответа, закрытия TCP-соединения и повторения. Таким образом, TCP-соединения могут быть повторно использованы.

По умолчанию агент HTTP будет открывать только 5 одновременных подключений к одному хосту, например, вашему серверу MySQL. Вы можете легко изменить это следующим образом:

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

Учитывая эти изменения, посмотрите, какое улучшение вы можете получить.

Другие идеи - убедиться, что пул соединений MySQL правильно используется, проверяя журналы MySQL, когда соединения открываются и закрываются. Если они часто открываются, вам может потребоваться увеличить значение тайм-аута ожидания в node -mysql.

Ответ 2

Попробуйте установить переменную окружения export NODE_CLUSTER_SCHED_POLICY="rr". По этот пост в блоге.