防止" mux_client_request_session的最佳方法:从主节点读取失败:管道损坏" ssh错误

时间:2017-05-14 03:33:12

标签: perl ssh-tunnel

我有一个perl脚本,它创建一个ssh隧道,并在其上设置一个Perl DBI连接,以查询远程主机上的数据库:

1 my $ssh = Net::OpenSSH->new('me@host.com');
2 $pid = $ssh->spawn({ssh_opts => '-L 127.0.0.1:12345:127.0.0.1:3306'}, 'sleep 3');

3 return DBI->connect($dsn, $db_user, $db_pass);

大约有80%到90%的时间可以使用,但其余的时间我在尝试连接数据库时遇到此错误:

  

mux_client_request_session:从主节点读取失败:管道损坏

在对此进行故障排除的过程中,我注意到如果我在使用usleep (10000)的第2行后非常简短地睡眠程序,它会在100%的时间内正常工作。我不确定为什么会这样,但我很好奇,以及如何正确解决问题。

感谢。

1 个答案:

答案 0 :(得分:3)

您的代码中存在争用条件:spawn运行在后台启动隧道的ssh命令,有时该命令比启动TCP套接字的DBI connect更快,有时它不是,所以connect失败。

无论如何,我通常建议创建隧道的方法是使用控制命令:

 $ssh->system({ssh_opts => [-O => 'forward', '-L3066:localhost:3066']})

如果要在建立与数据库的连接后删除隧道:

 $ssh->system({ssh_opts => [-O => 'cancel', '-L3066:localhost:3066']});

另外,请注意,只要您想使用数据库连接,就必须使Net::OpenSSH对象保持活动状态。

总结:

sub dbi_connect {
    my $mysql_port = 3066;
    my $local_port = 12345;
    my $dsn = "DBI:mysql:database=$database;host=localhost;port=$local_port";
    my $tunnel = join ':', $local_port, 'localhost', $mysql_port;
    my $ssh = Net::OpenSSH->new($host, ...);
    $ssh->system({ssh_opts => [-O => 'forward', "-L$tunnel"]});
    my $dbi = DBI->connect($dsn, $db_user, $db_pass);
    $ssh->system({ssh_opts => [-O => 'cancel', "-L$tunnel"]});
    return ($dbi, $ssh);
}

另请参阅PerlMonks的this post