缓存键 (proxy_cache_key): proxy_cache_key 指定了 NGINX 如何根据请求生成一个缓存键。
缓存键 (proxy_cache_key)
proxy_cache_key 指定了 NGINX 如何根据请求生成一个缓存键。
哈希计算
对缓存键进行哈希处理,常使用 MD5 算法。
例如,如果缓存键是 example.com/image.png , 进行 MD5 哈希后会得到一个 32 字符的哈希值(c877fe850c17cb1a4d0b158dd2b64cfb)。
这里有一种特殊情况:

  • 当第一次缓存的时候,得到的哈希,就是MD5(example.com/image.png)
  • 第二次缓存,如果源站有 Vary 响应头 并且 Vary 的值 与第一次缓存的不一致,则会通过 Vary 的值 计算一个新的哈希,如果没有 Vary 响应头,则不会计算新的哈希。

就是说,如果源站响应头没有 Vary,则每次的哈希都是一样的,缓存的是同一个文件,否则会生成新的缓存文件。

就是说,无论第一次的 Accept-Encoding 是什么,都不会被Accept-Encoding影响,第二次会因为Accept-Encoding影响。
第一次缓存算法:

// 直接对缓存键哈希
// cacheKey: 缓存键
func getCacheFileHash(cacheKey string) string {
    keyHash := md5.Sum([]byte(cacheKey))
    return hex.EncodeToString(keyHash[:])
}

第二次缓存算法:

// 计算缓存文件哈希
//
//  cacheKey: 缓存键
//  vary: 响应头的Vary字段 Accept-Encoding | Accept-Charset | Accept-Language | other
//  varyValue: deflate, gzip
func getCacheFileHash(cacheKey string, vary string, varyValue string) string {
    // 转小写 并且去除两边空格
    vary = strings.TrimSpace(strings.ToLower(vary))
 
    // 通过逗号或者空格分隔,然后再通过逗号join
    // 先转小写 并且去除两边空格
    varyValue = strings.TrimSpace(strings.ToLower(varyValue))
    varyValueList := strings.FieldsFunc(varyValue, func(r rune) bool {
        return r == ',' || unicode.IsSpace(r)
    })
    // 再去掉空白字符
    valueList := make([]string, 0, len(varyValueList))
    for _, s := range varyValueList {
        if s == "" {
            continue
        }
        valueList = append(valueList, s)
    }
    varyValue = strings.Join(valueList, ",")
    // 开始 计算
    // hexEncode(md5(md5(cacheKey) + vary + ":" + varyValue + "\r\n"))
    // example.com/image.png Accept-Encoding deflate, gzip
    // =  hexEncode(md5(md5("example.com/image.png") + "accept-encoding:deflate,gzip\r\n"))
    buf := bytes.NewBuffer(nil)
 
    keyHash := md5.Sum([]byte(cacheKey))
    buf.Write(keyHash[:])
    buf.WriteString(vary)
    buf.WriteString(":")
    buf.WriteString(varyValue)
    buf.WriteString("\r\n")
    newHash := md5.Sum(buf.Bytes())
    return hex.EncodeToString(newHash[:])
}

路径层次结构

在 proxy_cache_path 配置中,我们可以看到 levels=1:2。这表示:

  • 生成的缓存路径会被拆分成两级目录结构。
  • 第一层目录使用哈希值的倒数第一位字符(b)。
  • 第二层目录使用哈希值的倒数第三位和倒数第二位字符(cf)。
  • 最终的缓存文件名将使用哈希值(c877fe850c17cb1a4d0b158dd2b64cfb)。

例如,基于缓存键 example.com/image.png,假设其哈希值为 c877fe850c17cb1a4d0b158dd2b64cfb,则缓存文件的路径为:

/var/cache/nginx/b/cf/c877fe850c17cb1a4d0b158dd2b64cfb

参考:

  1. nginx反向代理缓存实现,md5加密规则,gzip压缩问题:https://blog.51cto.com/u_14210437/11868562
  2. 源代码:https://github.com/nginx/nginx/blob/master/src/http/ngx_http_file_cache.c