While watching MHA source, learn the second MANAGERUTIL.PMMHA version of Perl language
In order to let everyone have a common code learning environment, especially from the network to find mha4mysql-manager-0.56,mha4mysql-node-0.56 stable version as learning and research objects, you can go directly to GitHub to clone:
https://github.com/mysql-dev-fun/mha4mysql-manager-0.56https://github.com/mysql-dev-fun/mha4mysql-node-0.56
Window recommends using Phpstrom + perl plugin for code viewing log analysis, the test environment is recommended directly under Linux directly executed.
My basic Environment
Ip |
Description |
192.168.0.110 |
Mha4mysql-manager |
192.168.0.100 |
MySQL Main Library |
192.168.0.101 |
MySQL from Kuraichi |
192.168.0.102 |
MySQL from library II |
MySQL master-slave environment setup
This is omitted, you can refer to Big bro's blog.
Http://www.cnblogs.com/gomysql/p/3675429.html
MHA Installation
Reference Official manual: Https://github.com/yoshinorim/mha4mysql-manager/wiki/Installation
(The official latest version may appear a variety of pits, so we recommend that you use stable version of learning).
Refer to the installation steps:
#Install dependency 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
#Download Learning source code
$ cd /usr/local/src
$ git clone https://github.com/mysql-dev-fun/mha4mysql-manager-0.56.git
#Compile and install
$ 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 Getting Started
MANAGERUTIL.PM is a tool that many code uses, and the code in it is relatively small, perfect for our first learning object. Learning to read annotations is a necessary skill for the program, you can not read the code, but you can't read the comments, in the Perl language, the beginning of the # is a comment, we want to focus on the object.
#### _learn/ManagerUtil.pm
#!/usr/bin/env perl
# The execution environment of the script is perl. If the file has execute permission, it is related to:
# ./xxx.pl is equivalent to perl xxx.pl and is similar to shell scripts
# 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 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
# Affirm the package name, which is equivalent to the OOP Class. The package name used in the same project cannot be repeated.
# Perl is to use package to implement some features of OOP, but not all OOP features are found in Perl.
Package MHA::ManagerUtil;
# Perl It’s terrible to wave, so it’s recommended to use strict syntax
Use strict;
Use warnings FATAL => ‘all‘;
The # Carp module provides six functions, carp(), croak(), confess(), cluck(), shortmess(), and longmess(). These functions generate error messages similar to warn() and die().
# The difference is that the latter identifies the line number where the error occurred, and the former generates a subroutine command line location that calls the error.
Use Carp qw(croak);
# Introducing other packages of MHA
Use MHA::ManagerConst;
Use MHA::NodeUtil;
#Reference Log::Dispatch package, there are 18,000 ready-made packages in CPAN, commonly known as "wheels", you can use these "wheels" to create a variety of "cars" to achieve you the value of.
Use Log::Dispatch;
Use Log::Dispatch::File;
Use Log::Dispatch::Screen;
##### Program text start ######
# sub is used to apply for a subroutine or method. Compared with other languages, the special place is the parameter passing. In Perl, the most basic parameter passing method is:
# my @argxArr = @_; Use an array to receive parameters
# Another method is to use Perl's old place + shift mode, such as the following init_log method, the elements in the array @_ are assigned to $log_output, $level, which is more commonly used.
# Extend learning, the usage of shift can be referenced: http://perldoc.perl.org/functions/shift.html
# About Log::Dispatch The use and description will be highlighted in the following sections.
# Function: Initialize the log system
# Parameter 1: Optional, specify the log file path
# Parameter 2: Optional, specify the level of the log
# Return value: $log object
Sub init_log {
# my $log_output = shift @_;
My $log_output = shift;
# my level = shift @_;
My $level = shift;
The default value of #LOG level is info, which is equivalent to $level = $level ? $level : "info". If no level is specified, use "info";
$level = "info" unless ($level);
# Initialize the log object and specify the callback function of the log content output format, which is simply the record format of the custom log content.
# In ManagerConst.pm you can see that the custom format is:
# sprintf( "[%s][%s, ln%d] %s\n", $args{level}, $script, $ln, $msg );
# concrete meaning will be explained in detail later
My $log = Log::Dispatch->new( callbacks => $MHA::ManagerConst::log_fmt );
#Default log output to Screen, will be output to File after passing in the file
Unless ($log_output) {
$log->add(
Log::Dispatch::Screen->new(
Name => ‘screen‘,
Min_level => $level,
Callbacks => $MHA::ManagerConst::add_timestamp,
Mode => ‘append‘,
)
);
}
Else {
# Recording to file when $log_output is available
$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;
} # Function: Execute the local system command
# Parameter 1: System from command
# parameter 2: [optional] file path
# Return value: ($high, $low) double zero for execution success, non-double zero for execution error
Sub exec_system {
# Get parameters one by one
My $cmd = shift;
My $log_output = shift;
# After passing in the log file path, the normal output and error output of the command are recorded in the file.
The function of # MHA::NodeUtil::system_rc is to split the error code into high 8 and low 8 bits.
If ($log_output) {
Return MHA::NodeUtil::system_rc( system("$cmd >> $log_output 2>&1") );
}
Else {
# Directly execute the command, the output of the command will be output to the screen normally.
Return MHA::NodeUtil::system_rc( system($cmd) );
}
}
# Function: Execute the system command on the remote host. Note that there is no incoming password. All we need to do the passwordless channel between each host first;
#parameter 1:host
#
parameter 2:port
#
parameter 3:cmd
# Parameter 4: Log file path
# Special Note: The ($$$$) representation after the method name is a required parameter.
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
);
}
# Function: Execute the system command on the remote host. Note that there is no incoming password. All we need to do the passwordless channel between each host first;
# Parameter 1:host
# Parameter 2:port
# Parameter 3:cmd
# Parameter 4: Log file path
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
);
}
# Function: Execute the system command on the remote host. Note that there is no incoming password. All we need to do the passwordless channel between each host first;
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;
}
# Function: Get the version of the node node. When an error occurs, after logging the error message, it will die (exit the program);
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;
}
};
# $@ is the error message of the eval command. If it is empty, it means the last eval command was executed successfully.
If ($@) {
$log->error($@);
Die;
}
Return $node_version;
}
# Function: Get the version of the node node, return an error code when an error occurs
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;
}
# Function: Output error message, if you do not confirm whether $log is used during initialization
# Parameter 1: Information content
# parameter 2: $log object
# 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";
}
}
# In Perl, the return statement can return a scalar value or a list, this scalar value can be a variable, or the final evaluation of an expression
# However, the meaning of the package is somewhat confusing
1;
To summarize what we learned through MANAGERUTIL.PM:
1. Use the package to declare "class" name, used to call other Perl programs, to achieve code reuse;
2. Using use to introduce external package, these package need to exist in the Lib library of Perl, through @inc can view and change the path of Lib library;
3. Using my to declare local variables, we can use our global variables;
4. Using a sub to declare a method or function, the parameter can be received with a special symbol @_;
The return value of the 5.perl function can be multiple and can be received in a list, such as my ($a, $b) = function ();
6. Special symbol [email protected] is an error message for the previous eval command, and if empty, indicates that the last eval command was successfully executed;
The 7.perl variable starts with $, the array begins with @, and the% starts with a hash;
Get a quick look at Perl recommendations:
https://learnxinyminutes.com/docs/zh-cn/perl-cn/
Managerutil Applied Combat
In Managerutil, there are several very simple and common methods that are defined to facilitate use in other scripts, followed by hands-on combat, and experience Perl in coding fun:
#### _learn/test/TestManagerUtil.pl
#!/usr/bin/perl
Use strict;
Use warnings FATAL => ‘all‘;
# Introducing package
Use MHA::ManagerUtil;
#Execute commands on the remote host
#Parameter Description:
# Four parameters are:
# $ssh_host Remote Host IP
# $ssh_port port
# $ssh_cmd command
# $log_output log file path
Return value description:
# The return value of a normal shell error is a non-zero number. After the package is encapsulated, the error code is further higher by 8 bits ($high) and lower 8 digits ($low). The following complete call example
My ( $high, $low ) = MHA::ManagerUtil::exec_ssh_cmd("192.168.0.202","22","/usr/local/mysql/bin/mysql --version","/tmp/mha_fun.log ");
The upper 8 bits and the lower 8 bits are both 0, indicating that the remote command is successfully executed.
If ( $high == 0 && $low == 0 ) {
MHA::ManagerUtil::print_error("execute command successed.")
}else{
#Execution failed, the reason will be a lot: if ip is wrong, port is wrong, mysql path is wrong, no ssh no password channel, etc., the specific reason needs to view /tmp/mha_fun.log log file
#From here you can also see the importance of a log system, learn to view and analyze the importance of the log
MHA::ManagerUtil::print_error("execute command failed.")
}
#Executing native commands
#View the log file of (MHA::ManagerUtil::exec_ssh_cmd)
MHA::ManagerUtil::exec_system("cat /tmp/mha_fun.log");
#View the version of mha node
# Initialize a $log object, then pass the parameters to MHA::ManagerUtil::get_node_version, the code in the MHA code is very similar
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. ### Log System Quick Hold
Log system is our most direct search for all problems, the fastest "shortcut", basic each system has its own perfect log records, in the learning of others project, you can view the content of the log system, that is, fast and does not affect the reason of the code, Therefore, we should learn to use the log system to solve their own problems. Let's try the log "class" provided in the next CPAN:
#### _learn/test/TestLogDispatch.pl
#!/usr/bin/perl
Use strict;
Use warnings FATAL => ‘all‘;
# PerlLog System Log:Dispatch Use example code
# Log::Dispatch, you can input the log information into file, screen, email, and it is very simple and convenient to use;
# The following code is from CPAN's Log::Dispath project:
# http://search.cpan.org/~drolsky/Log-Dispatch-2.66/lib/Log/Dispatch.pm
Use Log::Dispatch;
# Initialize a $log object, bind the record to File and output to Screen, and specify a different level
My $log = Log::Dispatch->new(
Outputs => [
[‘File‘, min_level => ‘debug’, filename => ‘/tmp/perl.log‘ ],
[‘Screen’, min_level => ‘info‘ ],
],
);
#generate the info log
$log->info(‘Info:Blah, blah‘);
# Generate debug logs
$log->debug(‘Debug:Blah, blah‘);
# Generate error log
$log->error(‘Error:Blah, blah‘);
# After running this program, observe the contents of Screen and /tmp/perl.log and think about how to modify the code if the contents of Screen and File are exactly the same.
# Log::Dispatch There are a total of 7 levels. For details, please refer to the documentation. For the log level, briefly summarize:
# 1. In MHA you can configure the log level via log_level = [No|App/Global|info|debug].
# reference:https://raw.githubusercontent.com/wiki/yoshinorim/mha4mysql-manager/Parameters.md
# 2.debug is the lowest log level, generally used by developers to record information about troubleshooting.
# 3. When the program is running, it will ignore the log output lower than the configuration log_level. For example, when configuring log_level=info, all $log->debug(‘Blah, blah‘) information will be ignored.
# 4. The main role of log_level is to distinguish the generation environment and development environment log content by simple configuration without modifying the code.
### END, Just sun it,so easy,so fun. ###
In general, Perl is easy to get started with, but the multitude of "special symbols" do take more time than learning other languages.
The structure of the entire MHA code is still very clear and easy to expand, MHA can not only learn the syntax of Perl, the entire MHA code architecture is also worth learning.
Read MHA source and learn the Perl language of the second Managerutil