项目

概况

个人资料

操作

简介

Fobax 和 darix 构建了一个巧妙的解决方案,实现了非阻塞的 stat() 系统调用,这可能对 lighty 1.4.x 版本有用。
HowtoSpeedUpStatWithFastcgi

然而,之前我们使用 mod_expire 在 HTTP 标头中导出与缓存相关的指令,现在我们必须手动完成此操作。

因此,我们创建了 fcgi-stat-accel 的这个扩展版本。

变更

我们执行 stat() 调用来检索所需信息,以生成 Expires、max-age、Etag 和 Last-Modified 标头。因此,我们也可以移除 fopen() 和 fclose() 系统调用。
(我倾向于认为,无论如何 stat() 都比 fopen()+fclose() 更适合这类任务)
目前我们没有实现文件是否可读的检查,因为我们不需要,但你可能需要..

有些内容是硬编码的(例如只使用 ETAG_USE_SIZE 而非 MTIME 或 INODE)以及过期时间。根据需要调整。

代码


/*
  Originally written by Fobax.
  Edited by darix to support controlling thread count at runtime. 
  Edited by poison and Dieter_be to support some http headers derived from the files.

  Please do not remove any of the above.

  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> 

#include "etag.h" 
#include "buffer.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, headers) \
        FCGX_FPrintF(stream, "%sX-LIGHTTPD-send-file: %s\r\n\r\n", headers, filename);

#define EXPIRATION_TIME (int) 60*60*24*30

int genheaders (char* mybuffer, size_t bufferlen, const char* file)
{
        char timebuf[32]; //possibly unsafe   
        char lastmodbuf[32]; //possibly unsafe
        char etag[128]; //possibly unsafe
        struct stat statbuf;
        time_t exp;
        time_t lastmod;  
        buffer *etag_raw;
        buffer *etag_ok ;

        //create buffers for Etag
        etag_raw = buffer_init();
        etag_ok = buffer_init();

        // Stat the file
        if (stat (file, &statbuf) != 0)
        {
                return -1;
        }

        // Clear the buffer
        memset (mybuffer, 0, bufferlen);

        // Get the local time
        exp = time (NULL) + EXPIRATION_TIME;
        lastmod = statbuf.st_mtime;

        strftime (timebuf, (sizeof (timebuf) / sizeof (char)) - 1, "%a, %d %b %Y %H:%M:%S GMT", gmtime (&(exp)));
        strftime (lastmodbuf, (sizeof (lastmodbuf) / sizeof (char)) - 1, "%a, %d %b %Y %H:%M:%S GMT", gmtime (&(lastmod)));

        etag_create(etag_raw, &statbuf, ETAG_USE_SIZE);
        etag_mutate(etag_ok, etag_raw);

        buffer_free(etag_raw);

        snprintf (mybuffer, bufferlen, "Cache-Control: max-age=%d\r\nETag: \%s\r\nExpires: %s\r\nLast-Modified: %s\r\n", EXPIRATION_TIME, etag_ok->ptr, timebuf , lastmodbuf);

        buffer_free(etag_ok);

        return 0;
}

static void *doit(void *a){
        FCGX_Request request;
        int rc;
        char *filename;
        char extraheaders[192];
        int r;

        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((r = genheaders (extraheaders, 191, filename)) != 0){
                        NOTFOUND(request.out, filename);
        //no error, serve it
                }else{
                        SENDFILE(request.out, filename, extraheaders);
                }

                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,它非常棒!

..fcgi-stat-accel 也是如此!

如果您有任何意见或问题,请随时添加到此页面。

gstrauss将近 9 年前 更新 · 8 次修订