【问题标题】:FastCGI, Perl and ExitFastCGI、Perl 和退出
【发布时间】:2021-07-07 02:44:50
【问题描述】:

在过去的几周里,我一直在磨练我用于项目的大型、几十年前的代码库的性能,这里有人建议我应该看看 FastCGI 或 HTTP::Engine 之类的东西。我发现使用 FastCGI 非常简单,但有一个令人烦恼的问题,我发现答案不一。

我读过的一些文档说你永远不应该在通过 FastCGI 运行的脚本上调用exit,因为这会损害保持它持久加载的整个概念。其他人说没关系。我的代码在很多地方都使用了 exit,确保没有任何东西继续执行很重要。例如,我有调用授权检查的受限访问组件:

use MyCode::Authorization;
our $authorization = MyCode::Authorization->new();

sub administration {
    $authorization->checkCredentials();

    #...Do restricted access stuff.
}

为了尽可能避免代码中出现错误,允许某人在不应访问这些功能时出现错误,checkCredentials 在生成用户友好的响应后以 exit() 结束该过程使用登录页面 if 答案是用户没有适当的凭据。例如:

sub checkCredentials {
   #Logic to check credentials

   if ($validCredential) {
       return 1;
   }
   else {
       # Build web response.
       # Then:
       exit;
   }
}

}

我使用它是为了不会意外忽略导致安全漏洞的后续操作。目前,调用例程可以安全地假设它只从checkCredentials 获得控制权如果提供了正确的凭据。

但是,我想知道是否需要删除这些调用才能充分利用 FastCGI。 FCGI 的 $req->Finish()(或 PSGI 中的 HTTP::Engine 等效项)是否可以替代?

【问题讨论】:

标签: perl fastcgi psgi


【解决方案1】:

我听说你永远不应该在通过 FastCGI 运行的脚本上调用 exit,

您不希望进程退出,因为使用 FastCGI 的目的是使用单个进程来处理多个请求(以避免加载时间等)。

所以您要做的是覆盖 exit 以便结束您的请求特定代码,而不是 FastCGI 请求循环。


您可以覆盖exit,但您必须在编译时这样做。所以使用一个标志来表示覆盖是否处于活动状态。

our $override_exit = 0;
BEGIN { 
   *CORE::GLOBAL::exit = sub(;$) {
      die "EXIT_OVERRIDE\n" if $override_exit;
      CORE::exit($_[0] // 0);
   };
}

while (get_request()) {
   # Other setup...

   eval {
      local $override_exit = 1;
      handle_request();
   };
    
   my $exit_was_called = $@ eq "EXIT_OVERRIDE\n";
   log_error($@) if $@ && !$exit_was_called;
    
   log_error("Exit called\n") if $exit_was_called;

   # Other cleanup...
}

但这会产生一个可能无意中捕获的异常。所以让我们改用last

our $override_exit = 0;
BEGIN { 
   *CORE::GLOBAL::exit = sub(;$) {
      no warnings qw( exiting );
      last EXIT_OVERRIDE if $override_exit;
      CORE::exit($_[0] // 0);
   };
}

while (get_request()) {
   # Other setup...

   my $exit_was_called = 1;
   EXIT_OVERRIDE: {
      local $override_exit = 1;
      eval { handle_request() };
      log_error($@) if $@;
      $exit_was_called = 0;
   }

   log_error("Exit called\n") if $exit_was_called;

   # Other cleanup...
}

【讨论】:

  • 这确实是一段很酷的代码,尽管我仍然不确定替代方案,这是链接到“重复”问题堆栈溢出注释的地方并没有完全达到我的观点困惑。如果我覆盖exit,我如何确保相同的等价物?例如。该处理不会继续,但在 FastCGI 进程的情况下,我会立即返回我的阻塞 while 循环以等待另一个请求。
  • 我重新打开了它,但使用替代品没有意义,因为您应该已经在使用它了。
  • 相当于什么?如果您问“如果我覆盖退出,我如何确保我立即返回我的阻塞 while 循环以等待另一个 request.equivalent?”,这正是答案向您展示的做。
  • 抱歉,我不是很清楚。所以 handle_request() 部分确保我回到了我的 while 循环?
  • 哦,我想我应该提到{ ... }(作为一个声明,而不是哈希构造函数)在技术上是一个循环。在文档(IIRC)中称为“裸循环”,它是一个只执行一次的循环。所以last 只是退出了那个循环。 (标签处理嵌套。)
猜你喜欢
  • 2018-03-27
  • 2014-05-25
  • 1970-01-01
  • 2020-05-19
  • 2017-08-02
  • 2013-12-21
  • 2020-07-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多