如何使用nodejs有效地将请求转发到多个端点?

时间:2019-07-09 03:28:09

标签: node.js rest

我构建了一个nodejs服务器来充当适配器服务器,该服务器在接收到包含某些数据的发布请求后,从请求主体中提取数据,然后将其转发给其他一些外部服务器。最后,我的服务器将发送一个包含每个外部服务器响应的响应(成功/失败)。

如果只有1个端点要转发,这似乎很简单。但是,当我必须转发到多个服务器时,我必须依赖Promise.All()之类的东西,它具有快速响应的行为。这意味着,如果一个承诺被拒绝(外部服务器已关闭),所有其他承诺也将立即被拒绝,其余服务器将无法接收我的数据。

4 个答案:

答案 0 :(得分:4)

这可能不是确切的解决方案。但是,我发布的内容可能是解决您的问题的方法。

几天前,我遇到了同样的问题,因为我想实现API版本控制。这是我实现的解决方案,请看看。

Architecture Diagram

让我解释一下这个图

在此图中,是我们所做的服务器的初始配置。所有到达这里的api请求都将传递到发布目录中的“ index.js”文件。

index.js(在发行目录中)

const express = require('express');

const fid = require('./core/file.helper');

const router = express.Router();

fid.getFiles(__dirname,'./release').then(releases => {
    releases.forEach(release => {
        // release = release.replace(/.js/g,''); 
        router.use(`/${release}`,require(`./release/${release}/index`))
    })
})

module.exports = router

helper.js的代码段

//requiring path and fs modules
const path = require('path');
const fs = require('fs');

module.exports = {
    getFiles: (presentDirectory, directoryName) => {

        return new Promise((resolve, reject) => {
            //joining path of directory 
            const directoryPath = path.join(presentDirectory, directoryName);
            //passsing directoryPath and callback function

            fs.readdir(directoryPath, function (err, files) {

                // console.log(files);

                //handling error
                if (err) {
                    console.log('Unable to scan directory: ' + err);
                    reject(err)
                }
                //listing all files using forEach
                // files.forEach(function (file) {
                //     // Do whatever you want to do with the file
                //     console.log(file); 
                // });
                resolve(files)
            });
        })


    }
}

现在,从该索引文件中映射每个版本文件夹中的所有index.js

以下是v1或v2中“ index.js”的代码...

const express = require('express');
const mongoose = require('mongoose');
const fid = require('../../core/file.helper');
const dbconf = require('./config/datastore');
const router = express.Router();

// const connection_string = `mongodb+srv://${dbconf.atlas.username}:${dbconf.atlas.password}@${dbconf.atlas.host}/${dbconf.atlas.database}`;
const connection_string = `mongodb://${dbconf.default.username}:${dbconf.default.password}@${dbconf.default.host}:${dbconf.default.port}/${dbconf.default.database}`;

mongoose.connect(connection_string,{
    useCreateIndex: true,    
    useNewUrlParser:true
}).then(status => {

    console.log(`Database connected to mongodb://${dbconf.atlas.username}@${dbconf.atlas.host}/${dbconf.atlas.database}`);

    fid.getFiles(__dirname,'./endpoints').then(files => {

        files.forEach(file => {
            file = file.replace(/.js/g,''); 
            router.use(`/${file}`,require(`./endpoints/${file}`))
        });

    })

}).catch(err => {
    console.log(`Error connecting database ${err}`);
})

module.exports = router

在版本文件夹中的每个index.js中,实际上都映射到端点文件夹中的每个端点。

以下是其中一个端点的

代码

const express = require('express');
const router = express.Router();

const userCtrl = require('../controllers/users');


router.post('/signup', userCtrl.signup);
router.post('/login', userCtrl.login);

module.exports = router;

实际上,在此文件中,我们正在将端点连接到其控制器。

答案 1 :(得分:0)

var config = {'targets':
            [
                'https://abc.api.xxx',
                'https://xyz.abc',
                'https://stackoverflow.net'
            ]};
relay(req, resp, config);               
function relay(req, resp, config) {
    doRelay(req, resp, config['targets'], relayOne);
}

function doRelay(req, resp, servers, relayOne) {
    var finalresponses = [];
    if (servers.length > 0) {
        var loop = function(servers, index, relayOne, done) {
            relayOne(req, servers[index], function(response) {
                finalresponses.push[response];
                if (++index < servers.length) {
                    setTimeout(function(){
                        loop(servers, index, relayOne, done);
                    }, 0);
                } else {
                    done(resp, finalresponses);
                }
            });
        };
        loop(servers, 0, relayOne, done);
    } else {
        done(resp, finalresponses);
    }
}

function relayOne(req, targetserver, relaydone) {
//call the targetserver and return the response data
/*return relaydone(response data);*/
}

function done(resp, finalresponses){
    console.log('ended');
    resp.writeHead(200, 'OK', {
        'Content-Type' : 'text/plain'
    });
    resp.end(finalresponses);
    return;
}

答案 2 :(得分:-1)

听起来您正在尝试设计反向代理。如果您在努力使自定义代码正常工作,则有一个免费的npm库,它非常强大。

我建议使用node-http-proxy

我在下面发布了链接,由于您在问题中提到了对API格式的修改,因此您可以直接转到“修改响应”。不过,请务必阅读整个页面。

https://github.com/http-party/node-http-proxy#modify-a-response-from-a-proxied-server

注意:该库也非常好,因为它可以支持SSL,并且可以同时代理localhost(同一台计算机上的服务器)和其他计算机上的服务器(远程)。

答案 3 :(得分:-1)

Promise.all()来自MDN

  

它拒绝的原因是第一个承诺被拒绝。

要解决该问题,您需要catch()进行每个请求。 例如

Promise.all([
    request('<url 1>').catch(err => /* .. error handling */),
    request('<url 2>').catch(err => /* .. error handling */),
    request('<url 3>').catch(err => /* .. error handling */)
])
.then(([result1, result2, result3]) => {

    if(result1.err) { }

    if(result2.err) { }

    if(result3.err) { }

})