邊看MHA源碼邊學Perl語言之二 ManagerUtil

來源:互聯網
上載者:User

標籤:詳細   sql   eve   put   inux   app   分析日誌   package   nta   

邊看MHA源碼邊學Perl語言之二ManagerUtil.pmMHA版本

為了讓大家有一個共同的代碼學習環境,特別從網路找了mha4mysql-manager-0.56,mha4mysql-node-0.56穩定版作為學習和研究對象,大家可以到直接到github上進行clone:

https://github.com/mysql-dev-fun/mha4mysql-manager-0.56https://github.com/mysql-dev-fun/mha4mysql-node-0.56

Window推薦用phpstrom + perl plugin進行代碼查看志分析,測試環境推薦直接在Linux下直接執行。

我的基本環境
ip 說明
192.168.0.110 mha4mysql-manager
192.168.0.100 mysql主庫
192.168.0.101   mysql從庫一
192.168.0.102 mysql從庫二

 

MYSQL主從環境搭建

這裡就省略了,大家可以參考大師兄的部落格。

http://www.cnblogs.com/gomysql/p/3675429.html

MHA安裝

參考官方手冊:https://github.com/yoshinorim/mha4mysql-manager/wiki/Installation

(官方的最新版可能會出現各種坑,所以建議大家用穩定版進行學習)。

參考安裝步驟:

#安裝依賴 ssh to 192.168.0.110$ yum -y install perl-CPAN perl-devel$ yum -y install perl-ExtUtils-MakeMaker$ yum -y install perl-ExtUtils-Embed$ yum -y install perl-ExtUtils-CBuilder perl-ExtUtils-MakeMaker$ yum -y install rrdtool perl-rrdtool rrdtool-devel perl-Params-Validate#下載學習使用的源碼$ cd /usr/local/src$ git clone https://github.com/mysql-dev-fun/mha4mysql-manager-0.56.git#編譯安裝$ cd mha4mysql-manager-0.56$ perl -MCPAN -e "install Config::Tiny"$ perl -MCPAN -e "install Log::Dispatch"$ perl -MCPAN -e "install Parallel::ForkManager"$ perl Makefile.PL$ make$ sudo make install
MHA & Perl入門

ManagerUtil.pm是一個工具,很多代碼都會用到它,且裡面的代碼也相對較少,非常適合作為我們的首個學習對象。學會看注釋是程式的必備技能,你可以看不懂代碼,但你不能看不懂注釋,在Perl語言中#開頭的都為注釋,是我們要重點查看的對象。

#### _learn/ManagerUtil.pm#!/usr/bin/env perl# 申明指令碼的執行環境為 perl,如果檔案有執行許可權,相關於:# ./xxx.pl 等價於 perl xxx.pl   和shell 指令碼類似#  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 name,相當於OOP的Class,同一個項目中使用的package name不能重複。# Perl 就是用 package 來實現OOP的一些特性,但並不所有的OOP特性都在Perl中找得到。package MHA::ManagerUtil;# Perl 浪起來太可怕了,所以建議使用嚴格文法格式use strict;use warnings FATAL => ‘all‘;# Carp模組提供了carp(),croak(),confess(),cluck(),shortmess(),longmess()六個函數,這些函數產生的錯誤資訊與warn(),die()類似。# 不同之處在於,後者標識的是出現錯誤的行號,前者產生調用錯誤的子程式命令列位置。use Carp qw(croak);# 引入MHA的其它packageuse MHA::ManagerConst;use MHA::NodeUtil;# 引用 Log::Dispatch package,在CPAN中有十萬八千個現成的package,俗稱造好的“輪子”,你可以利用這些“輪子”,製造出各種各樣的“汽車”來實現你的價值。use Log::Dispatch;use Log::Dispatch::File;use Log::Dispatch::Screen;##### 程式本文開始 ####### sub 用來申請子程式或方法,和其它語言比起來,特殊的地方是參數的傳遞,在Perl中,最其基本的參數傳遞方法為:# my @argxArr = @_; 用一個數組來接收參數# 另外一種方法是使用Perl的老地方 + shift的方式,如下面這個 init_log方法,將數組@_中的元素依次賦值給 $log_output,$level,較為常用。# 延伸學習,shift的用法可以參考:http://perldoc.perl.org/functions/shift.html# 關於 Log::Dispatch 的使用與說明會在下面章節中重點介紹# 功能:初始化日誌系統# 參數1:可選,指定記錄檔路徑# 參數2:可選,指定日誌的level# 傳回值: $log 對象sub init_log {    #等價於 my $log_output = shift @_;    my $log_output = shift;    #等價於 my level = shift @_;    my $level      = shift;    #LOG level的預設值為info,等價於 $level = $level ? $level : "info",就是未指定 level時,就用"info";    $level = "info" unless ($level);    # 初始化log對象,並指定日誌內容輸出格式的回呼函數,簡單說就是 自訂 日誌內容的記錄格式    # 在ManagerConst.pm中可以看出自訂格式為:    # sprintf( "[%s][%s, ln%d] %s\n", $args{level}, $script, $ln, $msg );    # 具體含義以後再詳細說明    my $log = Log::Dispatch->new( callbacks => $MHA::ManagerConst::log_fmt );    #預設日誌輸出到Screen,傳入檔案後將輸出到File    unless ($log_output) {        $log->add(            Log::Dispatch::Screen->new(                name      => ‘screen‘,                min_level => $level,                callbacks => $MHA::ManagerConst::add_timestamp,                mode      => ‘append‘,            )        );    }    else {        # 有$log_output時,記錄到檔案        $log->add(            Log::Dispatch::File->new(                name              => ‘file‘,                filename          => $log_output,                min_level         => $level,                callbacks         => $MHA::ManagerConst::add_timestamp,                mode              => ‘append‘,                close_after_write => 1,            )        );    }    return $log;}# 功能:執行本機系統命令# 參數1:系統從命令# 參數2:[可選]檔案路徑# 傳回值:($high,$low)雙零為執行成功,非雙零為執行發生錯誤sub exec_system {    # 逐個擷取參數    my $cmd        = shift;    my $log_output = shift;    # 傳入記錄檔路徑後,命令的正常輸出和錯誤輸出均記錄到檔案中    # MHA::NodeUtil::system_rc 的功能是將錯誤碼拆分成高8位和低8位    if ($log_output) {        return MHA::NodeUtil::system_rc( system("$cmd >> $log_output 2>&1") );    }    else {        # 直接執行命令,命令的輸出將正常輸出到螢幕上        return MHA::NodeUtil::system_rc( system($cmd) );    }}# 功能:在遠程主機上執行系統命令,注意這裡是沒有傳入密碼的,所有我們需要先做好各主機間的無密碼通道;# 參數1:host# 參數2:port# 參數3:cmd# 參數4:記錄檔路徑# 特別說明:方法名後面的 ($$$$) 代表都是必填參數sub exec_ssh_check_cmd($$$$) {    my $ssh_host   = shift;    my $ssh_port   = shift;    my $ssh_cmd    = shift;    my $log_output = shift;    my $ret;    return exec_system(        "ssh $MHA::ManagerConst::SSH_OPT_CHECK -p $ssh_port $ssh_host \"$ssh_cmd\"",        $log_output    );}# 功能:在遠程主機上執行系統命令,注意這裡是沒有傳入密碼的,所有我們需要先做好各主機間的無密碼通道;# 參數1:host# 參數2:port# 參數3:cmd# 參數4:記錄檔路徑sub exec_ssh_cmd($$$$) {    my $ssh_host   = shift;    my $ssh_port   = shift;    my $ssh_cmd    = shift;    my $log_output = shift;    my $ret;    return exec_system(        "ssh $MHA::ManagerConst::SSH_OPT_ALIVE -p $ssh_port $ssh_host \"$ssh_cmd\"",        $log_output    );}# 功能:在遠程主機上執行系統命令,注意這裡是沒有傳入密碼的,所有我們需要先做好各主機間的無密碼通道;sub get_node_version {    my $log      = shift;    my $ssh_user = shift;    my $ssh_host = shift;    my $ssh_ip   = shift;    my $ssh_port = shift;    my $ssh_user_host;    my $node_version;    my $command = "apply_diff_relay_logs --version";    if ( $ssh_host || $ssh_ip ) {        if ($ssh_ip) {            $ssh_user_host = $ssh_user . ‘@‘ . $ssh_ip;        }        elsif ($ssh_host) {            $ssh_user_host = $ssh_user . ‘@‘ . $ssh_host;        }        $command =            "ssh $MHA::ManagerConst::SSH_OPT_ALIVE $ssh_user_host -p $ssh_port \"$command\" 2>&1";    }    my $v = `$command`;    chomp($v);    if ( $v =~ /version (\d+\.\d+)/ ) {        $node_version = $1;    }    else {        $log->error("Got error when getting node version. Error:");        $log->error("\n$v") if ($v);    }    return $node_version;}# 功能:擷取node節點的版本,發生錯誤時,記錄錯誤資訊後,就die(退出程式)了;sub check_node_version {    my $log      = shift;    my $ssh_user = shift;    my $ssh_host = shift;    my $ssh_ip   = shift;    my $ssh_port = shift;    my $node_version;    eval {        $node_version =            get_node_version( $log, $ssh_user, $ssh_host, $ssh_ip, $ssh_port );        my $host = $ssh_host ? $ssh_host : $ssh_ip;        croak "node version on $host not found! Is MHA Node package installed ?\n"            unless ($node_version);        if ( $node_version < $MHA::ManagerConst::NODE_MIN_VERSION ) {            $host = "local" unless ($host);            my $msg =                sprintf( "Node version(%s) on %s must be equal or higher than %s.\n",                    $node_version, $host, $MHA::ManagerConst::NODE_MIN_VERSION );            croak $msg;        }    };    # [email protected]為eval命令的錯誤訊息.如果為空白,則表示上一次eval命令執行成功    if ([email protected]) {        $log->error([email protected]);        die;    }    return $node_version;}# 功能:擷取node節點的版本,發生錯誤時返回錯誤碼sub check_node_version_nodie {    my $log      = shift;    my $ssh_user = shift;    my $ssh_host = shift;    my $ssh_ip   = shift;    my $ssh_port = shift;    my $rc       = 1;    eval {        check_node_version( $log, $ssh_user, $ssh_host, $ssh_ip, $ssh_port );        $rc = 0;    };    if ([email protected]) {        undef [email protected];    }    return $rc;}# 功能:輸出錯誤資訊,如果不確認 $log 是否有初始化時使用# 參數1:資訊內容# 參數2:$log 對象# should be used when it is unclear whether $log is initialized or notsub print_error {    my $str = shift;    my $log = shift;    if ($log) {        $log->error($str);    }    else {        warn "$str\n";    }}# 在Perl中,return語句可以返回一個標量值或者一個列表,這個標量值可以是一個變數,或者一個運算式的最後求值# 不過在 package 的含義有些費解1;

總結一下,通過ManagerUtil.pm我們學到什麼:
1.使用package聲明"類"名,用來給其它perl程式進行調用,實現代碼複用;
2.使用use引入外部package,這些package需存在於perl的lib庫中,通過@INC可以查看和改變lib庫的路徑;
3.使用my聲明局部變數,用our可以申表全域變數;
4.使用sub聲明方法或函數,參數可以用特殊符號@_來接收;
5.perl函數的傳回值可以是多個,可以用列表來接收,如 my($a,$b) = function();
6.特殊符號[email protected]為上一個eval命令的錯誤訊息,如果為空白,則表示上一次eval命令執行成功;
7.perl的變數以$開頭,數組以@開頭,另外還有%開頭的是hash;

快速瞭解perl推薦:
https://learnxinyminutes.com/docs/zh-cn/perl-cn/

ManagerUtil應用實戰

在ManagerUtil中,定義了幾個非常簡單且很常用的方法,以方便在其它指令碼中進行使用,接下就是動手實戰一下,體驗下perl在coding fun:

####  _learn/test/TestManagerUtil.pl 

#!/usr/bin/perluse strict;use warnings FATAL => ‘all‘;# 引入packageuse MHA::ManagerUtil;#在遠程主機上執行命令#參數說明:# 四個參數依次為:# $ssh_host 遠程主機IP# $ssh_port 連接埠# $ssh_cmd 命令# $log_output 記錄檔路徑#傳回值說明:# 正常的shell出錯誤的傳回值為一個非0的數字,作者經過封裝後,進一步把錯誤碼分別高8位($high)和低8位元字($low),下面一個完整的調用例子my ( $high, $low ) = MHA::ManagerUtil::exec_ssh_cmd("192.168.0.202","22","/usr/local/mysql/bin/mysql --version","/tmp/mha_fun.log");#高8位和低8位均為0,代表遠程命令執行成功if ( $high == 0 && $low == 0 ) { MHA::ManagerUtil::print_error("execute command successed.")}else{ #執行失敗,原因會很多:如ip不對,port不對,mysql的路徑不對,未作ssh無密碼通道等等,具體的原因需要查看 /tmp/mha_fun.log 記錄檔 #從這裡也可以看出一個日誌系統的重要性,學習查看和分析日誌的重要性 MHA::ManagerUtil::print_error("execute command failed.")}#執行本機命令#查看剛才(MHA::ManagerUtil::exec_ssh_cmd)的記錄檔MHA::ManagerUtil::exec_system("cat /tmp/mha_fun.log");#查看mha node的版本#先初始化一個$log對象,然後再作傳 參數傳給MHA::ManagerUtil::get_node_version,在MHA的代碼類似的代碼非常多my $log = MHA::ManagerUtil::init_log("/tmp/mha_fun.log","debug");my $node_version = MHA::ManagerUtil::get_node_version($log,"root",undef,"192.168.0.202","22");print $node_version;### END, Just run it,so easy,so fun. ###
日誌系統快速撐握

日誌系統是我們尋找一切問題最直接,最快速的"捷徑",基本每個系統都有自己完善日誌記錄,在學習別人項目的時候,可以把自己想要查看的內容通過日誌系統記錄起來,即快速也不影響原因的代碼,所以大家要學會多加利用日誌系統來解決自己的問題.接下來我們來體驗下CPAN中提供的Log"類":

#### _learn/test/TestLogDispatch.pl#!/usr/bin/perluse strict;use warnings FATAL => ‘all‘;# Perl日誌系統 Log:Dispatch 使用執行個體代碼# Log::Dispatch,可以把日誌資訊輸入到file,screen,email中,且使用起來非常的簡單和方便;# 以下代碼來自CPAN的Log::Dispath項目:# http://search.cpan.org/~drolsky/Log-Dispatch-2.66/lib/Log/Dispatch.pmuse Log::Dispatch;# 初始化一個 $log 對象,同時綁定記錄到File和輸出到Screen,且指定了不同的levelmy $log = Log::Dispatch->new(    outputs => [        [ ‘File‘,   min_level => ‘debug‘, filename => ‘/tmp/perl.log‘ ],        [ ‘Screen‘, min_level => ‘info‘ ],    ],);# 產生info日誌$log->info(‘Info:Blah, blah‘);# 產生debug日誌$log->debug(‘Debug:Blah, blah‘);# 產生error日誌$log->error(‘Error:Blah, blah‘);# 運行這個程式後,觀察一下Screen和/tmp/perl.log的內容,並思考一下如果使Screen和File的內容完全一樣,需要如何修改代碼.# Log::Dispatch 總共有7個層級,具體可能參考文檔,關於記錄層級,簡單總結一下:# 1.在MHA中可以通過 log_level = [No|App/Global|info|debug] 配置 記錄層級.#    參考:https://raw.githubusercontent.com/wiki/yoshinorim/mha4mysql-manager/Parameters.md# 2.debug是最低的記錄層級,一般用於開發人員記錄排錯的相關資訊.# 3.程式在運行時,將忽略低於配置log_level的日誌輸出.如:配置log_level=info時,所有的 $log->debug(‘Blah, blah‘)資訊都會被忽略.# 4.log_level的主要作用是在不用修改代碼的前提下,通過簡單的配置就可以區分產生環境和開發環境日誌內容.### END, Just sun it,so easy,so fun. ###

總的來說,perl還是很容易上手的,不過那個眾多的"特殊符號"確實要比學習其它語言花更多的時間.

整個MHA代碼的結構還是非常清晰和易於擴充的,通MHA不僅可以學會perl的文法,整個MHA代碼構架也是值得學習的.

 

邊看MHA源碼邊學Perl語言之二 ManagerUtil

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.