Nodejs -当节点崩溃时如何防止丢失?

eit6fx6z  于 2022-12-03  发布在  Node.js
关注(0)|答案(3)|浏览(193)

我有一个快速的应用程序,用户可以动态创建子域,其中代理网站。
用户可能会创建数千个唯一的代理,但如果应用程序出错、崩溃或重新启动,则这些数据将丢失。

var app = require('express')();
var proxy = require('express-http-proxy');
var vhost = require('vhost');

app.get('/create', function(req, res){
    app.use(vhost('subdomain.mysite.com', proxy('http://example.com')));
    res.send('Created');
});

app.listen(8080);

我知道我可以把这些存储在一个数据库中,然后循环遍历并重新创建每个代理,但这似乎不是一个可靠的解决方案,因为可能有成千上万个唯一创建的代理。
我知道这些新创建的路线存储在应用程序变量(app._router)中。有没有办法从其他来源获取路线?(redis?mongo?)
或者,是否有一种方法可以持久保存此路由信息?
或者,是否有任何节点管理工具(PM2、forever、supervisor等)可以防止或恢复此类事件?
或者有更好的解决方案吗?任何建议都是感激的。

nimxete2

nimxete21#

显然,您需要以一种在服务器进程崩溃后仍然存在的方式来持久化这些信息(您似乎已经知道了这一点)。
在架构上,您可以通过以下几种方式来实现这一点:
1.保留当前架构,但在代理状态发生更改时添加“保存到磁盘”步骤(添加、删除或修改)。因此,无论何时进行更改,您都要将路由的整个当前状态写入磁盘。为此,您需要添加一个新的基于RAM的数据结构,用于保存您创建的所有路由的当前状态。您可以尝试从Express中读取此信息,但坦率地说,我宁愿维护我自己的数据结构,因为它允许我精确地保存我想要的属性。这将允许你在服务器启动时读取你保存的配置/状态文件,并循环通过保存的状态来重新创建你上次拥有的状态。如果你在每次进行更改时都保存这个状态,最糟糕的情况是您可能会丢失将要保存的操作。您必须确保在保存操作中有适当的并发保护,这样两个保存操作就不会相互影响。这并不难做到。
1.切换到数据库体系结构,这样每次进行更改时,都将该更改写入数据库(使用持久性存储的数据库)。然后,在服务器重新启动的任何时候,都可以从数据库读取状态,并重新创建服务器重新启动前的状态。
数据库可能更具可伸缩性,但每次更改时将整个状态写入磁盘可能更简单(不需要数据库,只需将状态写入磁盘即可(JSON可能是最简单的读写方式)。任何一种方法都可以在一定的规模内正常工作,超过此规模后,数据库解决方案就更有意义了(根据每秒更改数或要跟踪的代理总数进行扩展)。
我知道我可以把这些存储在一个数据库中,然后循环遍历并重新创建每个代理,但这似乎不是一个可靠的解决方案,因为可能有成千上万个唯一创建的代理。
我想你可能搞错了。一个数据库可能更适合大量的代理服务器,尽管数千个代理服务器并不是一个特别大的数字。你可能可以用上面的任何一种技术来处理这个规模。
我知道这些新创建的路线存储在应用程序变量(app._router)中。有没有办法从其他来源获取路线?(redis?mongo?)
如果这是我的应用程序,我会在每次添加、删除或修改新代理时将数据保存到一个永久存储中,而不使用Express来跟踪任何东西。
或者,是否有一种方法可以持久保存此路由信息?
Express没有任何我所知道的自动持久化路由的特性。在Express架构中,假设您的启动代码或后续代码将创建它所需要的路由处理程序。您可以在创建这些路由时自己持久化信息。
或者,是否有任何节点管理工具(PM2、forever、supervisor等)可以防止或恢复此类事件?
据我所知没有。那些工具帮助管理过程本身,但不是它的内部状态。
或者有更好的解决方案吗?任何建议都是感激的。
您自己保存数据,然后在服务器启动时从保存的存放区重新建立状态,如上述两个选项所述。
下面是上面第一个选项的示例,它只在每次进行更改时保存数据,然后在服务器启动时读取保存的状态:

const app = require('express')();
const proxy = require('express-http-proxy');
const vhost = require('vhost');
const Promise = require('bluebird');
const fs = Promise.promisify(require('fs'));

let proxyData = [];
readProxyData();

app.get('/create', function(req, res){
    app.use(vhost('subdomain.mysite.com', proxy('http://example.com')));

    // save proxy data
    proxyData.push({subDomain: 'subdomain.mysite.com', userDomain: 'http://example.com'})
    saveProxyData();

    res.send('Created');
});

app.listen(8080);

// call this anytime a new proxy has been added 
// (after the proxy info is added to the proxyData data structure)
function saveProxyData() {
     // use a promise to automatically sequence successive saves
     // makes a pending save wait for the current one to finish
     proxyData.promise = proxyData.promise.then(function() {
         return fs.writeFileAsync("proxyState.json", JSON.stringify(proxyData));
     }).catch(function(err) {
         // log save state error, but allow promise to continue so
         // subsequent saves will continue uninterrupted
         console.err(err);
         return;
     });
}

// only to be called upon server startup
function readProxyData() {
    try {
        proxyData = require("proxyState.json");
    } catch(err) {
        console.err("Error reading proxyState.json - continuing with no saved state: ", err);
    }
    // set initial promise state (used for chaining consecutive writes)
    proxyData.promise = Promise.resolve();

    // establish any previously existing proxies saved in the proxyData
    proxyData.forEach(function(item) {
        app.use(vhost(item.subDomain, proxy(item.userDomain)));
    });
}
ippsafx7

ippsafx72#

我认为你只需要捕捉到未处理的错误/异常。你可以processuncaughtException这是你的应用程序不会退出,如果发生任何错误

process.on('uncaughtException', function (err) {
  console.log('Caught exception: ' + err);
});
unftdfkk

unftdfkk3#

一旦进程结束,工作内存中保存的所有数据都会丢失。一个常见的解决方案是在服务器收到数据后,立即将其写入一个更持久的系统(数据库、文件系统等)。
我最近编写了一个npm包cashola,它试图使这一点尽可能简单。
这就是使用cashola的示例。

var cashola = require('cashola');
var app = require('express')();
var proxy = require('express-http-proxy');
var vhost = require('vhost');

var state = cashola.rememberArraySync('state');

for (const item of state) {
    app.use(vhost(item.domain, proxy(item.proxy)));
}

app.get('/create', function(req, res){
    const vhostDomain = 'subdomain.mysite.com';
    const proxyDomain = 'http://example.com';
    state.push({ domain: vhostDomain, proxy: proxyDomain });
    app.use(vhost(vhostDomain, proxy(proxyDomain)));
    res.send('Created');
});

app.listen(8080);

相关问题