Egg: 请问如何配置代码修改后重启进程?

Created on 29 Mar 2017  ·  17Comments  ·  Source: eggjs/egg

为什么修改代码后不能自动重启;我试过了各种编辑器都不行;也查看了issues找不到答案;
配置了egg-development插件也不行?
难道必须修改一次代码就必须ctr+c,然后再运行一次 npm run dev?
没有livereload的功能么?

PR is welcome

Most helpful comment

刚才在 @atian25 的协助下重现了一下:

  1. 启动应用:

image

显示启动了 98249、98250、98251、98252 四个 worker。

  1. 浏览器访问:

image

接收请求的是 98252 这个 worker

  1. 更改内容引发重启,发现重启的就只有 98250、98251、98249:

image

  1. 刷新浏览器返回的还是 98252 ,用 ps aux | grep 98252 发现这个进程还活着:

image

  1. 把浏览器 kill 掉之后,这个进程才 disconnect 。

整理一下,复现步骤:

  1. egg-init --type=simple showcase 初始化项目,在controller 里面返回 pid
  2. node index.js 启动多个 worker
  3. 用浏览器(chrome, Safari, chrome 隐身模式) 访问页面
  4. 修改 controller 的输出,在控制台看到重启了,workers 列表中已经没有它,但这个 pid 没有 disconnect 信息
  5. 此时刷新浏览器,显示的内容还是原来的 pid
  6. 同时刻用 curl 请求,显示是新的 pid
  7. ps 这个 pid,一直存在。

关于:https://nodejs.org/api/cluster.html#cluster_worker_disconnect
相关问题: https://github.com/nodejs/node/issues/2642#issuecomment-229753639

All 17 comments

第一个问题请提交最小可复现代码。另外你确定修改的是后端代码吧?

第二个问题,livereload 是刷新前端,原来 koa 怎么弄现在就怎么弄

简单描述一下吧,可能对续看官有点帮助
项目使用egg-bin自动生成;选择的是“”simple“”
确定编辑改动是后台代码;因为,我只是对这个框架做后端service provider,而且屏蔽掉了static插件;
所以不存在前端代码的问题;
刚开始用的是sumlime编辑器,应该不存在"write_safe"的问题;
而且别的任何编辑器的文件改动都不会触发进程重启;
我没有再reproduce一遍这个过程;
然后,如果不用egg-bin自动生成项目,而是手动搭建的话,则问题不存在;
先这样,有问题再报告;
close this isuue right now

对这两个项目做下 diff?

@atian25 发现我本机也是会复现,用 egg-init 初始化的项目。

我在 cluster-reload 中的重启 worker 的逻辑上打印了kill掉的 worker pid:

for (var i = 1; i < aliveWorkers.length; i++) {
    worker = aliveWorkers[i];
    // console.log('worker %s %s', worker.id, worker.state);
    worker.kill(KILL_SIGNAL);
    console.log('kill worker' + worker.process.pid);
  }

并且在 middleware 上也加了打印当前 pid 的逻辑:

module.exports = function(app) {
  return function* (next) {
    console.log(process.pid);
    console.log('1133');
    yield next;
  }
};

然后测试发现,明明 pid 为 86556 的 worker 被 kill 掉了,但是在重新请求的时候,发现接收响应的还是 86556

kill worker86556
kill worker86557
kill worker86558
2017-03-30 20:08:17,749 INFO 86314 [master] App Worker#13:86586 start, state: none, current workers: ["9","13","14","15","16"]
2017-03-30 20:08:17,751 INFO 86314 [master] App Worker#14:86587 start, state: none, current workers: ["9","13","14","15","16"]
2017-03-30 20:08:17,753 INFO 86314 [master] App Worker#15:86588 start, state: none, current workers: ["9","13","14","15","16"]
2017-03-30 20:08:17,753 INFO 86314 [master] App Worker#16:86589 start, state: none, current workers: ["9","13","14","15","16"]
2017-03-30 20:08:17,755 INFO 86314 [master] App Worker#11:86557 disconnect, suicide: true, state: disconnected, current workers: ["9","13","14","15","16"]
[Thu Mar 30 2017 20:08:17 GMT+0800 (CST)] [cfork:master:86314] worker:86557 disconnect (suicide: true, state: disconnected, isDead: false)
[Thu Mar 30 2017 20:08:17 GMT+0800 (CST)] [cfork:master:86314] don't fork new work (refork: false)
[Thu Mar 30 2017 20:08:17 GMT+0800 (CST)] [cfork:master:86314] worker:86558 disconnect (suicide: true, state: disconnected, isDead: false)
[Thu Mar 30 2017 20:08:17 GMT+0800 (CST)] [cfork:master:86314] don't fork new work (refork: false)
2017-03-30 20:08:17,759 INFO 86314 [master] App Worker#12:86558 disconnect, suicide: true, state: disconnected, current workers: ["9","13","14","15","16"]
2017-03-30 20:08:17,758 WARN 86557 [app_worker] App Worker exit with signal SIGTERM
2017-03-30 20:08:17,762 WARN 86558 [app_worker] App Worker exit with signal SIGTERM
[Thu Mar 30 2017 20:08:17 GMT+0800 (CST)] [cfork:master:86314] worker:86557 exit (code: 0, suicide: true, state: dead, isDead: true, isExpected: true)
[Thu Mar 30 2017 20:08:17 GMT+0800 (CST)] [cfork:master:86314] worker:86558 exit (code: 0, suicide: true, state: dead, isDead: true, isExpected: true)
2017-03-30 20:08:19,844 WARN 86314 [master] App Worker#13:86586 started at 3000, remain 0 (296757ms)
2017-03-30 20:08:19,888 WARN 86314 [master] App Worker#15:86588 started at 3000, remain 0 (296801ms)
2017-03-30 20:08:19,903 WARN 86314 [master] App Worker#16:86589 started at 3000, remain 0 (296815ms)
2017-03-30 20:08:19,905 WARN 86314 [master] App Worker#14:86587 started at 3000, remain 0 (296818ms)
2017-03-30 20:08:19,948 WARN 86555 [app_worker] App Worker exit with signal SIGTERM
2017-03-30 20:08:19,955 INFO 86314 [master] App Worker#9:86555 disconnect, suicide: true, state: disconnected, current workers: ["13","14","15","16"]
[Thu Mar 30 2017 20:08:19 GMT+0800 (CST)] [cfork:master:86314] worker:86555 disconnect (suicide: true, state: disconnected, isDead: false)
[Thu Mar 30 2017 20:08:19 GMT+0800 (CST)] [cfork:master:86314] don't fork new work (refork: false)
[Thu Mar 30 2017 20:08:19 GMT+0800 (CST)] [cfork:master:86314] worker:86555 exit (code: 0, suicide: true, state: dead, isDead: true, isExpected: true)
86556
1133

然后过了五六分钟,才显示 86556 这个 worker 被干掉了,然后刷新才是新的内容。

86556
1133
[Thu Mar 30 2017 20:11:42 GMT+0800 (CST)] [cfork:master:86314] worker:86556 disconnect (suicide: true, state: disconnected, isDead: false)
[Thu Mar 30 2017 20:11:42 GMT+0800 (CST)] [cfork:master:86314] don't fork new work (refork: false)
2017-03-30 20:11:42,019 INFO 86314 [master] App Worker#10:86556 disconnect, suicide: true, state: disconnected, current workers: ["13","14","15","16"]
2017-03-30 20:11:42,020 WARN 86556 [app_worker] App Worker exit with signal SIGTERM

也就是 worker 不能被及时杀掉,导致没有即时更新。而且在 mac 上,貌似 cluster 的负载均衡也是没效果的,多次接收请求的 worker 都是同一个,所以才会有更改文件后不生效的问题。

然后我在 cluster-reload/index.js 中,把杀掉 worker 的逻辑改成:

process.kill(worker.process.pid, KILL_SIGNAL);

让 master 来把 worker kill 掉,就可以即时把 worker 干掉了。

cc @eggjs/core

刚才在 @atian25 的协助下重现了一下:

  1. 启动应用:

image

显示启动了 98249、98250、98251、98252 四个 worker。

  1. 浏览器访问:

image

接收请求的是 98252 这个 worker

  1. 更改内容引发重启,发现重启的就只有 98250、98251、98249:

image

  1. 刷新浏览器返回的还是 98252 ,用 ps aux | grep 98252 发现这个进程还活着:

image

  1. 把浏览器 kill 掉之后,这个进程才 disconnect 。

整理一下,复现步骤:

  1. egg-init --type=simple showcase 初始化项目,在controller 里面返回 pid
  2. node index.js 启动多个 worker
  3. 用浏览器(chrome, Safari, chrome 隐身模式) 访问页面
  4. 修改 controller 的输出,在控制台看到重启了,workers 列表中已经没有它,但这个 pid 没有 disconnect 信息
  5. 此时刷新浏览器,显示的内容还是原来的 pid
  6. 同时刻用 curl 请求,显示是新的 pid
  7. ps 这个 pid,一直存在。

关于:https://nodejs.org/api/cluster.html#cluster_worker_disconnect
相关问题: https://github.com/nodejs/node/issues/2642#issuecomment-229753639

我也是用 egg-init 的 simple 项目,很容易出现无法自动更新的情况,目前只能手动关闭,再重启

@dingyiming 开发的时候建议用 npm run dev,或者在 index.js 中的参数加上 workers: 1,单个 worker 就不会出现这个问题。

哦哦,不过,我就是用的 npm run dev, index.js 在dev的命令下,有用到吗? 而且 workers 的确是 1

image

@dingyiming run dev不会用到 index.js,不过 run dev 也是只会启动一个 worker,你那里单个 worker 也会出现?windows系统?

恩恩,是的 windows 系统,会出现无法自动更新的情况,而且后面请求发出来,控制台已经不打印任何东西了

@whxaxes 删除重装下依赖,再看看能否复现

既然一个 work 无法重现那就忽略这个问题吧,本地开发用的 cluster-reload 本身就是用比较暴力的方式重启,一直想把这个给去掉。

如果还是其他讨论的可以再打开。

可以再看下 cluster-reload 的实现,先保留第一个 worker 响应请求,然后 fork 一个 worker,当这个 worker 完成启动后(listening)再 kill 原来的第一个 worker,所以还是有时间差的。

@popomore
@guzuomuse
放弃egg的reload,定义一个启动文件,使用nodemon启动,
需要做如下代码处理:
cluster-client/lib/connection.js

_handleSocketError(err) {  
    // 返回true,不触发任何错误  
    return true;  
    this._lastError = err;  
    if (err.code === 'ECONNRESET') {
      this.logger.warn('[ClusterClient:Connection] socket is closed by other side while there were still unhandled data in the socket buffer');
    } else {
      this.emit('error', err);
    }
  }

cluster-client/lib/client.js

function reloadWorker(info) {
    if (!config.reloadOnDebug) {
      return;
    }
    if (isAssetsDir(info.path) || info.isDirectory) {
      return;
    }
    // don't reload if don't match
    if (config.reloadPattern && multimatch(info.path, config.reloadPattern).length === 0) {
      return;
    }
    logger.warn(`[agent:development] reload worker because ${info.path} ${info.event}`);
    // 注释掉这部分代码,不刷新worker
    // process.send({
    //   to: 'master',
    //   action: 'reload-worker',
    // });
  }

@popomore
@guzuomuse
放弃egg的reload,定义一个启动文件,使用nodemon启动,
需要做如下代码处理:
cluster-client/lib/connection.js

_handleSocketError(err) {  
    // 返回true,不触发任何错误  
    return true;  
    this._lastError = err;  
    if (err.code === 'ECONNRESET') {
      this.logger.warn('[ClusterClient:Connection] socket is closed by other side while there were still unhandled data in the socket buffer');
    } else {
      this.emit('error', err);
    }
  }

cluster-client/lib/client.js

function reloadWorker(info) {
    if (!config.reloadOnDebug) {
      return;
    }
    if (isAssetsDir(info.path) || info.isDirectory) {
      return;
    }
    // don't reload if don't match
    if (config.reloadPattern && multimatch(info.path, config.reloadPattern).length === 0) {
      return;
    }
    logger.warn(`[agent:development] reload worker because ${info.path} ${info.event}`);
    // 注释掉这部分代码,不刷新worker
    // process.send({
    //   to: 'master',
    //   action: 'reload-worker',
    // });
  }

请问能详细解释一下如何定义启动文件吗?

Was this page helpful?
0 / 5 - 0 ratings