项目

通用

个人资料

操作

FastCGI 接口

模块:mod_fastcgi

描述

lighttpd 为支持 FastCGI 接口的外部程序提供了一个接口。
FastCGI 接口由 https://fastcgi-archives.github.io/FastCGI_Specification.html 定义。
它是一个独立于平台和服务器的,介于 Web 应用程序和 Web 服务器之间的接口。

这意味着与 Apache Web 服务器一起运行的 FastCGI 程序将与 lighttpd 无缝运行,反之亦然。

FastCGI

警告:自 php 5.2.3 起,cgi 二进制文件默认安装为 "/usr/local/bin/php-cgi",而不是 "/usr/local/bin/php"。请务必替换以下文档中的相关部分。

FastCGI 消除了 CGI 程序的许多限制。CGI 程序的问题在于它们必须由 Web 服务器为每个请求重新启动,这导致性能值非常差。

FastCGI 通过保持进程运行并由这个始终运行的进程处理请求来消除此限制。这消除了 fork() 所用的时间以及创建和销毁进程所需的总体启动和清理时间。

CGI 程序通过管道与服务器通信,而 FastCGI 进程使用 Unix 域套接字或 TCP/IP 与 Web 服务器通信。这为您带来了相对于简单 CGI 程序的第二个优势:FastCGI 不必在 Web 服务器本身上运行,而可以在网络中的任何地方运行。

lighttpd 更进一步,提供了一个内部 FastCGI 负载均衡器,可用于在多个 FastCGI 服务器之间平衡负载。与其他解决方案不同,只需 FastCGI 进程位于集群上,而无需整个 Web 服务器。这使得 FastCGI 进程比例如 负载均衡器+apache+mod_php 解决方案拥有更多的资源。

如果您将 FastCGI 与 apache+mod_php 设置进行比较,您应该注意到 FastCGI 提供了额外的安全性,因为 FastCGI 进程可以在与 Web 服务器不同的权限下运行,并且也可以存在于可能与 Web 服务器运行环境不同的 chroot 中。

选项

lighttpd 通过 fastcgi 模块 (mod_fastcgi) 提供 FastCGI 支持。

fastcgi.debug
一个介于 0 到 65535 之间的值,用于设置 FastCGI 模块中的调试级别。目前只使用 0 和 1。使用 1 启用一些调试输出,使用 0 禁用它。

示例

    fastcgi.debug = 1

fastcgi.map-extensions
将多个扩展映射到同一个 fastcgi 服务器

示例

    fastcgi.map-extensions = ( ".php3" => ".php" )

or for multiple

    fastcgi.map-extensions = ( ".php3" => ".php", ".php4" => ".php" )

fastcgi.balance
后端负载均衡算法("fair"、"least-connection"、"round-robin"、"hash" 或 "sticky")

示例

    fastcgi.balance = "least-connection" 

fastcgi.server
用于向其发送请求的主机后端服务器定义;每个后端主机的选项。每个文件扩展名都可以有自己的处理程序。通过为同一个扩展指定多个处理程序来完成负载均衡。

fastcgi.server 部分的结构

 
( <extension> => 
  ( [ <name> => ] 
    ( # Be careful: lighty does *not* warn you if it doesn't know a specified option here (make sure you have no typos)
      "host" => <string> ,
      "port" => <integer> ,                 # starting port number; incremented for each "max-procs" > 1 when "bin-path" is set
      "socket" => <string>,                 # required: either unix "socket" path or "host"+"port" 
      "bin-path" => <string>,               # optional 
      "bin-environment" => <array>,         # optional 
      "bin-copy-environment" => <array>,    # optional 
      "mode" => <string>,                   # optional
      "docroot" => <string> ,               # optional if "mode" is not "authorizer" 
      "check-local" => <string>,            # optional
      "min-procs" => <integer>,             # optional - default is 4
      "max-procs" => <integer>,             # optional - default is 4
      "max-load-per-proc" => <integer>,     # optional - default is 1
      "listen-backlog" => <integer>,        # optional - default 1024
      "broken-scriptfilename" => <boolean>, # optional
      "fix-root-scriptname" => <boolean>,   # optional, since 1.4.23 (option didn't work before 1.4.23)
      "disable-time" => <integer>,          # optional
      "idle-timeout" => <integer>,          # optional
      "allow-x-send-file" => <boolean>,     # optional, (deprecated since 1.4.40)
      "x-sendfile" => <boolean>,            # optional, since 1.4.40
      "x-sendfile-docroot" => <array>,      # optional, since 1.4.40
      "kill-signal" => <integer>,           # optional, default is SIGTERM(15) (v1.4.14+)
      "tcp-fin-propagate" => <boolean>,     # optional, propagate TCP FIN to backend (since 1.4.50) (default: disabled)
      "connect-timeout" => <integer>,       # optional, since 1.4.60 (default: 8)
      "write-timeout" => <integer>,         # optional, since 1.4.60 (default: 0; no timeout)
      "read-timeout" => <integer>,          # optional, since 1.4.60 (default: 0; no timeout)
      "upgrade" => <boolean>,               # optional, since 1.4.74 (default: 0; disabled)
    ),
    ( "host" => ... 
    ) 
  )
)
  • <extension>:文件扩展名或前缀(如果以"/"开头)
  • <name>:可选名称,显示在 mod_status 生成的统计信息中,用于指示哪个后端处理程序处理了此扩展名(例如,名称“php”将显示为“fastcgi.backend.php.*”)
  • "host":FastCGI 进程的 IP(自 1.4.46 起,DNS 名称在 lighttpd 启动时解析为第一个 IP,参见#1417
  • "port":FastCGI 进程在“host”上使用的 tcp 端口(起始端口号;当“bin-path”设置且“max-procs”> 1 时,每个“max-procs”递增)
  • "bin-path":如果本地 FastCGI 未运行,则应启动的本地 FastCGI 二进制文件的路径
  • "socket":Unix 域套接字的路径
  • "mode":FastCGI 协议模式。默认为“responder”,也实现了“authorizer”模式。
  • "docroot":(可选)默认“responder”模式下远程主机的文档根目录。对于“authorizer”模式,它指向授权请求的文档根目录。出于安全原因,建议将此文档根目录保留在 server.document-root 树之外。
  • "check-local":可选,可以是“enable”(默认)或“disable”。如果启用,服务器首先检查本地“server.document-root”树中的文件,如果不存在则返回 404(未找到),并且不回退到 FastCGI。如果禁用,服务器会将请求转发到 FastCGI 接口,而无需此检查。
  • "broken-scriptfilename":以 PHP 可以从中提取 PATH_INFO 的方式破坏 SCRIPT_FILENAME(默认:禁用)
  • "fix-root-scriptname":对于扩展名为"/"的后端(且 check-local 已禁用)使用此选项。
  • "disable-time":在再次检查禁用后端之前等待的时间(对于通过 lighttpd 启动的后端,即设置了 bin-path 的后端,请勿将其设置为 0,否则 lighty 将不会重新启动您的后端)
  • "allow-x-send-file":(在 1.4.40 中已弃用,重命名为“x-sendfile”)
  • "x-sendfile":(自 1.4.40 起)控制是否遵守 X-Sendfile 头。请参阅X-Sendfile。(也遵守已弃用的头 X-LIGHTTPD-send-fileX-Sendfile2(自 1.4.24 起),两者均自 1.4.40 起已弃用)
  • "tcp-fin-propagate":(自 1.4.50 起)将 TCP FIN 传播到后端(默认:禁用)
  • "connect-timeout":在中止与后端建立connect()连接之前等待的秒数(自 1.4.60 起)(默认:8)
  • "write-timeout":尝试写入后端时中止前等待的秒数(自 1.4.60 起)(默认:0;无超时)
  • "read-timeout":尝试从后端读取时中止前等待的秒数(自 1.4.60 起)(默认:0;无超时)
  • "upgrade":允许 HTTP/1.1 "Upgrade: websocket" 或 HTTP/2 扩展 CONNECT 与 :protocol: websocket(自 1.4.74 起)(默认:0;禁用)(注意:如果启用,请考虑调整 server.max-read-idle 和“read-timeout”选项)

如果设置了 bin-path

  • "min-procs":设置要启动的 FastCGI 进程的最小数量(默认:与 max-procs 相同)(自 1.4.46 起)
  • "max-procs":要启动的 FastCGI 进程的上限(默认:4)
  • "idle-timeout":未使用的进程在终止前等待的秒数
  • "bin-environment":在启动的进程环境中添加一个条目
  • "bin-copy-environment":清理环境,只将指定的条目复制到新启动进程的干净环境中
  • "kill-signal":默认情况下,lighttpd 向由 lighttpd 启动的 FastCGI 进程发送 SIGTERM。链接 libfcgi 的应用程序需要使用 SIGUSR1 来终止。这适用于 php <5.2.1、"lua-magnet":http://jan.kneschke.de/projects/lua 等。

示例

同一主机的多个扩展

fastcgi.server = (
  ".php" =>
  (( "host" => "127.0.0.1", 
     "port" => 1026,
     "bin-path" => "/usr/local/bin/php" 
  )),
  ".php4" =>
  (( "host" => "127.0.0.1",
     "port" => 1026
  ))
)

带前缀的示例

fastcgi.server = (
  "/remote_scripts/" =>
  (( "host" => "192.168.0.3",
     "port" => 9000,
     "check-local" => "disable",
     "docroot" => "/" # remote server may use 
                      # its own docroot
  ))
)

请求“http://my.example.org/remote_scripts/test.cgi”将被转发到“192.168.0.3”上的 fastcgi 服务器,并且值“/remote_scripts/test.cgi”将用于 SCRIPT_NAME 变量。远程服务器可能会在其自己的文档根目录前加上此值。在这种情况下,索引文件的处理也是远程服务器的责任。

如果前缀没有以斜杠结尾,则前缀将被视为文件,并且“/test.cgi”将成为 PATH_INFO 而不是 SCRIPT_NAME 的一部分。

“authorizer”模式示例

    fastcgi.server = ( "/remote_scripts/" =>
      (( "host" => "10.0.0.2",
         "port" => 9000,
         "docroot" => "/path_to_private_docs",
         "mode" => "authorizer" 
      ))
    )

请注意,如果指定了“docroot”,则其值将用于传递给 FastCGI 服务器的 DOCUMENT_ROOTSCRIPT_FILENAME 变量。

使用 lighttpd.conf 变量避免每个主机重复配置的示例

var.host_policy = (
  "check-local" => "disable", # sample; default "enable" 
  #"connect-timeout" => 5,    # sample; default: 0
  #"write-timeout"   => 15,   # sample; default: 0
  #"read-timeout"    => 30,   # sample; default: 0
)

var.host_h1  = ( "host" => "192.168.1.101", "port" => 10000 )
var.host_h1 += host_policy
var.host_h2  = ( "host" => "192.168.1.102", "port" => 10000 )
var.host_h2 += host_policy
var.host_h3  = ( "host" => "192.168.1.103", "port" => 10000 )
var.host_h3 += host_policy

fastcgi.server = ( ".php" => ( host_h1, host_h2, host_h3 ) )


fastcgi_opts = (
  "check-local" => "disable", # sample; default "enable" 
  #"connect-timeout" => 5,    # sample; default: 0
  #"write-timeout"   => 15,   # sample; default: 0
  #"read-timeout"    => 30,   # sample; default: 0
)
fastcgi.server = ( ".php" =>
                   (
                     ( "host" => "192.168.1.101", "port" => 10000 ) + fastcgi_opts,
                     ( "host" => "192.168.1.102", "port" => 10000 ) + fastcgi_opts,
                     ( "host" => "192.168.1.103", "port" => 10000 ) + fastcgi_opts,
                   )
                 )

X-Sendfile

如果“x-sendfile”功能处于活动状态,包含完全限定路径的 X-Sendfile 响应头将导致 lighttpd 发送该路径下找到的本地文件,而不是后端生成的内容。“x-sendfile-docroot”(如果已配置)限制了 X-Sendfile 响应头中提供的路径允许的目录树(绝对文件系统路径)。由于 X-Sendfile 响应头允许后端将 lighttpd 可以读取的任何文件发送到客户端,因此您应该设置“x-sendfile-docroot”或仅对受信任的后端启用“x-sendfile”。(例如:您的后端可能无法读取 SSL 密钥文件,但 lighttpd 可以。因此,尽管 lighty 不会在正常请求中提供 SSL 密钥文件,并且后端无法读取它,但后端可以使用 X-Sendfile 获取 SSL 密钥文件)

后端可以
  • 发送“X-Sendfile: /absolute/path/to/file”头(“X-LIGHTTPD-send-file”是“X-Sendfile”的旧的、已弃用的别名)
    如果 lighttpd 可以读取该文件,它将发送指定的文件并使用文件长度作为 Content-Length 头,否则它将忽略该头。X-Sendfile 路径应进行 URL 编码。
  • 发送“X-Sendfile2: filename range”头
    (自 1.4.24 起可用,在 1.4.40 中已弃用,当时静态文件的 HTTP Range 请求已扩展到 X-Sendfile 处理)
    文件名必须进行 URL 编码(包括将 ',' 编码为 '%2c':str_replace(',', '%2c', urlencode($path));
    范围的形式为“start-end”(包含结尾)或“start-” (从开始到文件结束);您必须始终指定范围,对于完整文件使用“0-”。
    示例:X-Sendfile2: /tmp/test.txt 0-499/tmp/test.txt 发送前 500 字节。
    您可以多次发送此头,并带有不同的参数(或者您可以将单个值用“,”组合在一起)。

这是一个支持可续传下载的 PHP 示例实现
(在 lighttpd 1.4.40 中已弃用,因为 lighttpd 内部处理 X-Sendfile 的 HTTP Range 请求)

    
<?php

// Let's assume that $file contains the location of the file to download
$file = "/path/to/file";

// If the HTTP client needs to resume a download it will send a HTTP header called "Range" 
// In case of a download resume (for instance when using wget -c), the header will be like "bytes=2375680-" 
// See RFC2616 section 14.35.2 http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.2
if (isset($_SERVER['HTTP_RANGE']) && preg_match('/\Abytes=[0-9]+-\z/', $_SERVER['HTTP_RANGE'])) {

    // Set Content-Range header which should be like "bytes 2375680-12103815/12103816" 
    // That is start byte | dash | last byte | slash | total byte size
    // See RFC2616 section 14.16 http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.16
    $length = filesize($file);
    $start = substr($_SERVER['HTTP_RANGE'],6,-1);
    $end = $length - 1;
    header("Content-Range: bytes $start-$end/$length");

    // X-Sendfile2 does not set the 206 status code, we have to set it manually
    // See RFC2616 section 10.2.7 http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.7
    header("HTTP/1.1 206 Partial content");

    // The X-Sendfile2 with resume support should be like "/path/to/file 2375680-" 
    header("X-Sendfile2: $file $start-");

} else {

    // Usual X-Sendfile header should be like "/path/to/file" 
    header("X-Sendfile: $file");

}

?>

负载均衡

FastCGI 插件自动提供多个 FastCGI 服务器之间的负载均衡。

fastcgi.server = ( ".php" => 
  (
    ( "host" => "10.0.0.2",
      "port" => 1030
    ),
    ( "host" => "10.0.0.3",
      "port" => 1030 )
    )
  )

要了解负载均衡的工作原理,您可以启用 fastcgi.debug 选项,您将得到类似如下的输出:

  proc: 127.0.0.1 1031  1 1 1 31454
  proc: 127.0.0.1 1028  1 1 1 31442
  proc: 127.0.0.1 1030  1 1 1 31449
  proc: 127.0.0.1 1029  1 1 2 31447
  proc: 127.0.0.1 1026  1 1 2 31438
  got proc: 34 31454
  release proc: 40 31438
  proc: 127.0.0.1 1026  1 1 1 31438
  proc: 127.0.0.1 1028  1 1 1 31442
  proc: 127.0.0.1 1030  1 1 1 31449
  proc: 127.0.0.1 1031  1 1 2 31454
  proc: 127.0.0.1 1029  1 1 2 31447

即使这适用于本地机器上的多个 FastCGI 子进程,以下解释对于远程连接也同样有效。

输出显示

  • IP、端口、Unix 套接字(此处为空)
  • is-local、state(0 - 未设置,1 - 运行中,...)
  • 活动连接(负载)
  • PID

如您所见,列表始终按负载字段排序。

每当请求新连接时,都会选择第一个条目(负载最低的那个),负载增加(got proc: ...),然后列表再次排序。

如果 FastCGI 请求完成或连接断开,FastCGI 进程上的负载会降低,并且列表会再次排序(release proc: ...)

这种行为在代码中非常轻量级,并且仍然非常高效,因为它即使在 FastCGI 服务器具有不同 CPU 的情况下也能使它们保持均衡负载。

FastCGI 与编程语言

准备 PHP 作为 FastCGI 程序

一个最重要的具有 FastCGI 接口的应用程序是 php,可以从 https://php.ac.cn/ 下载。您必须从源代码重新编译 php 才能启用 FastCGI 接口,因为在发行版中通常默认不启用它。

如果您已经在 Web 服务器上安装了可用的 PHP,请执行一个只包含

  <?php phpinfo(); ?>

的简单脚本,然后查找包含 configure 调用的行。您可以将其用作编译的基础。

您必须删除所有 `--with-apxs`、`--with-apxs2` 等会使 PHP 支持 Apache 的内容。添加以下三个开关以编译支持 FastCGI 的 PHP。

  $ ./configure \
    --enable-fastcgi \
    --enable-force-cgi-redirect \
    ...

编译和安装后,通过调用以下命令检查您的 PHP 二进制文件是否包含 FastCGI 支持:

  $ php -v
  PHP 4.3.3RC2-dev (cgi-fcgi) (built: Oct 19 2003 23:19:17)

重要的部分是 (cgi-fcgi)。

启动 FastCGI-PHP

从 1.3.6 版本开始,lighttpd 可以在必要时本地启动 FastCGI 进程

  fastcgi.server = ( ".php" =>
    (( "socket" => "/tmp/php-fastcgi.socket",
        "bin-path" => "/usr/local/bin/php" 
    ))
  )

PHP 提供了 2 个特殊的环境变量,它们控制单个监控进程(PHP_FCGI_CHILDREN)控制下启动的工作进程数量,以及单个工作进程在终止自身之前处理的请求数量。

fastcgi.server = ( ".php" =>
  (( "socket" => "/tmp/php-fastcgi.socket",
     "bin-path" => "/usr/local/bin/php",
     "bin-environment" => ( 
       "PHP_FCGI_CHILDREN" => "16",
       "PHP_FCGI_MAX_REQUESTS" => "10000" 
     )
  ))
)

为了提高启动进程的安全性,您应该只将必要的环境变量传递给 FastCGI 进程。

fastcgi.server = ( ".php" =>
   (( "socket" => "/tmp/php-fastcgi.socket",
      "bin-path" => "/usr/local/bin/php",
      "bin-environment" => ( 
         "PHP_FCGI_CHILDREN" => "16",
         "PHP_FCGI_MAX_REQUESTS" => "10000" ),
      "bin-copy-environment" => (
     "PATH", "SHELL", "USER" )
   ))
 )

配置 PHP

如果您想在 PHP 脚本中使用 PATH_INFO 和 PHP_SELF,则必须配置 php 和 lighttpd。php.ini 需要以下选项:

  cgi.fix_pathinfo = 1

以及 fastcgi.server 配置中的“broken-scriptfilename”选项

fastcgi.server = ( ".php" =>
  (( "socket" => "/tmp/php-fastcgi.socket",
      "bin-path" => "/usr/local/bin/php",
      "bin-environment" => (
        "PHP_FCGI_CHILDREN" => "16",
        "PHP_FCGI_MAX_REQUESTS" => "10000" 
      ),
      "bin-copy-environment" => ( "PATH", "SHELL", "USER" ),
      "broken-scriptfilename" => "enable" 
  ))
)

为什么会这样?“cgi.fix_pathinfo = 0”会给您一个可用的 PATH_INFO 但没有 PHP_SELF。如果您启用它,情况会反过来。为了修复 PATH_INFO,`--enable-discard-path` 需要一个 SCRIPT_FILENAME,这与 CGI 规范相反,它是一个损坏的脚本文件名。通过在 php.ini 中设置“cgi.fix_pathinfo = 1”并在 fastcgi.server 配置中设置“broken-scriptfilename => "enable"”,您将同时获得两者。

请注意,CGI 二进制文件在 5.2.3 中已重命名为 php-cgi,因此您的配置中可能有一个类似 /usr/local/bin/php-cgi 的路径。

Roadsend PHP -- 另一种 PHP

Roadsend PHP 可从 http://code.roadsend.com/pcc 免费获取,它是 PHP 的另一种实现,与 lighttpd 配合使用。它可以直接运行 PHP 代码(如 Zend PHP 所做),但它也可以将 PHP 代码直接编译成本机 FastCGI 二进制文件。

一个可以用于解释服务器上 .php 文件的 Roadsend PHP 配置示例

fastcgi.server = ( ".php" =>
  ( "localhost" =>
    ( "host"        =>     "127.0.0.1",
      "port"        =>     1026,
      "bin-path"    =>     "/opt/roadsend/pcc/modules/fastcgi/pcc.fcgi",
    )
  )
)

要部署编译后的应用程序,您的 lighttpd 服务器需要启用 mod_rewrite 模块。假设您的应用程序名为“myapp”,将编译后的应用程序“myapp.fcgi”及其关联库“libmyapp_u.so”(由 pcc 创建)复制到您的 cgi-bin 目录中

     cp myapp.fcgi libmyapp_u.so /usr/lib/cgi-bin/

我们使用 mod_rewrite 模块来获取应用程序 Web 根目录中所有对 PHP 文件的请求,并将它们重新路由到 FastCGI 二进制文件。我们也允许使用 index.php 文件

# serve index pages
url.rewrite-once = ( "^/myapp/$" => "/myapp/index.php" )

# main fastcgi entry
$HTTP["url"] =~ "^/myapp/.+\.php$" {
  fastcgi.server = ( "/myapp" =>
    ( "localhost" =>
      ( "bin-path"    => "/var/www/localhost/cgi-bin/myapp.fcgi",
        "docroot"     => "/var/www/localhost/htdocs/myapp",
        "host"        => "127.0.0.1",
        "port"        => 1026,
        "check-local" => "disable" 
      )
    )
  )
} # HTTP[url]

外部生成

直接在 Web 服务器中生成 FastCGI 进程有一些缺点,例如:

  • FastCGI 进程只能在本地运行
  • 具有与 Web 服务器相同的权限
  • 具有与 Web 服务器相同的基本目录

一旦您使用单独的 FastCGI 服务器来分担 Web 服务器的一些负载,您就必须通过外部程序(如“spawn-fcgi”)来控制 FastCGI 进程。

"spawn-fcgi" 用于在其自己的环境中启动 FastCGI 进程,并设置用户 ID、组 ID 并更改到另一个根目录 (chroot)。

为了方便起见,应该使用一个包装脚本来处理所有必要的选项。lighttpd 发行版中包含这样一个脚本,名为 spawn-php.sh。

该脚本有一组配置变量,您应该查看一下

  ## ABSOLUTE path to the spawn-fcgi binary
  SPAWNFCGI="/usr/local/sbin/spawn-fcgi" 

  ## ABSOLUTE path to the PHP binary
  FCGIPROGRAM="/usr/local/bin/php" 

  ## bind to tcp-port on localhost
  FCGIPORT="1026" 

  ## bind to unix domain socket
  # FCGISOCKET="/tmp/php.sock" 
  ## number of PHP childs to spawn
  PHP_FCGI_CHILDREN=10

  ## number of request server by a single php-process until
  ## is will be restarted
  PHP_FCGI_MAX_REQUESTS=1000

  ## IP adresses where PHP should access server connections
  ## from
  FCGI_WEB_SERVER_ADDRS="127.0.0.1,192.168.0.1" 

  # allowed environment variables separated by spaces
  ALLOWED_ENV="ORACLE_HOME PATH USER" 

  ## if this script is run as root switch to the following user
  USERID=wwwrun
  GROUPID=wwwrun

  exec $SPAWNFCGI -a $FCGI_WEB_SERVER_ADDRS -p $FCGIPORT -f $FCGIPROGRAM -u $USERID -g $GROUPID

如果您已将变量设置为适合您设置的值,则可以通过调用以下命令启动它:

  $ spawn-php.sh
  spawn-fcgi.c.136: child spawned successfully: PID: 6925

如果您得到“child spawned successfully: PID:”,则 php 进程已成功启动。您应该能在进程列表中看到它们

  $ ps ax | grep php
  6925 ?        S      0:00 /usr/local/bin/php
  6928 ?        S      0:00 /usr/local/bin/php
  ...

进程数应为 PHP_FCGI_CHILDREN + 1。在这里,进程 6925 是并行处理工作的主进程的父进程。并行工作进程的数量可以通过 PHP_FCGI_CHILDREN 设置。由于 PHP 可能存在内存泄漏,一个工作进程在处理 PHP_FCGI_MAX_REQUESTS 个请求后会自动终止。

如果您以 root 用户身份启动脚本,php 进程将以 USERID 用户和 GROUPID 组的身份运行,以放弃 root 权限。否则,php 进程将以您启动脚本的用户身份运行。

由于脚本可能从未知阶段启动,甚至直接从命令行启动,因此它在启动进程之前会清理环境。 ALLOWED_ENV 包含所有应可用于 php 进程的外部环境变量。

“spawn-fcgi”的补丁,使其能够接受自定义配置文件位置,可以在这里找到:

http://forum.lighttpd.net/topic/446#1294

该补丁必须应用到 lighttpd 源代码的源目录中。要修补的文件是“spawn-fcgi.c”,并且需要重新编译 lighty,以便二进制文件也随之编译。

需要在“spawn-php.sh”文件中添加以下内容:

  ## ABSOLUTE path to PHP config
  FCGI_CONFIG="/path/to/php/config/php5.ini" 

在以下内容之后:

  ## ABSOLUTE path to the PHP binary
  FCGIPROGRAM="/etc/lighttpd/php" 

并将文件底部类似行更改为:

  if test x$UID = x0; then
    EX="$SPAWNFCGI -p $FCGIPORT -f $FCGIPROGRAM -u $USERID -g $GROUPID -C $PHP_FCGI_CHILDREN -x $FCGI_CONFIG" 
  else
    EX="$SPAWNFCGI -p $FCGIPORT -f $FCGIPROGRAM -C $PHP_FCGI_CHILDREN -x $FCGI_CONFIG" 
  fi

Perl

对于 Perl,您必须从 CPAN 安装 FCGI 模块。

远程授权器骨架

授权器的基本功能如下(详见 https://fastcgi-archives.github.io/FastCGI_Specification.html#S6.3)。

  #include <fcgi_stdio.h>
  #include <stdlib.h>
  #include <unistd.h>
  int main () {
    char* p;

    while (FCGI_Accept() >= 0) {   
      /* wait for fastcgi authorizer request */

      printf("Content-type: text/html\r\n");

      if ((p = getenv("QUERY_STRING")) == NULL) ||
           <QUERY_STRING is unauthorized>)
           printf("Status: 403 Forbidden\r\n\r\n");

      else printf("\r\n");  
        /* default Status is 200 - allow access */
    }

    return 0;
  }

可以使用 FastCGI 接口提供的任何其他变量进行授权检查。这里只是一个示例。

故障排除

注意
要使用 mod_fastcgi,您需要在 lighttpd.conf 文件中包含行 server.modules += ( "mod_fastcgi" )include "conf.d/fastcgi.conf"(默认情况下应包含行 server.modules += ( "mod_fastcgi" ))。否则,当尝试将 lighttpd 与 fastcgi(例如 php)一起使用时,您将收到错误 WARNING: unknown config-key: fastcgi.server (ignored)

fastcgi.debug 应启用以进行故障排除。

如果您收到

  (fcgi.c.274) connect delayed:  8
  (fcgi.c.289) connect succeeded:  8
  (fcgi.c.745) unexpected end-of-file (perhaps the fastcgi 
     process died):  8

fastcgi 进程接受了连接,但立即将其关闭。如果 FCGI_WEB_SERVER_ADDRS 不包含您连接来源的主机,就会发生这种情况。

如果您收到

  (fcgi.c.274) connect delayed:  7
  (fcgi.c.1107) error: unexpected close of fastcgi connection 
     for /peterp/seite1.php (no fastcgi process on host/port ?)
  (fcgi.c.1015) emergency exit: fastcgi: connection-fd: 5 
     fcgi-fd: 7

fastcgi 进程未在您连接的主机/端口上运行。请检查您的配置。

如果您得到:

  (fcgi.c.274) connect delayed:  7
  (fcgi.c.289) connect succeeded:  7

一切都很好。connect() 调用只是稍微延迟了一点,这是完全正常的。

Chrooted lighttpd、evhost 和非 chrooted fastcgi

如果您尝试在 chrooted lighttpd 中将非 chrooted fastcgi 与 evhost 结合使用,您似乎会遇到问题。要正确支持此配置,lighttpd 在发出 fastcgi 请求时需要在其文档根目录之前加上其 chroot 路径。“docroot”选项可以静态地做到这一点(即,当不使用 evhost 时),但不能用于多个主机。另请参阅有关此问题的#1816

C FastCGI 骨架

// gcc -o skeleton.fcgi skeleton.c -lfcgi
#include <fcgi_stdio.h>
int main (void) {
    while (FCGI_Accept() >= 0) {
        printf("Status: 200 OK\r\n\r\nHello World!\n");
    }
    return 0;
}

lighttpd 命名套接字上的 C/C++ FastCGI

请记住,命名套接字查找操作是通过 Linux 中的 /proc 条目执行的,因此您需要在任何希望使用命名套接字的 chrooted webserver 内部挂载 proc-device,在其他操作系统中也需要所需的 /dev 条目(大多数 chrooted 安装至少需要一个 /dev/null)。lightty mod_fastcgi.c 将在文件描述符 0 上预先打开一个应用程序服务器套接字(查找 "fastcgi.h" 中的 FCGI_LISTENSOCK_FILENO),该套接字已经经过 AF_UNIX 套接字的 socket()/bind()/listen() 循环 - 因此在您的 FastCGI 程序中,您只需要运行 accept()。对于多线程情况,您需要运行 FCGX_Init 一次,并创建任意数量的 FCGX_Request 句柄。

// gcc -I/usr/include/fastcgi -lfcgi testfastcgi.c -o test.fastcgi
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <alloca.h>
#include <fcgiapp.h>
#define LISTENSOCK_FILENO 0
#define LISTENSOCK_FLAGS 0
int main(int argc, char** argv) {
  openlog("testfastcgi", LOG_CONS|LOG_NDELAY, LOG_USER);
  int err = FCGX_Init(); /* call before Accept in multithreaded apps */
  if (err) { syslog (LOG_INFO, "FCGX_Init failed: %d", err); return 1; }
  FCGX_Request cgi;
  err = FCGX_InitRequest(&cgi, LISTENSOCK_FILENO, LISTENSOCK_FLAGS);
  if (err) { syslog(LOG_INFO, "FCGX_InitRequest failed: %d", err); return 2; }

  while (1) {
    err = FCGX_Accept_r(&cgi);
    if (err) { syslog(LOG_INFO, "FCGX_Accept_r stopped: %d", err); break; }
    char** envp;
    int size = 200;
    for (envp = cgi.envp; *envp; ++envp) size += strlen(*envp) + 11;
    char*  result = (char*) alloca(size);
    strcpy(result, "Status: 200 OK\r\nContent-Type: text/html\r\n\r\n");
    strcat(result, "<html><head><title>testcgi</title></head><body><ul>\r\n");

    for (envp = cgi.envp; *envp; ++envp) {
      strcat(result, "<li>"); 
      strcat(result, *envp); 
      strcat(result, "</li>\r\n");
    }

    strcat(result, "</ul></body></html>\r\n");
    FCGX_PutStr(result, strlen(result), cgi.out);
  }

  return 0;
}

和配置记录

  fastcgi.server = (
    "/login" => (
      "test.fastcgi.handler" => (
        "socket" => socket_dir + "test.fastcgi.socket",
        "check-local" => "disable",
        "bin-path" => "/usr/bin/test.fastcgi",
        "max-procs" => 30,
      )
    )
  )

使用 flup 在 lighty 上运行 Python FastCGI

下载 flup http://trac.saddi.com/flup

配置

fastcgi.server = (
    ".py" =>
    (
        "python-fcgi" =>
        (
         "socket" => socket_dir + "fastcgi.python.socket",
         "bin-path" => "test.py",
         "check-local" => "disable",
         "max-procs" => 1,
        )
    ))

test.py

#!/usr/bin/python2.5
def myapp(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/plain')])
    return ['Hello World!\n']

if __name__ == '__main__':
    from flup.server.fcgi import WSGIServer
    WSGIServer(myapp).run()

更多 Python 示例请参见 HowToPythonWSGI

gstrauss 1 年多前更新 · 98 次修订