Vue.js 资料Vue.js专区egg

Egg + Vue/Nunjucks 服务端渲染性能测试

2017-10-31  本文已影响1041人  hubcarl

在用Vue做服务端渲染时,大家对Vue服务端渲染的性能持怀疑态度,业界也有一些尝试,不过完整的产品项目和数据分析比较少。结合超级游戏中心,我们对 Vue 和 Nunjucks 进行模板渲染, 针对Render时间,CPU占用,内存占用进行全面的对比测试。

渲染性能对比

通过实现相同的模板功能,分别针对无缓存和有缓存进行了对比测试. 测试方法,通过ab压测工具对页面进行测试,Node层收集页面render渲染时间, 然后进行汇总统计分析。

Nunjucks 测试模板

<div>
    <div>
        <h2>h5GameList</h2>
        <ul>
            {% for item in h5GameList.data.list %}
            <li><a href="{{item.downloadUrl}}">{{item.name}}-{{item.resume}}</a></li>
            {% endfor %}
        </ul>
    </div>

    <div>
        <h2>recommendList</h2>
        {% for item in recommendList.data %}
        <div>
            <a href="{{item.downloadUrl}}">{{item.recommendBriefResume}}</a>
            <p>{{item.recommendResume}}</p>
        </div>
        {% endfor %}
    </div>

    <div>
        <h2>bestList</h2>
        {% for item in bestList.data.list %}
        <div>
            <a href="{{item.downloadUrl}}">{{item.resume}}</a>
            <p>{{item.packageName}}</p>
        </div>
        {% endfor %}
    </div>
    <div>
        <h2>bookingList</h2>
        {% for item in bookingList.data %}
        <div>
            <a href="{{item.logoUrl}}">{{item.name}}-{{item.categoryName}}</a>
            <p>{{item.resume}}</p>
        </div>
        {% endfor %}
    </div>
</div>

Vue测试模板

<template>
  <div>
     <div>
       <h2>h5GameList</h2>
       <ul>
         <li  v-for="item in h5GameList.data.list" ><a v-bind:href="item.downloadUrl">{{item.name}}-{{item.resume}}</a></li>
       </ul>
     </div>
    <div>
      <h2>recommendList</h2>
      <div v-for="item in recommendList.data" >
        <a v-bind:href="item.downloadUrl">{{item.recommendBriefResume}}</a>
        <p>{{item.recommendResume}}</p>
      </div>
    </div>

    <div>
      <h2>bestList</h2>
      <div v-for="item in bestList.data.list" >
        <a v-bind:href="item.downloadUrl">{{item.resume}}</a>
        <p>{{item.packageName}}</p>
      </div>
    </div>
    <div>
      <h2>bookingList</h2>
      <div v-for="item in bookingList.data">
        <a v-bind:href="item.logoUrl">{{item.name}}-{{item.categoryName}}</a>
        <p>{{item.resume}}</p>
      </div>
    </div>
  </div>
</template>

测试脚本一:

ab -c 50 -n 1000 http://ip:port/perf/nunjucks/
ab -c 50 -n 1000 http://ip:port/perf/vue/
其中 -n 表示请求数,-c 表示并发数

ab-1000.png

测试脚本二:

ab -c 50 -n 5000 http://ip:port/perf/nunjucks/
ab -c 50 -n 5000 http://ip:port/perf/vue/

ab-5000.png

从上面统计来看可以得出如下结论:

global.num = 100; 
 
const vm = require('vm'); 
const code = 'var ret = num * num * num;'; 
const sandbox = { num : 1000}; 
const benchmark = (msg, fun) => { 
  const start = new Date; 
  for (let i = 0; i < 10000; i++) { 
    fun(); 
  } 
  const end = new Date; 
  console.log(msg + ': ' + (end - start) + 'ms'); 
}; 

const ctx = vm.createContext(sandbox); 
 
// runInThisContext 用于创建一个独立的沙箱运行空间,code内的代码可以访问外部的global对象,但是不能访问其他变量 
benchmark('vm.runInThisContext', () => { 
  vm.runInThisContext(code); 
}); 
 
// runInContext 创建一个独立的沙箱运行空间,sandBox将做为global的变量传入code内,但不存在global变量 
benchmark('vm.runInContext', () => { 
  vm.runInContext(code, ctx); 
}); 
 
// 与runInContext 一样, 这里可以直接传sandbox 
benchmark('vm.runInNewContext', () => { 
  vm.runInNewContext(code, sandbox); 
}); 
 
const script = vm.createScript(code); 
 
benchmark('script.runInThisContext', () => { 
  script.runInThisContext(); 
}); 
benchmark('script.runInNewContext', () => { 
  script.runInNewContext(sandbox); 
}); 
benchmark('script.runInContext', () => { 
  script.runInContext(ctx); 
}); 
benchmark('fn', () => { 
  new Function('num', code)(); 
}); 
 
/* 
 vm.runInThisContext: 15ms 
 vm.runInContext: 71ms 
 vm.runInNewContext: 70ms 
 script.runInThisContext: 7ms 
 script.runInNewContext: 59ms 
 script.runInContext: 57ms 
 fn: 9ms 
 
 script方式都比vm方式快 
 */ 

线上应用性能数据

首页内容有5-6屏内容,一次性渲染,部分组件动态加载。

首页模板编译时间
vue-render.png

从首页render-avg的统计来看,== 模板的编译时间非常的短,平均在24ms-27ms之间==,还有优化的空间。

首页访问链路时间
vue-perf.png

从整个链路时间来看rt(首屏时间) 可以看到, 平均首屏时间小于1s, 而render时间平均在30ms,在整个链路上面,==render的时间可以说是可以忽略的==,至少从上面图来看,==性能问题大头部分在于网络,接口耗时两部分。==

CPU和内存占用

前提条件:

Nunjucks CPU和内存占用

This is ApacheBench, Version 2.3 <$Revision: 1663405 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 100.84.250.56 (be patient)
Completed 5000 requests
Completed 10000 requests
Completed 15000 requests
Completed 20000 requests
Completed 25000 requests
Completed 30000 requests
Completed 35000 requests
Completed 40000 requests
Completed 45000 requests
Completed 50000 requests
Finished 50000 requests


Server Software:
Server Hostname:        100.84.250.56
Server Port:            7001

Document Path:          /perf/nunjucks/
Document Length:        13899 bytes

Concurrency Level:      100
Time taken for tests:   173.686 seconds
Complete requests:      50000
Failed requests:        48138
   (Connect: 0, Receive: 0, Length: 48138, Exceptions: 0)
Total transferred:      709284995 bytes
HTML transferred:       694684887 bytes
Requests per second:    287.88 [#/sec] (mean)
Time per request:       347.372 [ms] (mean)
Time per request:       3.474 [ms] (mean, across all concurrent requests)
Transfer rate:          3988.00 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0   10  38.1      2    3410
Processing:    22  336 313.5    241    3416
Waiting:       22  333 302.2    241    3398
Total:         56  347 311.6    247    3432

Percentage of the requests served within a certain time (ms)
  50%    247
  66%    280
  75%    332
  80%    367
  90%    645
  95%    877
  98%   1195
  99%   1460
 100%   3432 (longest request)
nunjucks-cpu-memory.png

Vue CPU和内存占用

This is ApacheBench, Version 2.3 <$Revision: 1663405 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 100.84.250.56 (be patient)
Completed 5000 requests
Completed 10000 requests
Completed 15000 requests
Completed 20000 requests
Completed 25000 requests
Completed 30000 requests
Completed 35000 requests
Completed 40000 requests
Completed 45000 requests
Completed 50000 requests
Finished 50000 requests


Server Software:
Server Hostname:        100.84.250.56
Server Port:            7001

Document Path:          /perf/vue/
Document Length:        13840 bytes

Concurrency Level:      100
Time taken for tests:   193.524 seconds
Complete requests:      50000
Failed requests:        48989
   (Connect: 0, Receive: 0, Length: 48989, Exceptions: 0)
Total transferred:      707135621 bytes
HTML transferred:       692535158 bytes
Requests per second:    258.37 [#/sec] (mean)
Time per request:       387.048 [ms] (mean)
Time per request:       3.870 [ms] (mean, across all concurrent requests)
Transfer rate:          3568.35 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0   10  27.3      2    1384
Processing:    22  377 223.9    285    2236
Waiting:       22  373 217.7    285    2235
Total:         42  386 219.5    290    2239

Percentage of the requests served within a certain time (ms)
  50%    290
  66%    335
  75%    409
  80%    481
  90%    697
  95%    841
  98%   1030
  99%   1126
 100%   2239 (longest request)dou
vue-cpu-memory.png

两个图对比发现如下信息:

Nunjucks与Vue对比分析

首先我们来看看 ab 执行结果的几个关键参数含义:

下面是 ab -c 100 -n 50000 针对 nunjucks 和 vue 数据对比:


image.png | center | 1021x206image.png | center | 1021x206

从上图 ab 对比数据可以得出以下结论:

总体上,nunjucks 和 vue 在 模板渲染,CPU使用,内存占用没有很明显的差异,各指标基本接近。 其中 nunjucks 在模板渲染方面略微快一点点(个位数毫秒级), 内存占用方面 vue 比 nunjucks 占用略微多一点,但都在可接受范围内。

CPU和内存工具

在进行 CPU 和 内存 监控统计分析时,也没有找到简单好用的火焰图工具。Alinode 平台统计粒度太大,数据也不是时时可以拿到,也不好使。找到一些成熟的工具比如 perf 和 FlameGraph 都比较复杂,而且一些资料都是 linux 上面的, 配置相当繁琐,只好放弃。另外找到 Mac 的一个小工具 iStat Menus 能显示电脑磁盘CPU,内存等占用情况不错,图也很小且不适合做具体分析,作为电脑监控工具还不错。最终也没有找到合适简单工具,只好简单撸一个,顺便玩了一把 socket.io 和图表工具。上面 CPU 和 内存 统计是通过 Egg egg-socket.ioegg-schedule 插件, current-processessocket.io.js 以及图片库 Ignite UI 实现的。

'use strict';
const ps = require('current-processes');
const os = require('os');
const totalMem = os.totalmem(); // bytes to MB

module.exports = app => {

  exports.schedule = {
    interval: '3s',
    type: 'worker'
  };

  exports.task = function* (ctx) {
    ps.get((err, processes) => {
      const proArr = processes.filter(pro => {
        return pro.name === 'node';
      }).sort((a, b) => {
        return a.pid - b.pid;
      });
      proArr.shift();
      proArr.shift();
      const cpu_mem_info = proArr.map(pro => {
        return {
          pid: pro.pid,
          cpuPercent: pro.cpu,
          totalMemory: totalMem,
          usedMemory: pro.mem.private, // RSS实际内存占用大小
          memoryPercent: pro.mem.usage * 100, // 进程占用内存百分比
          virtualMemory: pro.mem.virtual, // 虚拟内存占用大小
        };
      });
      ctx.app.io.emit('monitor-memory-cpu', cpu_mem_info);
    });
  };
  return exports;
};
var socket = io.connect("http://localhost:7001");
socket.on("monitor-memory-cpu", function (data) {
    data.forEach(info => {
      info.displayCPU = info.cpuPercent;
      info.displayMem = info.usedMemory;
      info.displayTime = new Date().toLocaleTimeString();
      cpuData.push(info);
      $("#cpuChart").igDataChart("notifyInsertItem", cpuData, cpuData.length - 1, info);
      $("#memoryChart").igDataChart("notifyInsertItem", cpuData, cpuData.length - 1, info);
    });
});
上一篇 下一篇

猜你喜欢

热点阅读