【问题标题】:Perl FCGI Exit Without DieingPerl FCGI 退出而不死
【发布时间】:2014-03-13 10:43:52
【问题描述】:

如果使用 Perl FCGI,如何在不使用 exit 的情况下结束脚本。在搜索了几天后,我发现唯一的解决方案是跳转到主脚本中的标签处。下面是主index.fcgi的代码。

    $fcgi_requests = 0; # the number of requests this fcgi process handled.
$handling_request = 0;
$exit_requested = 0;
$app_quit_request = 0; # End the application but not the FCGI process

# workaround for known bug in libfcgi
while (($ignore) = each %ENV) { }

$fcgi_request = FCGI::Request();
#$fcgi_request = FCGI::Request( \*STDIN, \*STDOUT, \*STDERR, \%ENV, $socket);

sub sig_handler {
    my ($callpackage, $callfile, $callline) = caller;
    if ($app_quit_request) {
        $app_quit_request = 0;
        goto ABORTLABEL;
    }

    $exit_requested = 1;
    exit(0) if !$handling_request;
}

$SIG{USR1} = \&sig_handler;
$SIG{TERM} = \&sig_handler;
$SIG{PIPE} = 'IGNORE';

#The goal of fast cgi is to load the program once, and iterate in a loop for every request.
while ($handling_request = ($fcgi_request->Accept() >= 0)) {
    process_fcgi_request();
    $handling_request = 0;
    last if $exit_requested;
    #exit if -M $ENV{SCRIPT_FILENAME} < 0; # Autorestart
}

$fcgi_request->Finish();
exit(0);
#=========================================================#
sub process_fcgi_request() {
    $fcgi_requests++;

    # dispatch current request
    my_app();

    $fcgi_request->Finish();
}
#=========================================================#
# let it think we are done, used by abort
ABORTLABEL:
    $fcgi_request->Finish();
#=========================================================#

主要请求是我想停止从子 insidi 模块内部执行程序,这些模块可能会被长深度调用,例如在帐户模块中的登录函数内部。 当然我不能使用 exit 因为它会终止 fcgi 进程,我尝试了所有错误并抛出并尝试模块都使用 die 这也结束了进程。当然我可以使用每个 sub 的返回,但这需要为 fcgi 重写整个程序。

【问题讨论】:

    标签: perl fastcgi


    【解决方案1】:

    在 Perl 中为异常建模的常规方法是在 eval BLOCK 内调用 die,它会捕获 die,因此不会终止进程。它只会终止eval,然后程序会立即继续运行。据我所见,CPAN 上的异常处理模块大多是围绕这个基本功能的包装器,以赋予它不同的语法或更容易编写 catch 块。因此,我很惊讶这些对你不起作用。您是否真的尝试过它们,或者您是否只是假设die 总是会终止进程?这个名字有点误导,因为它的真正意思是“抛出异常”。只要您在eval 之外执行此操作,解释器就会捕获它,并且它的唯一响应是终止进程。

    eval {
      say "Hello world";
      die;
      say "Not printed";
    };
    say "Is printed";
    

    您不想在eval 中调用exit。什么都没有。

    我建议重写 FCGI 的整个控制流。代码的生命周期发生了显着变化,因此您必须进行一定量的修改以确保变量重用正常工作并且不会泄漏内存。通常,最好提前做,而不是花几天时间去追踪奇怪的错误。

    【讨论】:

    • 感谢 Matthew 提供的详细信息。这个问题是一个旧的大型应用程序,很难以完整的 OO 方式重写它,我只需要在子模块内的任何特定点结束执行。上述逻辑暂时有效,但是,这是正确且可接受的逻辑吗?
    【解决方案2】:

    经过几个问题和深入研究,我得到了这个解决方案。此编码示例允许您从任何嵌套级别的调用返回。模块Scope::Upper 是 XS,所以应该很快。

    use Scope::Upper qw/unwind CALLER/;
    
    sub level1 {
        print "before level 1 \n";
        level2();
        print "after level 1 \n";
    }
    
    sub level2 {
        print "before level 2 \n";
        level3();
        print "after level 2 \n";
    }
    
    sub level3 {
        print "before level 3 \n";
        level4();
        print "after level 3 \n";
    }
    
    sub level4 {
        print "before level 4 \n";
    
        #unwind CALLER 2;
    
        my @frame;
        my $i;
        #$i++ while @frame = caller($i);# and $frame[0] ne "main";
        $i++ while @frame = caller($i);
        #print "i=: $i \n";
        #unwind CALLER (@frame ? $i : $i - 1);
        unwind CALLER $i-1;
    
        print "after level 4 \n";
    }
    
    print level1();
    

    如果您运行此代码,输出将是:

    before level 1 
    before level 2 
    before level 3 
    before level 4 
    

    您可以使用以下方法返回任何更高级别:

    my intLevel = 2;    
    unwind CALLER intLevel;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-07
      • 2015-05-10
      • 1970-01-01
      • 1970-01-01
      • 2017-03-04
      • 2015-05-10
      • 2014-09-30
      • 2020-10-04
      相关资源
      最近更新 更多