NodeJS On 运行-Time Adding/Removing/Reloading 需要不重启服务器(也没有 nodemon)
NodeJS On Run-Time Adding/Removing/Reloading requires WITHOUT server restart (No nodemon either)
我有一个工作项目,我基本上是在创建一种 CMS 形式,我们将随着时间的推移向其添加应用程序。
我们遇到的问题是让这些应用程序在 运行 时间在服务器内加载(并更具体地修改)。
我们需要这种形式的 "hot loading" 的原因是因为我们不希望在进行更改时重新启动服务器,更具体地说,我们希望添加新的应用程序通过管理面板。
Nodemon 是一个非常有用的开发工具,但对于我们的生产环境,我们希望能够替换现有的应用程序(或者 module/plugin,如果你愿意)而不必重新启动服务器(无论是手动还是通过 nodemon,服务器需要一直 运行ning)。
您可以将其与 Drupal、Yoomla 或 Wordpress 等 CMS 的工作方式进行比较,但出于我们的需要,出于多种原因,我们认为 Node 是更好的选择。
代码明智,我正在寻找这样的东西,但它会起作用:
let applications = []
//add a new application through the web interface calling the appropiate class method, within the method the following code runs:
applications.push(require('path/to/application');
//when an application gets modified:
applications.splice(index,1);
applications.push('path/to/application');
但我还需要调整所述应用程序的现有实例。
示例:
// file location: ./Applications/application/index.js
class application {
greet() {
console.log("Hello");
}
}
module.exports = application;
应用程序加载器将在所述应用程序中加载:
class appLoader {
constructor() {
this.List = new Object();
}
Add(appname) {
this.List[appname] = require(`./Applications/${appname}/index`);
}
Remove(appname) {
delete require.cache[require.resolve(`./Applications/${appname}/index`)]
delete this.List[appname];
}
Reload(appname) {
this.Remove(appname);
this.Add(appname);
}
}
运行宁码:
const AppLoader = require('appLoader');
const applications = new AppLoader();
applications.add('application'); // adds the application created above
var app = new applications.List['application']();
app.greet();
// Change is made to the application file, .greet() now outputs "Hello World" instead of "Hello"
//do something to know it has to reload, either by fs.watch, or manual trigger
applications.Reload('application');
app.greet();
预期的行为是:
Hello
Hello World
实际上,我得到:
Hello
Hello
如果有人能帮我想出一种方法来动态加载这样的应用程序,而且还可以在 运行 时间内 remove/reload 它们,我将不胜感激!
编辑:如果有一种方法 运行 我的应用程序代码无需使用允许动态 load/reload/remove 的要求,那也是一个受欢迎的解决方案
好的,感谢@jfriend00,我意识到我需要用我的代码修复一些其他的东西,所以他的评论对其他人仍然有用。至于我卸载所需模块或在不重启服务器的情况下重新加载它们的问题,我想出了一个相对优雅的方法来实现它。
首先让我向您展示我的所有测试 class 和 app.js,然后我将解释我做了什么以及它是如何工作的。
Class.js:
"use strict";
class Class {
constructor() {
// this.file will be put in comments post run-time, and this.Output = "Hey" will be uncommented to change the source file.
var date = new Date()
this.Output = date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds() + "." + date.getMilliseconds();
this.file = global.require.fs.readFileSync('./file.mov');
//this.Output = "Hey";
}
}
module.exports = Class;
app.js:
'use strict';
global.require = {
fs: require('fs')
};
const arr = [];
const mod = './class.js'
let Class = [null];
Class[0] = require(mod);
let c = [];
c.push(new Class[0]());
console.log(c[0].Output);
console.log(process.memoryUsage());
setTimeout(() => {
delete require.cache[require.resolve(mod)];
delete Class[0];
Class[0] = require(mod);
console.log(Class)
delete c[0];
c[0] = new Class[0]();
console.log(c[0].Output);
console.log(process.memoryUsage());
}, 10000);
现在让我在这里稍微解释一下,请注意,这是测试代码,所以命名很糟糕。
我是这样上班的:
第 1 步
我需要一种方法来分离所需的模块(如 fs、websocket、express 等),这样它就不会混淆整个 delete require_cache()
部分代码,我的解决方案是在全球范围内制作这些模块要求:
global.required = {
fs: require('fs')
}
第 2 步
找出一种方法来确保垃圾收集器删除 unloaded 代码,我通过将我的 require 和 class 声明放在一个变量中来实现这一点,这样我可以在 Node/Javascript 中使用 delete
功能。 (我在我的测试代码中使用了 let
因为我事先测试了另一种方法,还没有测试 const
是否会再次起作用)。
我还制作了一个包含文件路径字符串的变量(在本例中为 './Class.js' 但为了下面的解释,我将按原样编写)
let Class = [null] //this declares an array that has an index '0'
Class[0] = require('./Class');
let c = [new Class[0]()] // this declares an array that has the class instantiated inside of index '0'
至于垃圾回收,我只能做到以下几点:
delete Class[0];
delete c[0];
在此之后,我能够重做所需 class 的声明,随后重做 class 本身,并保持我的代码正常工作而无需重新启动。
请记住,他在实际项目中需要大量工作才能实现,但您可以通过向 class 添加一个 unload() 方法来将其拆分以卸载底层自定义 classes。但我的初步测试表明这很有魅力!
编辑:我觉得需要注意,如果没有 jfriend00 的评论,我永远不会想出这个解决方案
输出
项目启动时,输出当前时间和process.memoryUsage()
13:49:13.540
{ rss: 50343936,
heapTotal: 7061504,
heapUsed: 4270696,
external: 29814377 }
在 10 秒的等待期间,我将 Class.js
文件更改为不读取 file.mov
并说 "Hey" 而不是时间,在 10 秒超时后,这是输出:
Hey
{ rss: 48439296,
heapTotal: 7585792,
heapUsed: 4435408,
external: 8680 }
我有一个工作项目,我基本上是在创建一种 CMS 形式,我们将随着时间的推移向其添加应用程序。
我们遇到的问题是让这些应用程序在 运行 时间在服务器内加载(并更具体地修改)。
我们需要这种形式的 "hot loading" 的原因是因为我们不希望在进行更改时重新启动服务器,更具体地说,我们希望添加新的应用程序通过管理面板。
Nodemon 是一个非常有用的开发工具,但对于我们的生产环境,我们希望能够替换现有的应用程序(或者 module/plugin,如果你愿意)而不必重新启动服务器(无论是手动还是通过 nodemon,服务器需要一直 运行ning)。
您可以将其与 Drupal、Yoomla 或 Wordpress 等 CMS 的工作方式进行比较,但出于我们的需要,出于多种原因,我们认为 Node 是更好的选择。
代码明智,我正在寻找这样的东西,但它会起作用:
let applications = []
//add a new application through the web interface calling the appropiate class method, within the method the following code runs:
applications.push(require('path/to/application');
//when an application gets modified:
applications.splice(index,1);
applications.push('path/to/application');
但我还需要调整所述应用程序的现有实例。
示例:
// file location: ./Applications/application/index.js
class application {
greet() {
console.log("Hello");
}
}
module.exports = application;
应用程序加载器将在所述应用程序中加载:
class appLoader {
constructor() {
this.List = new Object();
}
Add(appname) {
this.List[appname] = require(`./Applications/${appname}/index`);
}
Remove(appname) {
delete require.cache[require.resolve(`./Applications/${appname}/index`)]
delete this.List[appname];
}
Reload(appname) {
this.Remove(appname);
this.Add(appname);
}
}
运行宁码:
const AppLoader = require('appLoader');
const applications = new AppLoader();
applications.add('application'); // adds the application created above
var app = new applications.List['application']();
app.greet();
// Change is made to the application file, .greet() now outputs "Hello World" instead of "Hello"
//do something to know it has to reload, either by fs.watch, or manual trigger
applications.Reload('application');
app.greet();
预期的行为是:
Hello
Hello World
实际上,我得到:
Hello
Hello
如果有人能帮我想出一种方法来动态加载这样的应用程序,而且还可以在 运行 时间内 remove/reload 它们,我将不胜感激!
编辑:如果有一种方法 运行 我的应用程序代码无需使用允许动态 load/reload/remove 的要求,那也是一个受欢迎的解决方案
好的,感谢@jfriend00,我意识到我需要用我的代码修复一些其他的东西,所以他的评论对其他人仍然有用。至于我卸载所需模块或在不重启服务器的情况下重新加载它们的问题,我想出了一个相对优雅的方法来实现它。 首先让我向您展示我的所有测试 class 和 app.js,然后我将解释我做了什么以及它是如何工作的。
Class.js:
"use strict";
class Class {
constructor() {
// this.file will be put in comments post run-time, and this.Output = "Hey" will be uncommented to change the source file.
var date = new Date()
this.Output = date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds() + "." + date.getMilliseconds();
this.file = global.require.fs.readFileSync('./file.mov');
//this.Output = "Hey";
}
}
module.exports = Class;
app.js:
'use strict';
global.require = {
fs: require('fs')
};
const arr = [];
const mod = './class.js'
let Class = [null];
Class[0] = require(mod);
let c = [];
c.push(new Class[0]());
console.log(c[0].Output);
console.log(process.memoryUsage());
setTimeout(() => {
delete require.cache[require.resolve(mod)];
delete Class[0];
Class[0] = require(mod);
console.log(Class)
delete c[0];
c[0] = new Class[0]();
console.log(c[0].Output);
console.log(process.memoryUsage());
}, 10000);
现在让我在这里稍微解释一下,请注意,这是测试代码,所以命名很糟糕。
我是这样上班的:
第 1 步
我需要一种方法来分离所需的模块(如 fs、websocket、express 等),这样它就不会混淆整个 delete require_cache()
部分代码,我的解决方案是在全球范围内制作这些模块要求:
global.required = {
fs: require('fs')
}
第 2 步
找出一种方法来确保垃圾收集器删除 unloaded 代码,我通过将我的 require 和 class 声明放在一个变量中来实现这一点,这样我可以在 Node/Javascript 中使用 delete
功能。 (我在我的测试代码中使用了 let
因为我事先测试了另一种方法,还没有测试 const
是否会再次起作用)。
我还制作了一个包含文件路径字符串的变量(在本例中为 './Class.js' 但为了下面的解释,我将按原样编写)
let Class = [null] //this declares an array that has an index '0'
Class[0] = require('./Class');
let c = [new Class[0]()] // this declares an array that has the class instantiated inside of index '0'
至于垃圾回收,我只能做到以下几点:
delete Class[0];
delete c[0];
在此之后,我能够重做所需 class 的声明,随后重做 class 本身,并保持我的代码正常工作而无需重新启动。
请记住,他在实际项目中需要大量工作才能实现,但您可以通过向 class 添加一个 unload() 方法来将其拆分以卸载底层自定义 classes。但我的初步测试表明这很有魅力!
编辑:我觉得需要注意,如果没有 jfriend00 的评论,我永远不会想出这个解决方案
输出
项目启动时,输出当前时间和process.memoryUsage()
13:49:13.540
{ rss: 50343936,
heapTotal: 7061504,
heapUsed: 4270696,
external: 29814377 }
在 10 秒的等待期间,我将 Class.js
文件更改为不读取 file.mov
并说 "Hey" 而不是时间,在 10 秒超时后,这是输出:
Hey
{ rss: 48439296,
heapTotal: 7585792,
heapUsed: 4435408,
external: 8680 }