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
    
  • 相关阅读:
    快速幂模板
    部分有关素数的题
    POJ 3624 Charm Bracelet (01背包)
    51Nod 1085 背包问题 (01背包)
    POJ 1789 Truck History (Kruskal 最小生成树)
    HDU 1996 汉诺塔VI
    HDU 2511 汉诺塔X
    HDU 2175 汉诺塔IX (递推)
    HDU 2077 汉诺塔IV (递推)
    HDU 2064 汉诺塔III (递推)
  • 原文地址:https://www.cnblogs.com/hzcya1995/p/13348856.html
Copyright © 2011-2022 走看看