zoukankan      html  css  js  c++  java
  • perl DBD处理超时问题

    名字:
    
    dbd-oracle-timeout.pod  测试DBD-Oracle超时操作使用Sys::SigAction
    
    
    摘要:
    
    本文讨论我使用SIGALRM来超时某个DBD-Oracle操作遇到的问题
    
    Perl 5.8.0 和以后的版本支持sigaction() 实现'safe'的信号处理。
    
    不幸的是,工作的斑斑在5.8之前,提出了解决这个一问题的几种方法
    
    描述
    
    如果你是实现一个实时业务,你的软件必须快速响应和很多的表现。
    
    它是必要的没有任何操作话费很长的时间来完成,
    
    资源是快速回收的,这样服务才能响应新的请求。
    
    在这种情况下,通常最好是超时或者返回一个错误,而不是允许请求被长时间挂起
    
    我的团队已经实现了大量的实时服务使用perl和DBI接口使用DBD-Oracle driver. 
    
    本文针对的是使用Oracle遇到的问题,
    
    但是 我相信 我们遇到的问题从5.6到5.8是通用的,
    
    可能影响任何数据库驱动使用一个client库来调用像connect()
    
    这里介绍的技术可以用于解决这类问题使用任何DBD 驱动,或者任何系统调用可能会hang的
    
    为此 SIGALRM 已经用于中断调用
    
    使用5.8.0之前的DBI接口,它是容易使用设置代码引用到 $SIG{'ALRM'}, 
    
    然后使用 alarm() 来实现超时
    
    信息处理然后die()或者其他中断调用 
    
    我发现这两种操作需要这个处理:
    
    1 Database Host is Down -- connect() hangs  数据库主机Down connect() hangs
    
    使用SQL*NET ,DBI->connect()请求会hang大概4分钟,下面是我们如何处理这种情况在5.8以前的版本
    
    eval {
       local $SIG{ALRM} = sub { die "open timed out"; };
       eval {
          alarm(2); #implement 2 second time out
          $dbh = DBI->connect("dbi:Oracle:$dbn" ... );
          alarm(0);
       };
       alarm(0);
       die $@ if $@;
    };
    if ( $@ ) { print "connection to $dbn timed out
    " ; }
    
    因为$SIG{ALRM} 已经被本地化,这个代码还原$SIG{ALRM}原始的值 当eval block 退出后
    
    eval 说明
    
    
    读者可能注意到 "double evals" 在上面的代码例子。
    
    CPAN bug #50628已针对Sys::SigAction提交了bug,
    
    问题 
    
    我们中很多人使用perl 5.6.x 好多年了,
    
    上面的代码工作的非常好。
    
    我们知道 perl 5.6和以前的版本信号处理是不安全的,我们接受了风险
    
    信号处理程序可以在一个合适的时候被调用
    
    从5.8.2 perlvar 手册:
    
    默认的信号传递策略从即时的(也被称为不安全)改为了延迟,也被称为安全信号
    
    不幸的是, 'safe signals'处理导致了一些系统调用被重试之前(取决于它们是如何被调用的)
    
    具体信号处理的执行依赖于库是如何实现的
    
    结果是这个发生是一些调用者永远不返回,尽管信号已经被处罚。
    
    这个例子是使用DBD-Oracle connect()请求(case 1上面的例子)
    
    因此,标准的超时实现不在工作在perl 5.8和以后版本
    
    eval {
       local $SIG{ALRM} = sub { die "open timed out"; };
       eval {
          alarm(2); #implement 2 second time out
          $dbh = DBI->connect("dbi:Oracle:$dbn" ... );
          alarm(0);
       };
       alarm(0);
       die $@ if $@;
    };
    if ( $@ ) { print "connection to $dbn timed out
    " ; }
    
    
    [oracle@node01 perl]$ cat test5.pl 
    use DBI;
    use Encode;
    use Data::Dumper;
    use Sys::SigAction ;
    use Sys::SigAction qw( set_sig_handler );
    use Sys::SigAction qw( set_sig_handler );
    eval {
       local $SIG{ALRM} = sub { die "open timed out"; };
       eval {
          alarm(2); #implement 2 second time out
          $dbh = DBI->connect("dbi:Oracle://1.168.137.2:1521/serv", 'system', 'oracle') or die "can't connect to database ";;
          alarm(0);
       };
       alarm(0);
       die $@ if $@;
    };
    if ( $@ ) { print "connection to $dbn timed out
    " ; }
    [oracle@node01 perl]$ perl test5.pl 
    
    不能超时退出
    
    解决办法:
    
    解决这个问题的办法(记录在perlvar手册页中)是安装信号处理使用 POSIX::sigaction().
    
    这个提供了低级访问POSIX sigaction() system API(假设)你的系统有 sigaction().
    
    如果你的系统没有sigaction(),  你可能没有这个问题
    
    在那种情况下 perl 实现原始的(不安全的)信号处理方法 使用POSIX::sigaction(), 
    
    我们可以控制信号屏蔽和sa_flags是用于安装handler,
    
    在perl 5.8.2和以后版本,一个安全的切换是提供来使用寻求安全信号处理,
    
    
    使用POSIX::sigaction()  不确保信号处理是被调用当信号是被处罚时。
    
    调用die()程序在信号处理内 会导致系统调用被中断,控制会返回给perl 脚本。
    
    但是这样做高效实现了返回我们不安全的信号行为  至少在 5.8.0.
    
    在perl 5.8.2 它是要求延迟 安全的信号处理 当仍旧通知sa_flags 用于安装信号控制
    
    perl 5.8.2 是比5.6安全
    
    痛点
    
    除了不能低于5.8版本 ,它需要大约4到5行代码 以前只需要设置一个localized $SIG{ALRM}.
    
    
    POSIX::sigaction() 代码看起来像这样对于(connect()例子)
    
    use POSIX ':signal_h';
     
    eval {
       my $mask = POSIX::SigSet->new( SIGALRM ); #list of signals to mask in the handler
       my $action = POSIX::SigAction->new( 
           sub { die "connect failed" ; } #the handler code ref
          ,$mask ); #assumes we're not using an specific flags or 'safe' switch
       my $oldaction = POSIX::SigAction->new();
       sigaction( 'ALRM' ,$action ,$oldaction );
       eval {
          alarm(2); #implement 2 second time out
          $dbh = DBI->connect("dbi:Oracle:$dbn" ... );
          alarm(0);
       };
       alarm(0);
       sigaction( 'ALRM' ,$oldaction ); #restore original signal handler
       die $@ if $@;
    };
    if ( $@ ) ....
    
    
    这个不是在perl 5.6一行代码的完美替换,更糟糕的是因为POSIX::sigaction() 不能工作在5.8版本以下,
    
    我们必须让他满足perl 版本的条件
    
    止痛药; Sys::SigAction
    
    幸运的是,我已经被这个问题咬了一口,不想复制所有的代码在我的超时逻辑里,
    
    我实现了一个模块 使用POSIX::sigaction() 来容易的设置一个 localized $SIG{ALRM} 
    
    Sys::SigAction 模块可以从CPAN检索
    
     Sys::SigAction  模块包含了所有的POSIX:: code 到一个单独的函数请求 返回一个对象引用
     
     当对象超出范围时,它的构造器重置信号程序  因此上面的代码重写如下:
     
     use Sys::SigAction qw( set_sig_handler );
     
    eval {
       my $h = set_sig_handler( 'ALRM' ,sub { die "connect failed" ; } );
       eval {
          alarm(2); #implement 2 second time out
          $dbh = DBI->connect("dbi:Oracle:$dbn" ... );
          alarm(0);
       };
       alarm(0);
       die $@ if $@;
    }; #original signal handler restored here when $h goes out of scope
    if ( $@ ) ....
    
     #eval {$dbh1 = DBI->connect( "dbi:Oracle://$dbip:1521/$dbname", $dbuser, $dbpass ) or die "Cannot conenct db: $DBI::errstr
    ";};
    
            eval {
              my $h = set_sig_handler( 'ALRM' ,sub { die "connect failed" ; } );
             eval {
                alarm(10); #implement 10 second time out
                $dbh1 = DBI->connect( "dbi:Oracle://$dbip:1521/$dbname", $dbuser, $dbpass ) or die "Cannot conenct db: $DBI::errstr
    ";
                alarm(0);
                  };
                 alarm(0);
                 die $@ if $@;
           }; #original signal handler restored here when $h goes out of scope
    
  • 相关阅读:
    给定一个无序数组arr,求出需要排序的最短子数组长度。例如: arr = [1,5,3,4,2,6,7] 返回4,因为只有[5,3,4,2]需要排序。
    Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses. For example, given n = 3, a solution set is: "((()))", "(()())", "(())()", "()(())", "()()()"
    shell数组
    学习ansible(一)
    nginx搭建简单直播服务器
    rsync
    Linux运维最常用150个命令
    Linux 三剑客
    学习Python(一)
    学习k8s(三)
  • 原文地址:https://www.cnblogs.com/hzcya1995/p/13348856.html
Copyright © 2011-2022 走看看