从源码看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;
}
相关推荐
_OP_CHEN2 小时前
【Git原理与使用】(四)Git 远程操作与标签管理全解析:从分布式协作到版本标记最全攻略
linux·运维·分布式·git·git远程仓库·企业级组件·git标签管理
艾莉丝努力练剑2 小时前
【Linux基础开发工具 (七)】Git 版本管理全流程与 GDB / CGDB 调试技巧
大数据·linux·运维·服务器·git·安全·elasticsearch
weixin_307779132 小时前
Jenkins SSH Build Agents 插件详解:远程构建的利器
运维·开发语言·架构·ssh·jenkins
靈龍靈2 小时前
ELBK部署
运维·ci/cd·jenkins
为什么要内卷,摆烂不香吗2 小时前
sed 流编辑器练习自用
linux·运维·编辑器
智象科技2 小时前
从资源到业务:运维监控体系的差异
大数据·运维·一体化运维·智能运维·多云管理
杜子不疼.2 小时前
【Linux】多机管理终极方案:禁用 root 密码,用面板实现批量部署 + 操作追溯
linux·运维·服务器
艾莉丝努力练剑2 小时前
【Python基础:语法第五课】Python字典高效使用指南:避开KeyError,掌握遍历与增删改查精髓
大数据·运维·人工智能·python·安全·pycharm
后端小张2 小时前
【JAVA进阶】Docker 2025完全指南:从容器入门到企业级实践
java·运维·开发语言·spring·docker·容器·springboot