标签:
Longview.pl 流程图
Longview.pl 源码剖析
#!/usr/bin/env perl use strict; use warnings; =head1 COPYRIGHT/LICENSE Copyright 2013 Linode, LLC. Longview is made available under the terms of the Perl Artistic License, or GPLv2 at the recipients discretion. =head2 Perl Artistic License Read it at L<http://dev.perl.org/licenses/artistic.html>. =head2 GNU General Public License (GPL) Version 2 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, see http://www.gnu.org/licenses/ See the full license at L<http://www.gnu.org/licenses/>. =cut # 程序运行前需要执行的代码块 BEGIN { # 访问配置信息模块 use Config; # 找到当前脚本的目录 use FindBin; # @INC 中包含了所有的模块的查找路径 # 将相关路径添加到 @INC 数组中 push @INC, "$FindBin::RealBin/../"; push @INC, "$FindBin::RealBin/../lib/perl5"; push @INC, "$FindBin::RealBin/../lib/perl5/${Config{archname}}/"; push @INC, "$FindBin::RealBin/../usr/include"; { # 忽略警告一次 no warnings ‘once‘; # 设定 SOCKET_CLASS 为 IO::Socket::INET6 $Net::HTTP::SOCKET_CLASS = ‘IO::Socket::INET6‘; } # HTTP 连接的客户端 require Net::HTTP; } # 解析和打包 JSON use JSON; # 异常处理模块 use Try::Tiny; # 获取本机主机名的模块 use Sys::Hostname; # web代理模块 use LWP::UserAgent; # Zlib 压缩库的接口 use Compress::Zlib; # Socket 模块 use IO::Socket::INET6; # 自定义的 Linode/Longview/DataGetter # 主要用于数据采集 use Linode::Longview::DataGetter; # 自定义的 Linode/Longview::Util ‘:DRIVER‘ # Util 定义一些常用的工具 # :DRIVER 声明一些全局变量 use Linode::Longview::Util ‘:DRIVER‘; # $logger 实例和 $VERSION 变量全部来源于 Util ‘:DRIVER‘ # 写入 启动信息 "启动 Longview客户端,版本是 $VERSION" $logger->info("Starting Longview Agent version $VERSION"); # 如果启动用户不是root写入日志"Longview 的启动用户必须是root,以便于用来采集数据" # $< 就是 %ENV ($UID, $REAL_USER_ID) root 的 UID 是0 $logger->logdie(‘Longview must be run as root in order to collect data‘) unless ($< == 0); # 检查程序是否在运行中 my $pid = check_already_running(); # 如在在运行中 写入日志"Longview 程序已经在运行了 PID: $pid" $logger->logdie("The Longview agent is already running as PID: $pid") if $pid; # 配置文件主目录 my $confdir = ‘/etc/linode‘; # API Key 文件的绝对路径 my $api_key_file = "$confdir/longview.key"; # 获取 API Key 文件的内容 $apikey = scalar(slurp_file($api_key_file)); # 如果文件内容为空 unless ($apikey){ # 输出 "没有找到 API Key. 请输入你的 API Key" # if -t 打开文件句柄 print "\nNo api key found. Please enter your API Key: " if -t; # 接收用户输入 $apikey = <>; # 如果 $apikey 未定义 unless(defined $apikey){ # 打印 "没有找到 API Key. 请在启动 longview 之前添加你的 API Key 到/etc/linode/longview.key 文件中." print "No api key found. Please add your API key to /etc/linode/longview.key before starting longview.\n"; # 退出程序 exit 1; } # 清除用户输入多余的小尾巴. chomp($apikey); # 如果用户输入的 API Key 不满足规定的格式 unless ($apikey =~ /^[0-9A-F]{8}-(?:[0-9A-F]{4}-){2}[0-9A-F]{16}\z$/){ # 打印 "无效的 API Key" print "Invalid API Key\n"; # 退出程序 exit 1; } # 设置当前进程的 umask umask 066; # 创建配置文件主目录 mkdir $confdir; # 打开 API Key 文件的文件句柄 # 如果打开失败, 打印 "不能写状态打开 $api_key_file" open my $fh, ‘>‘, $api_key_file or $logger->logdie("Couldn‘t open $api_key_file for writing: $!"); # 将用户输入写入到文件中 print $fh $apikey; # 关闭 API Key 的文件句柄 # 如果关闭失败打印 "不能关闭 $api_key_file" close $fh or $logger->logdie("Couldn‘t close $api_key_file: $!"); } # 如果文件内容不为空 # 如果文件内容不满足规定格式打印 "无效的 API Key" $logger->logdie(‘Invalid API key‘) unless ($apikey =~ /^[0-9A-F]{8}-(?:[0-9A-F]{4}-){2}[0-9A-F]{16}\z$/); # 声明统计数据的 Hash my $stats = { # API Key apikey => $apikey, # 版本 version => ‘1.0‘, # 存储的数据 payload => [], }; # main方法执行前的准备动作 _prep_for_main(); # 设置退出状态、数据和重启状态为空 my ($quit, $data, $reload) = (0, {}, 0); # 如果 $quit 为假就执行无限循环 while (!$quit) { # 如果 reload 为真 if ($reload){ # 重新加载模块 reload_modules(); # 并将加载状态设置为 0 $reload = 0; } # 获取间隔时间 my $sleep = $SLEEP_TIME; # 获取当前时间戳 $data->{timestamp} = time; # 收集数据 get($_,$data,) for @{run_order()}; # 将数据保存到 $stats 中 constant_push($stats->{payload},$data); # 清空 $data $data = {}; # 获取当前时间戳 $stats->{timestamp} = time; # 上报数据并获取回报内容 my $req = post($stats); # 如果请求成功 if ($req->is_success){ # 写入DEBUG 日志: 状态信息 $logger->debug($req->status_line); # 写入DEBUG 日志: 返回内容 $logger->debug($req->decoded_content); # 声明一个 $rep 变量 my $rep; try { # 解析json $rep = decode_json($req->decoded_content); } catch { # 如果解析失败 # 写入 DEBUG 日志: "不能解析这个 JSON" $logger->debug("Couldn‘t decode JSON response: $_"); }; # 如果返回的内容中包含间隔时间 # 那么间隔时间设置为 manger 给的间隔时间 $sleep = $rep->{sleep} if defined $rep->{sleep}; # 如果返回的值中定义了 die 并且内容是"please" if (defined($rep->{die}) && $rep->{die} eq ‘please‘) { # 打印日志 "服务器要求关闭这个 API Key 发送数据" $logger->logdie(‘Server has requested this API Key stop sending data‘); } # 清空数据 @{$stats->{payload}} = (); } # 如果请求失败 else{ # 写入DEBUG 日志: 状态信息 $logger->info($req->status_line); # 写入DEBUG 日志: 返回内容 $logger->info($req->decoded_content); } # 循环间隔时间 sleep $sleep; } # 准备动作 sub _prep_for_main { # 给 API Key 文件设置属主和属组为 root chown 0, 0, $api_key_file; # 位置文件权限 0600 chmod 0600, $api_key_file; # 生成 PID 文件 daemonize_self(); # 如果启动存在参数 # 并且参数的第一个内容是 Debug # 那么开启 debug 模式 enable_debug_logging() if(defined $ARGV[0] && $ARGV[0] =~ /Debug/i); # 加载模块 # DataGetter 模块中将 load_modules 设置成了全局变量 load_modules(); # 设定程序名称 "linode-longview" # $0 等同于 $PROGRAM_NAME $0 = ‘linode-longview‘; # 检测到下列任何一种信号, 将 $quit 设置为 1 # TREM : 终端信号 # INT : 来自键盘的中断 # QUIT : 来自键盘的停止 $SIG{TERM} = $SIG{INT} = $SIG{QUIT} = sub { $quit = 1 }; # 检测到挂起信号, 将 $reload 设置为 1 $SIG{HUP} = sub { $reload = 1}; # 写入日志 "启动完成" $logger->info(‘Start up complete‘); }
标签:
原文地址:http://www.cnblogs.com/mydevops/p/4530387.html