1

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

用户可能会创建数以千计的唯一代理,但如果应用程序出错和崩溃或重新启动,那么这些数据就会丢失。

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、forever、supervisor 等)防止此类事情发生或从中恢复?

还是有更好的解决方案?任何建议表示赞赏。

4

2 回答 2

0

显然,您需要以一种能够在服务器进程崩溃(您似乎已经知道)中幸存下来的方式保存这些信息。像这样持久化信息的常用方法是将其保存到持久存储(通常是磁盘)中。

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

  1. 保留您当前的架构,但只要代理状态发生更改(添加、删除或修改),请添加“保存到磁盘”步骤。因此,无论何时进行更改,您都将整个当前状态的路由写入磁盘。为此,您将添加一个新的基于 RAM 的数据结构,用于保存您创建的所有路由的当前状态。您可以尝试从 Express 中阅读此内容,但坦率地说,我宁愿为此维护自己的数据结构,因为它允许我准确地保留我想要的属性。这将允许您在服务器启动时读取您保存的配置/状态文件并遍历保存的状态以重新创建您上次拥有的状态。如果每次进行更改时都保存此状态,那么您可能会丢失的最坏情况是即将保存的操作。您必须确保在保存操作中具有适当的并发保护,这样两个保存就不会相互影响。这并不难做到。

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

数据库可能更具可扩展性,但每次更改时将整个状态写入磁盘可能更简单(不需要数据库,只需将您的状态写入磁盘(可能 JSON 最容易读写)。两者都可以正常工作一定规模之后,数据库解决方案更有意义(根据每秒更改或要跟踪的代理总数来衡量)。

我知道我可以将这些存储在数据库中,然后循环遍历并重新创建每个,但这对于可能数千个独特创建的代理来说似乎不是一个可靠的解决方案。

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

我知道这些新创建的路由存储在 app 变量 (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)));
    });
}
于 2016-09-19T01:38:07.077 回答
0

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

process.on('uncaughtException', function (err) {
  console.log('Caught exception: ' + err);
});
于 2016-09-19T04:23:58.483 回答