標籤:-- target 返回 ons att details 從庫 訊息 article
邊看MHA源碼邊學Perl語言之三 NodeUtil.pmNodeUtil.pm源碼分析
MHA的代碼分為mha4mysql-manager(管理節點)和mha4mysql-node(資料庫節點)兩部分,可能有些人認為mha4mysql-node只需要安裝在資料庫節點就可以了,但通過源碼可看出,在管理點節也是需要安裝node節點,因為在manager節點也會調用NodeUtil中的方法.以下為加過comment的mha4mysql-node的代碼:
#!/usr/bin/env perl# Copyright (C) 2011 DeNA Co.,Ltd.## This program is free software; you can redistribute it and/or modify# it under the terms of the GNU General Public License as published by# the Free Software Foundation; either version 2 of the License, or# (at your option) any later version.## This program is distributed in the hope that it will be useful,# but WITHOUT ANY WARRANTY; without even the implied warranty of# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the# GNU General Public License for more details.## You should have received a copy of the GNU General Public License# along with this program; if not, write to the Free Software# Foundation, Inc.,# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA# 聲明包名package MHA::NodeUtil;# Perl 浪起來太可怕了,所以建議使用嚴格文法格式use strict;use warnings FATAL => ‘all‘;# 引入package,如果在Perl的lib路徑中找不到相應的檔案,則會報錯use Carp qw(croak);use MHA::NodeConst;use File::Path;use Errno();# 當目錄不存時,建立此目錄.sub create_dir_if($) { my $dir = shift; #判斷目錄不存時才建立 if ( !-d $dir ) { eval { print "Creating directory $dir.. "; mkpath($dir); print "done.\n"; }; # [email protected]為eval命令的錯誤訊息.如果為空白,則表示上一次eval命令執行成功 if ([email protected]) { # 細節處理: [email protected] 是全域的,為了防止影響到其它程式,所有先把值賦給局部變數 $e 後,再 undef 進行重設 my $e = [email protected]; undef [email protected]; # 目錄已存在,這裡邏輯有點問題,因為已經有判斷目錄不存在才會到這裡 if ( -d $dir ) { print "ok. already exists.\n"; } else { # 輸出錯誤資訊,使用croak會輸出指令碼名稱,代碼位置等有用的調試資訊,更方便的找到問題 croak "failed to create dir:$dir:$e"; } } }}# 對比本機和遠程主機檔案是否一致# 參數1:本機檔案# 參數2:遠程主機上的檔案# 參數3:ssh user# 參數4:遠程主機IP# 參數5:遠程主機port,預設為 22# 傳回值:# 1:檔案不存在# 2:檔案不一致# 0:檔案一致# Compare file checksum between local and remote hostsub compare_checksum { my $local_file = shift; my $remote_path = shift; my $ssh_user = shift; my $ssh_host = shift; my $ssh_port = shift; # 預設連接埠號碼 $ssh_port = 22 unless ($ssh_port); my $local_md5 = `md5sum $local_file`; return 1 if ($?); chomp($local_md5); $local_md5 = substr( $local_md5, 0, 32 ); my $ssh_user_host = $ssh_user . ‘@‘ . $ssh_host; my $remote_md5 =`ssh $MHA::NodeConst::SSH_OPT_ALIVE -p $ssh_port $ssh_user_host \"md5sum $remote_path\"`; return 1 if ($?); chomp($remote_md5); $remote_md5 = substr( $remote_md5, 0, 32 ); return 2 if ( $local_md5 ne $remote_md5 ); return 0;}# 本地檔案複製到遠程主機上# 參數:略sub file_copy { my $to_remote = shift; my $local_file = shift; my $remote_file = shift; my $ssh_user = shift; my $ssh_host = shift; my $log_output = shift; my $ssh_port = shift; $ssh_port = 22 unless ($ssh_port); my $ssh_user_host = $ssh_user . ‘@‘ . $ssh_host; my ( $from, $to ); if ($to_remote) { $from = $local_file; $to = "$ssh_user_host:$remote_file"; } else { $to = $local_file; $from = "$ssh_user_host:$remote_file"; } my $max_retries = 3; my $retry_count = 0; my $copy_fail = 1; my $copy_command = "scp $MHA::NodeConst::SSH_OPT_ALIVE -P $ssh_port $from $to"; if ($log_output) { $copy_command .= " >> $log_output 2>&1"; } while ( $retry_count < $max_retries ) { if ( system($copy_command) || compare_checksum( $local_file, $remote_file, $ssh_user, $ssh_host, $ssh_port ) ) { my $msg = "Failed copy or checksum. Retrying.."; if ($log_output) { system("echo $msg >> $log_output 2>&1"); } else { print "$msg\n"; } $retry_count++; } else { $copy_fail = 0; last; } } return $copy_fail;}# 拆分錯誤碼為高8位和低8位sub system_rc($) { my $rc = shift; my $high = $rc >> 8; my $low = $rc & 255; return ( $high, $low );}# 當檔案不存時,建立一個空檔案sub create_file_if { my $file = shift; if ( $file && ( !-f $file ) ) { open( my $out, ">", $file ) or croak "$!:$file"; close($out); }}# 當檔案存在時,刪除一個檔案sub drop_file_if($) { my $file = shift; if ( $file && -f $file ) { unlink $file or croak "$!:$file"; }}# 解析host的IP地址sub get_ip { my $host = shift; my ( $bin_addr_host, $addr_host ); if ( defined($host) ) { $bin_addr_host = gethostbyname($host); unless ($bin_addr_host) { croak "Failed to get IP address on host $host!\n"; } $addr_host = sprintf( "%vd", $bin_addr_host ); return $addr_host; } return;}# 擷取系統目前時間,精確到秒sub current_time() { my ( $sec, $min, $hour, $mday, $mon, $year ) = localtime(); $mon += 1; $year += 1900; my $val = sprintf( "%d-%02d-%02d %02d:%02d:%02d", $year, $mon, $mday, $hour, $min, $sec ); return $val;}# 檢查manager的版本,低於node版本時,直接報錯並退出.sub check_manager_version { my $manager_version = shift; if ( $manager_version < $MHA::NodeConst::MGR_MIN_VERSION ) { croak"MHA Manager version is $manager_version, but must be $MHA::NodeConst::MGR_MIN_VERSION or higher.\n"; }}# 從字串中解析出mysql的版本號碼,只取數字,便於比較版本大小# $str = "SELECT VERSION() AS Value"sub parse_mysql_version($) { my $str = shift; my $result = sprintf( ‘%03d%03d%03d‘, $str =~ m/(\d+)/g ); return $result;}# 從字串中解析出mysql的主要版本號,只取數字,便於比較版本大小# $str = "SELECT VERSION() AS Value"sub parse_mysql_major_version($) { my $str = shift; my $result = sprintf( ‘%03d%03d‘, $str =~ m/(\d+)/g ); return $result;}# 比較$my_version是否高於$target_version# mysql 主從結構 從庫版本要高於主庫版本sub mysql_version_ge { my ( $my_version, $target_version ) = @_; my $result = parse_mysql_version($my_version) ge parse_mysql_version($target_version) ? 1 : 0; return $result;}# shell需轉義的特殊字元數組my @shell_escape_chars = ( ‘"‘, ‘!‘, ‘#‘, ‘&‘, ‘;‘, ‘`‘, ‘|‘, ‘*‘, ‘?‘, ‘~‘, ‘<‘, ‘>‘, ‘^‘, ‘(‘, ‘)‘, ‘[‘, ‘]‘, ‘{‘, ‘}‘, ‘$‘, ‘,‘, ‘ ‘, ‘\x0A‘, ‘\xFF‘);# 反轉義shell的特殊字元sub unescape_for_shell { my $str = shift; if ( !index( $str, ‘\\\\‘ ) ) { return $str; } foreach my $c (@shell_escape_chars) { my $x = quotemeta($c); my $pattern = "\\\\(" . $x . ")"; $str =~ s/$pattern/$1/g; } return $str;}# 轉義shell的特殊字元sub escape_for_shell { my $str = shift; my $ret = ""; foreach my $c ( split //, $str ) { my $x = $c; my $escape = 0; foreach my $e (@shell_escape_chars) { if ( $e eq $x ) { $escape = 1; last; } } if ( $x eq "‘" ) { $x =~ s/‘/‘\\‘‘/; } if ( $x eq "\\" ) { $x = "\\\\"; } if ($escape) { $x = "\\" . $x; } $ret .= "$x"; } $ret = "‘" . $ret . "‘"; return $ret;}# 轉義mysql_command的特殊字元sub escape_for_mysql_command { my $str = shift; my $ret = ""; foreach my $c ( split //, $str ) { my $x = $c; if ( $x eq "‘" ) { $x =~ s/‘/‘\\‘‘/; } $ret .= $x; } $ret = "‘" . $ret . "‘"; return $ret;}# mysql終端命令的預先處理sub client_cli_prefix { my ( $exe, $bindir, $libdir ) = @_; croak "unexpected client binary $exe\n" unless $exe =~ /^mysql(?:binlog)?$/; my %env = ( LD_LIBRARY_PATH => $libdir ); my $cli = $exe; if ($bindir) { if ( ref $bindir eq "ARRAY" ) { $env{‘PATH‘} = $bindir; } elsif ( ref $bindir eq "" ) { $cli = escape_for_shell("$bindir/$exe"); } } for my $k ( keys %env ) { if ( my $v = $env{$k} ) { my @dirs = ref $v eq "ARRAY" ? @{$v} : ( ref $v eq "" ? ($v) : () ); @dirs = grep { $_ && !/:/ } @dirs; if (@dirs) { $cli = "$k=" . join( ":", ( map { escape_for_shell($_) } @dirs ), "\$$k" ) . " $cli"; } } } # $cli .= " --no-defaults"; return $cli;}1;
NodeUtil.pm小結
通過上一節,我們已經對perl有了基本的瞭解,相信看在NodeUtil的源碼時,就會輕鬆很多,裡面都是一些顧名思義的方法,具體調用執行個體代碼就留給各位看官自由發揮了.
唯一一個值得提及的重點是perl的Regex處理,但這個網上已經有相當然的教程,這裡就不重複去講解了,給大家兩個地址:
入門版:
http://www.runoob.com/perl/perl-regular-expressions.html
詳細版:
http://blog.csdn.net/eroswang/article/details/1812528
邊看MHA源碼邊學Perl語言之三 NodeUtil.pm