项目

通用

个人资料

操作

问题

阻塞操作是基于事件的服务器的真正问题。这里,诸如 stat() 之类的阻塞 I/O 就是一个很好的例子。

Fobax 在 #lighttpd 上提出了使用 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",
                    )
                  )
               )

gstrauss近 9 年前更新 · 12 次修订