【问题标题】:Optimize Express + Swig HTML rendering time优化 Express + Swig HTML 渲染时间
【发布时间】:2015-01-07 06:26:58
【问题描述】:

我遇到了一个关键问题。

我的应用架构描述如下:

nginx -> web app (express/nodejs) -> api (jetty/java) -> mysql

API 应用程序经过了很好的优化,因此这里无需提及其性能。 (约 200 毫秒/请求,100 个请求/秒)

我的网络应用程序:

  1. Express
  2. Swig template engine

在做 profile log 的时候,我注意到 Swig 模板引擎的 HTML 渲染时间阻塞了 I/O 太长,所以它大大增加了其他待处理请求的等待时间。

为了呈现 1MB 的文本/html 响应,Swig 模板需要大约 250 毫秒。


这是我的压力测试的输出:

$ node stress.js 20
Receive response [0] - 200 - 431.682654ms
Receive response [1] - 200 - 419.248099ms
Receive response [2] - 200 - 670.558033ms
Receive response [4] - 200 - 920.763105ms
Receive response [3] - 200 - 986.20115ms
Receive response [7] - 200 - 1521.330763ms
Receive response [5] - 200 - 1622.569327ms
Receive response [9] - 200 - 1424.500137ms
Receive response [13] - 200 - 1643.676996ms
Receive response [14] - 200 - 1595.958319ms
Receive response [10] - 200 - 1798.043086ms
Receive response [15] - 200 - 1551.028243ms
Receive response [8] - 200 - 1944.247382ms
Receive response [6] - 200 - 2044.866157ms
Receive response [11] - 200 - 2162.960215ms
Receive response [17] - 200 - 1941.155794ms
Receive response [16] - 200 - 1992.213563ms
Receive response [12] - 200 - 2315.330372ms
Receive response [18] - 200 - 2571.841722ms
Receive response [19] - 200 - 2523.899486ms
AVG: 1604.10ms

如您所见,请求越晚,等待时间越长。

当我返回响应代码而不是渲染 HTML 时,通过修改一些代码:

function render(req, res, next, model) {
    return res.status(200).end(); // add this line
    res.render('list', model);
}

压力测试输出变为:

$ node stress.js 20
Receive response [0] - 200 - 147.738725ms
Receive response [1] - 200 - 204.656645ms
Receive response [2] - 200 - 176.583635ms
Receive response [3] - 200 - 218.785931ms
Receive response [4] - 200 - 194.479036ms
Receive response [6] - 200 - 191.531871ms
Receive response [5] - 200 - 265.371646ms
Receive response [7] - 200 - 294.373466ms
Receive response [8] - 200 - 262.097708ms
Receive response [10] - 200 - 282.183757ms
Receive response [11] - 200 - 249.842496ms
Receive response [9] - 200 - 371.228602ms
Receive response [14] - 200 - 236.945983ms
Receive response [13] - 200 - 304.847457ms
Receive response [12] - 200 - 377.766879ms
Receive response [15] - 200 - 332.011981ms
Receive response [16] - 200 - 306.347012ms
Receive response [17] - 200 - 284.942474ms
Receive response [19] - 200 - 249.047099ms
Receive response [18] - 200 - 315.11977ms
AVG: 263.30ms

我之前尝试过一些解决方案,但没有一个可以减少响应时间:

使用node-cluster(我的服务器中有2个worker)

if (conf.cluster) {
    // cluster setup
    var cluster = require('cluster');
    var numCPUs = require('os').cpus().length;

    if (cluster.isMaster) {
        for (var i = 0; i < numCPUs; i++) {
            cluster.fork();
        }

        cluster.on('exit', function(worker, code, signal) {
            console.log('Worker ' + worker.process.pid + ' died');

            // create new worker
            cluster.fork();
        });
    } else {
        rek('server').listen(conf.port, function() {
            console.log('Application started at port ' + conf.port + ' [PID: ' + process.pid + ']');
        });
    }
} else {
    rek('server').listen(conf.port, function() {
        console.log('Application started at port ' + conf.port + ' [PID: ' + process.pid + ']');
    });
}

使用JXCore 16 个线程(最大线程数)

jx mt-keep:16 app.js

使用NGINX load balancing

启动4个节点进程

$ PORT=3000 forever start app.js
$ PORT=3001 forever start app.js
$ PORT=3002 forever start app.js
$ PORT=3003 forever start app.js

nginx.conf

upstream webapp {
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
    server 127.0.0.1:3003;
}

server {
    listen 80;

    location / {
        proxy_pass http://webapp;
    }

    [...]
}

我以为以上所有解决方案都会提供多个进程/线程在执行像HTML渲染这样的繁重任务时不会互相阻塞,但结果与我的预期不一样:等待时间并没有减少。虽然日志显示请求实际上由多个进程/线程提供服务。

我在这里错过了什么吗?

或者你能告诉我另一个减少等待时间的解决方案吗?

【问题讨论】:

  • 仅仅优化渲染时间没有简单的答案。这个问题太笼统了。您真正想要做的是防止您的脚本阻塞 IO,您可以通过创建集群并使用主服务器将处理负载分配给工作人员来做到这一点。 master的IO永远不会阻塞,可以同时处理多个请求。
  • 问题是虽然我创建了集群/线程,但性能并没有变得更好

标签: javascript node.js express nginx swig-template


【解决方案1】:

创建集群不会减少响应时间,但可以让您在不阻塞 IO 的情况下并行运行响应。当然,为了正确使用集群,您需要为 master 设置自己的逻辑以有效地控制 worker。添加没有适当逻辑的集群永远不会给您带来任何真正的好处。为了使这项工作正常进行,您的 master 需要处理所有传入的请求并将它们分发给工作人员进行处理。然后工人将结果发送回处理其余部分的主人。

【讨论】:

    【解决方案2】:

    我认为您应该检查以下几点:

    ->使用模板缓存避免阻塞IO

    ->将模板拆分为多个部分并按需加载

    ->采集数据时间已经覆盖了一次?

    【讨论】:

      【解决方案3】:

      这里真正的答案是缓存。

      每次遇到压力时都需要重新渲染页面吗?我不认为模板数据每分钟都会改变,如果有的话。

      一个解决方案可能是编写 2 个充当“三明治”的中间件,渲染的路由应该具有这样的结构:

      • 请求
      • 从缓存中获取中间件
      • 数据库???
      • swig 模板
      • 放入缓存中间件
      • 响应中间件

      put-in-cache 应该将编译后的模板写入一个非常快速的缓存数据库(REDIS 非常适合),使其在 1 分钟内到期,并使用 url 或其他一些(更聪明的)机制(如 userid)对其进行索引请求它,或标头)

      get-from-cache 会在每个 HTTP 请求上向 REDIS 查询“索引查询”,如果它发现模板已经编译,它应该只是 res.send 离开

      【讨论】:

        猜你喜欢
        • 2011-10-02
        • 2014-07-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-07-14
        相关资源
        最近更新 更多