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

时间:2016-09-19 00:58:13

标签: node.js express proxy express-vhost

我有一个快速应用程序,用户可以动态创建代理网站的子域。

用户可以创建数千个独特的代理,但是如果应用程序错误和崩溃或重启然后这些数据就会丢失。

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变量(app._router)上。有没有办法从其他来源获取路线?(redis?mongo?)

或者有办法保留此路由信息吗?

或者,任何节点管理工具(PM2,永远,主管等)是否可以阻止或从这类事件中恢复?

或者有更好的解决方案吗?任何建议表示赞赏。

2 个答案:

答案 0 :(得分:0)

显然,您需要以服务器进程崩溃(您似乎已经知道)的方式保留此信息。保存这类信息的常用方法是将其保存到持久存储(通常是磁盘)。

在架构上,您有几种方法可以做到这一点:

  1. 保留当前的体系结构,但添加一个"保存到磁盘"任何时候更改代理状态(添加或删除或修改一个)的步骤。因此,无论何时进行更改,您都可以将整个当前的路由状态写入磁盘。为此,您需要添加一个新的基于RAM的数据结构,该结构保存您已创建的所有路由的当前状态。您可以尝试从Express中读取此内容,但坦率地说,我宁愿维护自己的数据结构,因为它允许我保留我想要的属性。这将允许您在服务器启动时读取已保存的配置/状态文件并循环保存状态以重新创建您上次拥有的状态。如果每次更改时都保存此状态,则可能丢失的最差是即将保存的操作。您必须确保在保存操作中具有适当的并发保护,因此两个保存不会相互踩踏。这并不难。

  2. 切换到数据库体系结构,因此每次进行更改时,都会将一个更改写入数据库(使用持久性存储的数据库)。然后,在服务器重新启动的任何时候,您都可以从数据库中读取状态并重新创建服务器重新启动之前的状态。

  3. 数据库可能更具可扩展性,但每次更改时写入整个磁盘状态可能更简单(不需要数据库,只需将状态写入磁盘(可能JSON最简单的读写)。精确到一定规模,之后数据库解决方案更有意义(根据每秒更改或代理总数来跟踪)。

      

    我知道我可以将它们存储在数据库中,然后循环遍历和   重新创造每一个,但这似乎不是一个坚实的解决方案   可能有数千个独特创建的代理。

    我认为你倒退了。对于大量代理,数据库可能更具可扩展性,尽管数千个数量不是特别大。您可以使用上述任一技术处理该大小。

      

    我知道这些新创建的路线存储在app变量中   (app._router)。有没有办法从另一个获得路线   来源?(redis?mongo?)

    如果这是我的应用程序,每次添加,删除或修改新代理并且不使用Express跟踪任何内容时,我会自己将数据保留到永久存储。

      

    或者有办法保留此路由信息吗?

    Express没有任何我知道自动保留路由的功能。在快速体系结构中假设您的启动代码或后续代码将创建它需要的路由处理程序。您可以在创建这些路径时自行保留信息。

      

    或者做任何节点管理工具(PM2,永远,主管等)   防止或从这类事物中恢复过来?

    不是我知道的。这些工具有助于管理流程本身,但不管理其内部状态。

      

    或者有更好的解决方案吗?任何建议表示赞赏。

    自己保留数据,然后在服务器启动时从持久存储中重新创建状态,如上面两个选项中所述。

    以上是第一个选项的示例,它只是在每次更改时保存数据,然后在服务器启动时读取保存的状态:

    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)));
        });
    }
    

答案 1 :(得分:0)

我认为你只需要捕获未处理的错误/异常。如果发生任何错误,您process uncaughtException您的应用可能不会退出

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