2017-05-14 1 views
2

J'ai un script perl qui crée un tunnel ssh et établit une connexion Perl DBI à travers elle pour interroger une base de données sur un hôte distant:Meilleure façon d'éviter "mux_client_request_session: lecture du maître a échoué: erreurs ssh tronquées"

1 my $ssh = Net::OpenSSH->new('[email protected]'); 
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); 

cela fonctionne environ 80 à 90% du temps, mais le reste du temps je reçois cette erreur en essayant de se connecter à la base de données:

mux_client_request_session: lire maître a échoué: Broken pipe

En cours de dépannage cela, j'ai remarqué que si je dors le programme très brièvement après la ligne 2 avec usleep (10000), cela fonctionne 100% du temps. Je ne suis pas sûr pourquoi c'est mais je suis curieux savoir et comment je peux résoudre correctement le problème.

Merci.

Répondre

3

Il y a une condition de course dans votre code: spawn exécute la commande ssh qui démarre le tunnel sur l'arrière-plan, parfois cette commande est plus rapide que DBI connect à partir du socket TCP, il est parfois pas et si connect échoue.

Quoi qu'il en soit, la façon dont je recommande généralement de créer des tunnels est d'utiliser une commande de contrôle:

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

Si vous souhaitez supprimer le tunnel une fois que la connexion à la base de données a été établie:

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

Notez également que vous devez conserver l'objet Net::OpenSSH actif tant que vous souhaitez utiliser la connexion DB.

En résumé:

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

Voir aussi this post à PerlMonks.

+0

Merci. Qu'est-ce qui se passe dans le $ dsn? Aussi, puisque je ne suis pas sur Windows, je suppose que j'utilise 3306 pour le port mysql au lieu de 4022? – StevieD

+0

'$ dsn' devrait pointer vers le côté local du tunnel. J'ai inclus un exemple ci-dessus pour mysql. – salva

+0

Merci. Je vais tester cela dès que j'ai une chance. – StevieD