标签:详细 sql eve put inux app 分析日志 package nta
为了让大家有一个共同的代码学习环境,特别从网络找了mha4mysql-manager-0.56,mha4mysql-node-0.56稳定版作为学习和研究对象,大家可以到直接到github上进行clone:
https://github.com/mysql-dev-fun/mha4mysql-manager-0.56 https://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从库二 |
这里就省略了,大家可以参考大师兄的博客。
http://www.cnblogs.com/gomysql/p/3675429.html
参考官方手册: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
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的其它package
use 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;
}
};
# $@为eval命令的错误消息.如果为空,则表示上一次eval命令执行成功
if ($@) {
$log->error($@);
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 ($@) {
undef $@;
}
return $rc;
}
# 功能:输出错误信息,如果不确认 $log 是否有初始化时使用
# 参数1:信息内容
# 参数2:$log 对象
# should be used when it is unclear whether $log is initialized or not
sub 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.特殊符号$@为上一个eval命令的错误消息,如果为空,则表示上一次eval命令执行成功;
7.perl的变量以$开头,数组以@开头,另外还有%开头的是hash;
快速了解perl推荐:
https://learnxinyminutes.com/docs/zh-cn/perl-cn/
在ManagerUtil中,定义了几个非常简单且很常用的方法,以方便在其它脚本中进行使用,接下就是动手实战一下,体验下perl在coding fun:
#### _learn/test/TestManagerUtil.pl
#!/usr/bin/perl use strict; use warnings FATAL => ‘all‘; # 引入package use 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/perl
use 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.pm
use Log::Dispatch;
# 初始化一个 $log 对象,同时绑定记录到File和输出到Screen,且指定了不同的level
my $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代码构架也是值得学习的.
标签:详细 sql eve put inux app 分析日志 package nta
原文地址:http://www.cnblogs.com/mysql-dev-fun/p/7545481.html