Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/364.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 使用承诺处理来自ssh2的成功/错误响应_Javascript_Node.js_Ssh_Promise - Fatal编程技术网

Javascript 使用承诺处理来自ssh2的成功/错误响应

Javascript 使用承诺处理来自ssh2的成功/错误响应,javascript,node.js,ssh,promise,Javascript,Node.js,Ssh,Promise,我正在构建一个node.js应用程序,在生产中它将充当许多服务器的SSH客户端,其中一些服务器可能在任何给定时间都无法访问。我试图编写一个函数,在启动时尝试在每个客户机的配置中运行SSH命令,但我无法处理成功的会话和以错误结束的会话。我用承诺包装了一个ssh2客户机。如果我删除了第三个(垃圾)服务器,并且只有成功的结果,这就可以了!请参见输出: STDOUT: Hello World STDOUT: Hello World Session closed Session closed Succ

我正在构建一个node.js应用程序,在生产中它将充当许多服务器的SSH客户端,其中一些服务器可能在任何给定时间都无法访问。我试图编写一个函数,在启动时尝试在每个客户机的配置中运行SSH命令,但我无法处理成功的会话和以错误结束的会话。我用承诺包装了一个ssh2客户机。如果我删除了第三个(垃圾)服务器,并且只有成功的结果,这就可以了!请参见输出:

STDOUT: Hello World

STDOUT: Hello World

Session closed
Session closed
Successful session: Hello World,Hello World
但是,如果其中一个连接超时,即使我处理了错误,我也无法保留任何数据。错误消息似乎覆盖了所有已解决的承诺

Successful session: Error: Timed out while waiting for handshake,Error: 
Timed out while waiting for handshake,Error: Timed out while waiting 
for handshake
这是我的代码,如果这有点分散,请原谅,因为为了这个问题,我结合了一些模块。我的目标是保留成功会话中的数据,并优雅地处理失败

var Client = require('ssh2').Client;

 const labs = {
    "ny1": "192.168.1.2",
    "ny2": "192.168.1.3",
    "ny3": "1.1.1.1"
};

function checkLabs() {
    let numLabs = Object.keys(labs).length;
    let promises = [];

    for(i=0;i<numLabs;i++){
        let labName = Object.keys(labs)[i];
        promises.push(asyncSSH("echo 'Hello World'", labs[labName]));
    }

    Promise.all(promises.map(p => p.catch(e => e)))
        .then(results => console.log("Successful session: " + results))
        .catch(e => console.log("Error! " + e));
}

var sendSSH = function (command, dest, callback) {
var conn = new Client();

        conn.on('ready', function() {
            return conn.exec(command, function(err, stream) {
                if (err) throw err;
                stream.on('data', function(data) {
                    callback(null, data);
                    console.log('STDOUT: ' + data);
                }).stderr.on('data', function(data){
                    callback(err);
                    console.log('STDERR: ' + data);
                }).on('close', function(err) {
                    if(err) {
                        console.log('Session closed due to error');
                    } else {
                        console.log('Session closed');
                    }
                });
                stream.end('ls -l\nexit\n');
            });
        }).on('error', function(err){
            callback(err);
        }).connect({
            host: dest,
            port: 22,
            username: 'root',
            readyTimeout: 10000,
            privateKey: require('fs').readFileSync('link-to-my-key')
        });
};

function asyncSSH(command, dest) {
    return new Promise(function(resolve, reject){
        sendSSH(command, dest, function(err,data) {
            if (!err) {
                resolve(data);
            } else {
                reject(err);
            }
        });
    });
}

checklabs();
var-Client=require('ssh2')。客户端;
常数实验室={
“ny1”:“192.168.1.2”,
“ny2”:“192.168.1.3”,
“ny3”:“1.1.1.1”
};
函数checkLabs(){
设numLabs=Object.keys(labs).length;
让承诺=[];
对于(i=0;i p.catch(e=>e)))
.then(results=>console.log(“成功会话:+results))
.catch(e=>console.log(“Error!”+e));
}
var sendsh=函数(命令、目标、回调){
var conn=新客户端();
连接on('ready',function(){
返回conn.exec(命令、函数(错误、流){
如果(错误)抛出错误;
stream.on('data',函数(data){
回调(空,数据);
log('STDOUT:'+数据);
}).stderr.on('data',函数(data){
回调(err);
log('STDERR:'+数据);
}).on('close',函数(err){
如果(错误){
log('由于错误而关闭了会话');
}否则{
console.log(“会话已关闭”);
}
});
stream.end('ls-l\nexit\n');
});
}).on('error',函数(err){
回调(err);
}).连接({
主持人:dest,,
港口:22,
用户名:'根',
readyTimeout:10000,
privateKey:require('fs')。readFileSync('link-to-my-key'))
});
};
函数asynchssh(命令,dest){
返回新承诺(功能(解决、拒绝){
sendsh(命令、目标、函数(错误、数据){
如果(!err){
解析(数据);
}否则{
拒绝(错误);
}
});
});
}
checklabs();

如何更好地使用此承诺包装器来处理来自ssh2客户端的任何错误?任何提示都非常感谢。

要从每个连接中获得最佳使用,您可以(也可以说应该)单独承诺:

  • 每个Client()实例的安装
  • 每个实例的
    conn.exec()
    方法(以及所需的任何其他异步方法)
这将允许使用不同的命令重用
Client()
的每个实例(尽管在本例中不是必需的)

您还应该确保在完成每个套接字的工作后,通过调用
client.end()
断开其连接。为此,建议采用“处置器模式”

考虑到这些观点和一些假设,我得出以下结论:

var Client = require('ssh2').Client;

const labs = {
    "ny1": "192.168.1.2",
    "ny2": "192.168.1.3",
    "ny3": "1.1.1.1"
};

function checkLabs() {
    let promises = Object.keys(labs).map((key) => {
        return withConn(labs[key], (conn) => { 
            return conn.execAsync("echo 'Hello World'")
            .catch((e) => "Error: " + e.message); // catch in order to immunise the whole process against any single failure.
                                                  // and inject an error message into the success path.
        });
    });
    Promise.all(promises)
    .then(results => console.log("Successful session: " + results))
    .catch(e => console.log("Error! " + e.message)); // with individual errors caught above, you should not end up here.
}

// disposer pattern, based on https://stackoverflow.com/a/28915678/3478010
function withConn(dest, work) {
    var conn_;
    return getConnection(dest).then((conn) => {
        conn_ = conn;
        return work(conn);
    }).then(() => {
        if(conn_) {
            conn_.end(); // on success, disconnect the socket (ie dispose of conn_).
        }
    }, () => {
        if(conn_) {
            conn_.end(); // on error, disconnect the socket (ie dispose of conn_).
        }
    });
    // Note: with Bluebird promises, simplify .then(fn,fn) to .finally(fn).
}

function getConnection(dest) {
    return new Promise((resolve, reject) => {
        let conn = promisifyConnection(new Client());
        conn.on('ready', () => {
            resolve(conn);
        })
        .on('error', reject)
        .connect({
            host: dest,
            port: 22,
            username: 'root',
            readyTimeout: 10000,
            privateKey: require('fs').readFileSync('link-to-my-key')
        });
    });
}

function promisifyConnection(conn) {
    conn.execAsync = (command) => { // promisify conn.exec()
        return new Promise((resolve, reject) => {
            conn.exec(command, (err, stream) => {
                if(err) {
                    reject(err);
                } else {
                    let streamSegments = []; // array in which to accumulate streamed data
                    stream.on('close', (err) => {
                        if(err) {
                            reject(err);
                        } else {
                            resolve(streamSegments.join('')); // or whatever is necessary to combine the accumulated stream segments
                        }
                    }).on('data', (data) => {
                        streamSegments.push(data);
                    }).stderr.on('data', function(data) {
                        reject(new Error(data)); // assuming `data` to be String
                    });
                    stream.end('ls -l\nexit\n'); // not sure what this does?
                }
            });
        });
    };
    // ... promisify any further Client methods here ...
    return conn;
}
注:

  • conn.exec()
    的建议包括一个假设,即数据可以在一系列段(例如数据包)中接收。如果此假设无效,则不再需要
    streamSegments
    数组
  • getConnection()
    promisifyConnection()
    可以作为一个函数编写,但是使用单独的函数更容易看到发生了什么
  • getConnection()
    promisifyConnection()
    将所有乱七八糟的东西远离应用程序代码

变量conn似乎不知从何而来,只是神奇地存在。不确定为什么要定义
labs
而不是
const labs={…
您只能解析或拒绝一个承诺一次,但解析多次,因为on data发生多次。这不会导致错误,但在第一次解析之后的任何解析调用都将被忽略。相反,您应该在close和on data上解析保存数组中的任何数据,以便在close时使用该数组进行解析。这看起来不像是任何可以实际运行并演示问题的代码。@HMR这非常有用,谢谢。我在一个单独的文件中使用了define方法作为配置,也许我应该将其称为const。另外,如果我删除了ssh2代码并在psuedo代码中演示了success/err,这将作为演示更有用。I猜测处理ssh2行为是我的问题的核心,我需要花更多的时间处理该部分。为了将问题提高一点,我这里的问题是我需要使用同一段ssh2代码进行多个事务。每个事务都已关闭,或者成功关闭,在这种情况下,
(“数据”
API导致
close
打开('error'
在连接失败的情况下。每个事务可能需要10秒的超时时间。我不在乎它们是同步发生的还是异步发生的,我只需要保存每个事务的数据,并优雅地处理发生的任何错误,恢复任何剩余的ssh会话。看起来有点奇怪,您应该走得更远作为“Hello World,Hello World”,只有一个
客户端实例()
。我原以为您需要(a)每个主机一个实例,或者(b)执行串联连接。如果回显消息包含一些独特的内容,例如
ny1
/
ny2
键,会发生什么情况。这很好,非常感谢您的设计。Th