码迷,mamicode.com
首页 > 其他好文 > 详细

Linode/Longview.pl

时间:2015-06-10 11:45:05      阅读:213      评论:0      收藏:0      [点我收藏+]

标签:

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);
}

Linode/Longview.pl

标签:

原文地址:http://www.cnblogs.com/mydevops/p/4530387.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!