操作
问题¶
阻塞操作是基于事件的服务器的真正问题。这里的阻塞 I/O,例如 stat() 就是一个很好的例子。
#lighttpd 上的 Fobax 提出了使用 fastcgi 来加速我们的阻塞 stat() 的想法。
想法¶
Lighttpd 将请求传递给一个简单的 fastcgi 应用程序,该应用程序执行 stat() 调用。
然后应用程序发送一个带有原始文件名的 X-LIGHTTPD-send-file 头。
如果 lighttpd 现在对同一个文件进行 stat,信息已经在内核缓存中热加载,我们得到
结果,无需等待磁盘。
一方面,这增加了 fastcgi 到静态文件服务的开销,另一方面它确实加快了 stat() 调用。对于瓶颈是硬盘寻道时间的工作负载,这是一个值得的权衡。
这对于 stat() 调用次数非常高的服务器效果最佳,例如所有必须发送大量小文件(缩略图、图像、广告等)的服务器。对于单驱动器服务器,它不会产生太大影响,但对于多驱动器服务器,它会产生很大影响。在具有 7 个驱动器的机器上进行的一项基准测试显示,比单独使用 lighttpd 提高了 8 倍。通过使用 lighttpd 的 max-workers 功能可以实现类似的结果,尽管这避免了多进程的问题,并且更轻量。
代码¶
Fobax 为我们提供了一个可用的示例代码。我只是添加了运行时修改线程数的功能。
为了更容易支持 spawn-fcgi,我们滥用了 PHP_FCGI_CHILDREN 变量。
/*
compile with:
$ gcc -lfcgi -lpthread fcgi-stat-accel.c -o fcgi-stat-accel
fcgi-stat-accel will use the PHP_FCGI_CHILDREN environment variable to set the thread count.
The default value, if spawned from lighttpd, is 20.
*/
#include "fcgi_config.h"
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include "fcgiapp.h"
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#define THREAD_COUNT 20
#define FORBIDDEN(stream) \
FCGX_FPrintF(stream, "Status: 403 Forbidden\r\nContent-Type: text/html\r\n\r\n<h1>403 Forbidden</h1>\n");
#define NOTFOUND(stream, filename) \
FCGX_FPrintF(stream, "Status: 404 Not Found\r\nContent-Type: text/html\r\n\r\n<h1>404 Not Found</h1>\r\n%s", filename);
#define SENDFILE(stream, filename) \
FCGX_FPrintF(stream, "X-LIGHTTPD-send-file: %s\r\n\r\n", filename);
static void *doit(void *a){
FCGX_Request request;
int rc;
char *filename;
FILE *fd;
FCGX_InitRequest(&request, 0, FCGI_FAIL_ACCEPT_ON_INTR);
while(1){
//Some platforms require accept() serialization, some don't. The documentation claims it to be thread safe
// static pthread_mutex_t accept_mutex = PTHREAD_MUTEX_INITIALIZER;
// pthread_mutex_lock(&accept_mutex);
rc = FCGX_Accept_r(&request);
// pthread_mutex_unlock(&accept_mutex);
if(rc < 0)
break;
//get the filename
if((filename = FCGX_GetParam("SCRIPT_FILENAME", request.envp)) == NULL){
FORBIDDEN(request.out);
//don't try to open directories
}else if(filename[strlen(filename)-1] == '/'){
FORBIDDEN(request.out);
//open the file
}else if((fd = fopen(filename, "r")) == NULL){
NOTFOUND(request.out, filename);
//no error, serve it
}else{
SENDFILE(request.out, filename);
fclose(fd);
}
FCGX_Finish_r(&request);
}
return NULL;
}
int main(void){
int i,j,thread_count;
pthread_t* id;
char* env_val;
FCGX_Init();
thread_count = THREAD_COUNT;
env_val = getenv("PHP_FCGI_CHILDREN");
if (env_val != NULL) {
j = atoi(env_val);
if (j != 0) {
thread_count = j;
};
};
id = malloc(sizeof(*id) * thread_count);
for (i = 0; i < thread_count; i++) {
pthread_create(&id[i], NULL, doit, NULL);
}
doit(NULL);
free(id);
return 0;
}
lighty 配置¶
要使其工作,请将以下类似内容添加到您的 lighttpd.conf 中
## for all files fastcgi.server = ( "" => ( "fastcgi-stat-local" => ( "socket" => "/var/tmp/fcgi-stat-accel.socket", "bin-path" => "/usr/local/bin/fcgi-stat-accel", "bin-environment" => ( "PHP_FCGI_CHILDREN" => "2", ), "max-procs" => 1, "disable-time" => 2, "check-local" => "disable", "allow-x-send-file" => "enable", ) ) )