从源码看nginx的缓存功能

1.关键的整体流程

cpp 复制代码
1. ngx_http_proxy_handler()                    // 入口
   ↓
2. ngx_http_upstream_init()                    // 初始化上游
   ↓
3. ngx_http_upstream_init_request()              // 初始化请求
   ↓
4. ngx_http_upstream_cache()                   // 缓存处理
   ↓
5. ngx_http_file_cache_exists()               // 检查缓存是否存在
   ↓
6. ngx_http_file_cache_open()                 // 打开缓存文件

2. ngx_http_upstream_init_request

cpp 复制代码
static void
ngx_http_upstream_init_request(ngx_http_request_t *r)
{ 
  // ...
#if (NGX_HTTP_CACHE)

    if (u->conf->cache) {
        ngx_int_t  rc;

        rc = ngx_http_upstream_cache(r, u);
        if (rc == NGX_BUSY) {
            r->write_event_handler = ngx_http_upstream_init_request;
            return;
        }
        r->write_event_handler = ngx_http_request_empty_handler;

        if (rc == NGX_ERROR) {
            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
            return;
        }

        if (rc == NGX_OK) {
            rc = ngx_http_upstream_cache_send(r, u);

            if (rc == NGX_DONE) {
                return;
            }

            if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) {
                rc = NGX_DECLINED;
                r->cached = 0;
                u->buffer.start = NULL;
                u->cache_status = NGX_HTTP_CACHE_MISS;
                u->request_sent = 1;
            }
        }

        if (rc != NGX_DECLINED) {
            ngx_http_finalize_request(r, rc);
            return;
        }
    }

#endif
 // ...

}

3.ngx_http_upstream_cache(缓存处理核心函数)

cpp 复制代码
/*
1. 检查请求方法是否支持缓存
2. 获取缓存配置
3. HEAD请求特殊处理
4. 创建缓存上下文
5. 生成缓存键
6. 检查缓冲区大小
7. 检查缓存绕过条件
8. 设置缓存锁参数
9. 标记为缓存未命中
*/

static ngx_int_t
ngx_http_upstream_cache(ngx_http_request_t *r, ngx_http_upstream_t *u)
{
    ngx_int_t               rc;
    ngx_http_cache_t       *c;
    ngx_http_file_cache_t  *cache;

    c = r->cache;
  
    // 这里加c == NULL判断的用户是可能会出现重入ngx_http_upstream_cache这个函数
    if (c == NULL) {
    
        /*
         1.检查请求方法是否支持缓存
         检查当前请求的方法是否在配置的缓存方法位掩码中. 
         如果不在,直接返回 NGX_DECLINED,跳过缓存处理.
         默认只支持GET和HEAD这两种方法,可以通过 proxy_cache_methods指令配置.
        */
        if (!(r->method & u->conf->cache_methods)) {
            return NGX_DECLINED;
        }
        /*
          2.获取缓存配置
          如果返回 NGX_OK,继续缓存处理
          如果返回 NGX_DECLINED,跳过缓存
          如果返回 NGX_ERROR,记录错误
        */
        rc = ngx_http_upstream_cache_get(r, u, &cache);

        if (rc != NGX_OK) {
            return rc;
        }

        if (r->method == NGX_HTTP_HEAD && u->conf->cache_convert_head) {
            u->method = ngx_http_core_get_method;
        }
        // 这里如果成功处理,r->cache将会被赋值
        if (ngx_http_file_cache_new(r) != NGX_OK) {
            return NGX_ERROR;
        }

        if (u->create_key(r) != NGX_OK) {
            return NGX_ERROR;
        }

        /* TODO: add keys */

        ngx_http_file_cache_create_key(r);

        if (r->cache->header_start + 256 > u->conf->buffer_size) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "%V_buffer_size %uz is not enough for cache key, "
                          "it should be increased to at least %uz",
                          &u->conf->module, u->conf->buffer_size,
                          ngx_align(r->cache->header_start + 256, 1024));

            r->cache = NULL;
            return NGX_DECLINED;
        }

        u->cacheable = 1;

        c = r->cache;

        c->body_start = u->conf->buffer_size;
        c->min_uses = u->conf->cache_min_uses;
        c->file_cache = cache;

        switch (ngx_http_test_predicates(r, u->conf->cache_bypass)) {

        case NGX_ERROR:
            return NGX_ERROR;

        case NGX_DECLINED:
            u->cache_status = NGX_HTTP_CACHE_BYPASS;
            return NGX_DECLINED;

        default: /* NGX_OK */
            break;
        }

        c->lock = u->conf->cache_lock;
        c->lock_timeout = u->conf->cache_lock_timeout;
        c->lock_age = u->conf->cache_lock_age;

        u->cache_status = NGX_HTTP_CACHE_MISS;
    }

    rc = ngx_http_file_cache_open(r);

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http upstream cache: %i", rc);

    switch (rc) {

    case NGX_HTTP_CACHE_STALE:

        if (((u->conf->cache_use_stale & NGX_HTTP_UPSTREAM_FT_UPDATING)
             || c->stale_updating) && !r->background
            && u->conf->cache_background_update)
        {
            if (ngx_http_upstream_cache_background_update(r, u) == NGX_OK) {
                r->cache->background = 1;
                u->cache_status = rc;
                rc = NGX_OK;

            } else {
                rc = NGX_ERROR;
            }
        }

        break;

    case NGX_HTTP_CACHE_UPDATING:

        if (((u->conf->cache_use_stale & NGX_HTTP_UPSTREAM_FT_UPDATING)
             || c->stale_updating) && !r->background)
        {
            u->cache_status = rc;
            rc = NGX_OK;

        } else {
            rc = NGX_HTTP_CACHE_STALE;
        }

        break;

    case NGX_OK:
        u->cache_status = NGX_HTTP_CACHE_HIT;
    }

    switch (rc) {

    case NGX_OK:

        return NGX_OK;

    case NGX_HTTP_CACHE_STALE:

        c->valid_sec = 0;
        c->updating_sec = 0;
        c->error_sec = 0;

        u->buffer.start = NULL;
        u->cache_status = NGX_HTTP_CACHE_EXPIRED;

        break;

    case NGX_DECLINED:

        if ((size_t) (u->buffer.end - u->buffer.start) < u->conf->buffer_size) {
            u->buffer.start = NULL;

        } else {
            u->buffer.pos = u->buffer.start + c->header_start;
            u->buffer.last = u->buffer.pos;
        }

        break;

    case NGX_HTTP_CACHE_SCARCE:

        u->cacheable = 0;

        break;

    case NGX_AGAIN:

        return NGX_BUSY;

    case NGX_ERROR:

        return NGX_ERROR;

    default:

        /* cached NGX_HTTP_BAD_GATEWAY, NGX_HTTP_GATEWAY_TIME_OUT, etc. */

        u->cache_status = NGX_HTTP_CACHE_HIT;

        return rc;
    }

    if (ngx_http_upstream_cache_check_range(r, u) == NGX_DECLINED) {
        u->cacheable = 0;
    }

    r->cached = 0;

    return NGX_DECLINED;
}
相关推荐
j_xxx404_3 分钟前
Linux:进程控制(创建/终止/等待/获取退出信息/多进程)
linux·运维·服务器
BUG_MeDe16 分钟前
LINUX MTU/MSS(1500 1460等)的一些理解
linux·运维·服务器
曹牧17 分钟前
Nginx:正向代理与反向代理
运维·nginx
Ha_To19 分钟前
2026.1.30 搭建docker仓库
运维·docker·容器
lpfasd12321 分钟前
Docker Desktop 在国内使用的囧境:镜像拉取失败、加速器失效与破局之道
运维·docker·容器
江湖有缘26 分钟前
Docker部署SurveyKing调查问卷系统和考试系统
运维·docker·容器
小小管写大大码10 小时前
如何让vscode变得更智能?vscode接入claude实现自动编程
运维·ide·vscode·自动化·编辑器·ai编程·腾讯云ai代码助手
zhang1338308907510 小时前
CG-09H 超声波风速风向传感器 加热型 ABS材质 重量轻 没有机械部件
大数据·运维·网络·人工智能·自动化
Fᴏʀ ʏ꯭ᴏ꯭ᴜ꯭.11 小时前
Keepalived VIP迁移邮件告警配置指南
运维·服务器·笔记
物联网软硬件开发-轨物科技11 小时前
【轨物洞见】告别“被动维修”!预测性运维如何重塑老旧电站的资产价值?
运维·人工智能