You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Apache自定义缓存模块:如何拦截请求、获取内容并保存至文件

实现Apache独立缓存模块的可行方案

核心思路

你需要结合请求早期钩子和输出过滤器钩子来实现需求:

  1. 早期钩子:在请求处理流程的极早阶段检查缓存文件,若存在则直接返回缓存内容并终止后续流程,避免触发PHP等内容生成脚本。
  2. 输出过滤器钩子:当缓存不存在时,拦截Apache向客户端发送的响应数据,将其写入本地缓存文件,完成缓存生成。

具体钩子选择与实现

1. 请求早期钩子:ap_hook_map_to_storage

选择这个钩子的原因是它在Apache处理请求的早期阶段执行(早于handler、fixups),此时还未触发PHP等内容生成逻辑。实现逻辑:

  • 根据当前请求的URL生成对应的缓存文件路径(比如用MD5/SHA1哈希URL作为文件名,或映射到特定目录结构)。
  • 用APR的文件API检查缓存文件是否存在且有效(可额外添加过期时间判断)。
  • 如果缓存有效,直接调用ap_send_fd发送缓存文件内容,设置r->status为HTTP_OK并返回OK终止后续流程。
  • 如果缓存不存在或无效,返回DECLINED让Apache继续执行后续流程。

示例代码片段:

static int cache_check_handler(request_rec *r) {
    // 跳过非GET请求(按需调整)
    if (strcmp(r->method, "GET") != 0) {
        return DECLINED;
    }

    // 基于URL生成缓存文件路径
    char cache_path[APR_PATH_MAX];
    apr_sha1_ctx_t ctx;
    unsigned char digest[APR_SHA1_DIGESTSIZE];
    char hex_digest[APR_SHA1_DIGESTSIZE * 2 + 1];
    apr_sha1_init(&ctx);
    apr_sha1_update(&ctx, (const unsigned char *)r->uri, strlen(r->uri));
    apr_sha1_final(digest, &ctx);
    apr_hex_encode(hex_digest, (const char *)digest, APR_SHA1_DIGESTSIZE);
    apr_snprintf(cache_path, sizeof(cache_path), "/path/to/your/cache/dir/%s.cache", hex_digest);

    // 检查缓存文件是否存在
    apr_file_t *cache_fd;
    apr_status_t rv = apr_file_open(&cache_fd, cache_path, APR_READ, APR_OS_DEFAULT, r->pool);
    if (rv == APR_SUCCESS) {
        // 设置响应头(按需调整内容类型)
        ap_set_content_type(r, "text/html");
        r->status = HTTP_OK;
        // 发送缓存文件
        ap_send_fd(cache_fd, r);
        apr_file_close(cache_fd);
        return OK; // 终止后续流程
    }

    // 缓存不存在,继续执行
    return DECLINED;
}

// 注册钩子,优先级设为APR_HOOK_EARLY,确保早于其他handler执行
static void register_hooks(apr_pool_t *p) {
    ap_hook_map_to_storage(cache_check_handler, NULL, NULL, APR_HOOK_EARLY);
}

2. 输出过滤器钩子:ap_register_output_filter

当缓存不存在时,需要捕获响应内容并写入缓存文件。输出过滤器可以拦截Apache发送给客户端的每一块数据,我们可以在过滤器中收集数据并写入缓存。

实现逻辑:

  • 定义过滤器上下文结构体,存储缓存文件句柄、缓存路径等信息。
  • 首次收到数据时,创建并打开缓存文件。
  • 每收到一块数据,先写入缓存文件,再传递给下一个过滤器(确保客户端能正常接收响应)。
  • 请求结束时关闭缓存文件。

示例代码片段:

// 过滤器上下文结构体
typedef struct {
    apr_file_t *cache_fd;
    char *cache_path;
} cache_filter_ctx;

static apr_status_t cache_filter(ap_filter_t *f, apr_bucket_brigade *bb) {
    request_rec *r = f->r;
    cache_filter_ctx *ctx = f->ctx;
    apr_status_t rv;

    // 跳过非GET请求或已初始化过的上下文
    if (strcmp(r->method, "GET") != 0 || ctx != NULL) {
        return ap_pass_brigade(f->next, bb);
    }

    // 生成缓存文件路径(和早期钩子逻辑一致)
    char cache_path[APR_PATH_MAX];
    // ... 此处省略路径生成代码,与cache_check_handler中逻辑相同 ...

    // 递归创建缓存目录(如果不存在)
    apr_dir_make_recursive("/path/to/your/cache/dir", APR_OS_DEFAULT, r->pool);

    // 打开缓存文件用于写入
    rv = apr_file_open(&ctx->cache_fd, cache_path, APR_WRITE | APR_CREATE | APR_TRUNCATE, APR_OS_DEFAULT, r->pool);
    if (rv != APR_SUCCESS) {
        // 缓存创建失败,不影响响应发送
        return ap_pass_brigade(f->next, bb);
    }
    ctx->cache_path = apr_pstrdup(r->pool, cache_path);
    f->ctx = ctx;

    // 遍历数据块,写入缓存并传递给下一个过滤器
    apr_bucket *e;
    for (e = APR_BRIGADE_FIRST(bb); e != APR_BRIGADE_SENTINEL(bb); e = APR_BUCKET_NEXT(e)) {
        const char *data;
        apr_size_t len;

        if (APR_BUCKET_IS_EOS(e)) {
            // 请求结束,关闭缓存文件
            apr_file_close(ctx->cache_fd);
            return ap_pass_brigade(f->next, bb);
        }

        rv = apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
        if (rv != APR_SUCCESS) {
            return rv;
        }

        // 写入缓存文件
        apr_file_write(ctx->cache_fd, data, &len);
    }

    return ap_pass_brigade(f->next, bb);
}

// 在register_hooks中注册输出过滤器
static void register_hooks(apr_pool_t *p) {
    ap_hook_map_to_storage(cache_check_handler, NULL, NULL, APR_HOOK_EARLY);
    ap_register_output_filter("CACHE_FILTER", cache_filter, NULL, AP_FTYPE_RESOURCE);
}

关键注意事项

  • 缓存有效性:可在缓存文件名中加入时间戳,或在缓存文件头部写入过期时间,在早期钩子中判断是否过期。
  • 并发安全:用APR的apr_file_lock机制避免多个请求同时生成同一缓存文件的竞争问题。
  • 缓存清理:可通过定时任务(如cron)清理过期缓存,或在早期钩子中检查到过期缓存时直接删除。
  • 模块独立性:所有逻辑基于Apache和APR核心API实现,不依赖第三方模块,满足独立自包含需求。

内容的提问来源于stack exchange,提问作者user171453

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 13:15:31