nodejs实践录:pm2实验测试记录

本文是测试在运行时更新ecosystem配置文件的记录。

源码

服务端

/*
express简单示例
*/
//const log = require('../lib/log.js');
const http = require('http');
const express = require('express');

var cluster = require('cluster');
var os      = require('os');

// const g_port = 4000;
const g_port = process.env["SERVER_PORT"];
const g_name = process.env["NODE_ENV"];

function test1()
{
app = express();

const server = http.createServer(app);

// 监听端口
server.listen(g_port, () => {
  console.log(`111Express is listening on ${g_port}`);
});

// 访问首页,显示当时时间
app.get('/', function (req, res) {
    console.log('111 got request from ...',  req.url);
    str = 'Hello World! now is: 
'; myDate = new Date(); myDate.toLocaleString(); str += myDate; res.send(str); }); process.on('SIGINT', () => { console.log('111 got sigint...\n'); const cleanUp = () => { }; // 如果服务器关闭,延时2秒退出 server.close(() => { // Stop after 10 secs setTimeout(() => { cleanUp(); process.exit(); }, 2000); }); // Force close server after 15 secs setTimeout((e) => { console.log('Forcing server close !!!', e); cleanUp(); process.exit(1); }, 3000); }); } var g_cnt = 0; // 这里创建worker是否必要? // 解答:使用pm2的cluster模式,就不用手动写代码 function test2() { var numCPUs = 2; //os.cpus().length; if (cluster.isMaster) { // Master: // Let's fork as many workers as you have CPU cores for (var i = 0; i < numCPUs; ++i) { cluster.fork(); } } else { // Worker: // Let's spawn a HTTP server // (Workers can share any TCP connection. // In this case its a HTTP server) console.log(`111 httpserver is listening on ${g_port}`); http.createServer(function(req, res) { res.writeHead(200); console.log('got request from ...', req.url); str = 'here is my voice: Hello World! now is: \n'; myDate = new Date(); myDate.toLocaleString(); str += myDate; g_cnt++; var cnt = ' cnt: ' + g_cnt.toString() + '\n'; str += cnt; res.end(str); }).listen(g_port); } } // 该函数不使用worker方式 function test3() { console.log(`httpserver[${g_name}] is listening on ${g_port}`); http.createServer(function(req, res) { res.writeHead(200); console.log('111 got request from ...', req.url); str = '111 here is my voice: Hello World! now is: \n'; myDate = new Date(); myDate.toLocaleString(); str += myDate; g_cnt++; var cnt = ' cnt: ' + g_cnt.toString() + '\n'; str += cnt; res.end(str); }).listen(g_port); } // main函数 function main() { //test1(); //test2(); test3(); } main();

客户端


/*
用于测试http服务器连接性,每0.5秒发送GET请求,如连接不上,返回错误,打印err
*/

var request = require('request');

const log = require('../lib/log.js');

const g_port = 4000;

function httpGet(url, requestData)
{
    var head = {
        url: url,
        method: "GET",
        json: true,
        headers: {
            "content-type": "application/json",
            'User-Agent': 'git-oschina-hook',
            'X-Gitee-Token': 'webhook password',
            'X-Gitee-Event': 'Push Hook'
        },
        body: requestData
        // body: JSON.stringify(requestData)
    };

    request(head, function fn(error, response, body) {
        if (!error && response.statusCode == 200) {
            log.print('=============GET content: \n', body) // 成功
        }else{
            log.print('err');
        }
    });
}

function getHttp()
{
    var url = 'http://127.0.0.1:'+ g_port;
    var msg = new Object();
    msg['devid'] = '123456798';
    msg['station'] = '01';
    msg['stateCode']=='501';
    //log.print('will get the http.');
    httpGet(url, msg);
}

var g_loopId = null;

function startLoop()
{
    g_loopId = setInterval(loopFunc, 500);
}

function stopLoop()
{
    clearInterval(g_loopId);
}

function loopFunc()
{
    getHttp();
}

function main()
{
    log.print('start looping...');
    startLoop();
}

main();

配置文件ecosystem.config.js

module.exports = {
  // APP是一个数组,可以有多个
  // 参数参考:https://pm2.io/doc/en/runtime/reference/ecosystem-file/
  apps : [
    {
    name: 'app',
    script: 'server.js',
    args: 'null',
    instances: 1,
    //exec_mode: "cluster", // 集群模式,如不指定,默认为fork
    autorestart: false,
    min_uptime: "60s",
    max_restarts: 3,
    watch: false,
    //error_file: "./logs/app-err.log",
    //out_file: "./logs/app-out.log",
    log: "./logs/app.log",
    //log_date_format: "YYYY-MM-DD HH:mm Z", // pm2 log添加日期
    max_memory_restart: '1G',
    //listen_timeout: 3000,
    kill_timeout: 3000,
    // wait_ready: true,
    env:
    {
      NODE_ENV: 'development',
      SERVER_PORT: "4000",
    },
    env_production:
    {
      NODE_ENV: 'production1',
      SERVER_PORT: "4000",
    }
  }
  ]
};

使用

启动服务端:

pm2 start

使用客户端连接,并观察输出日志:

node client.js

测试使用的命令:

重启pm2配置文件
pm2 reload ecosystem.config.js
重启实例
pm2 reload 

测试项

下面分别进行不同的测试,大部分只写结论。

更新env中的字段值

可行。

如果修改的是env_production,则:

pm2 reload ecosystem.config.js --env production

另外在命令后面,加上不加上 --update-env ,好像没影响。

更新非env的字段

使用pm2 reload,没效果,不成功。

新加实例

原来只有一个app,再加上一个app,但要修改名称和端口号,其它可相同。使用pm2 reload,可以看到有新的实例运行。

减少实例

原来有2个app,减少一个。
使用pm2 reload,没效果,不成功。

实例改名

原来app改名,端口没变化:
会启动新的实例,但提示端口被占用(因为没改)。
结论:只要名称不一样,pm2就认为是新的实例,要启动。打印信息会提示[PM2][WARN] Applications app1 not running, starting...之类。所以,在mp2运行过程中,有一种方式“更新”实例,先将旧的实例停止(否则会提示占用端口号),将旧实例配置参数复制一份,将实例名称改为新的名称。再reload配置文件。更好的方法请参考下面示例。

删除实例再启动

pm2 del 1 (实例ID为1)
pm2 reload ecosystem.config.js

此时服务端的实例ID为2,1已经不存在了,但服务名称是一致的。

删除实例,修改实例,再启动

同上,可以修改pm2配置文件,也生效。

结论

1、pm2可以在原有基础上新加实例,直接reload ecosystem.config.js即可启动新的实例。
2、通过pm2 delpm2 reload可以修改某个实例的所有内容(注:只是理论,笔者主要关注log、端口等参数)。
3、如果要一切复原,终极办法是使用pm2 kill

李迟 2019.2.25 周一中午

你可能感兴趣的:(nodejs)