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-file 和 X-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_ROOT 和 SCRIPT_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