Rust in Everything
基于 pingora-cache 0.9.0

缓存

这一部分讲 pingora-cache 0.9.0(tag 0.9.0)和 pingora-proxy 里驱动它的 proxy_cache.rs。pingora-cache/src/ 一共 13551 行:lib.rs(3074 行,HttpCache 状态机)、lock.rs(1392 行,缓存锁)、meta.rs 和 cache_control.rs(元数据与 Cache-Control 解析,各一千二百多行)、eviction/(三种淘汰管理器,2321 行),以及键、存储、预测器、准入、最大文件大小这些小文件。proxy_cache.rs 2791 行,其中一半是 Range 处理。pingora-proxy 无条件依赖 pingora-cache,不开缓存的请求也会走一遍 proxy_cache 的开头(一次请求)。

README 把缓存和代理的集成定位为「experimental」,CHANGELOG 0.9.0 的缓存一节标着「(alpha)」,自带的存储 MemCache 只供测试。这一部分按缓存主线(同一个 URL 请求两次)把机制走通,并在本机实测它在并发、失败、超时下的表现。默认行为里有几处要用户自己补的:不检查请求方法、不删 Set-Cookie、不看 Vary。

  1. 缓存状态机:HttpCache 的 11 个相位;proxy_cache 怎么开缓存、算键、查找、等锁、命中时自己写响应;未命中时 HEAD 改 GET、条件头被删,响应经过 cache_http_task 写进存储;过期之后的 304、200、stale-while-revalidate(要缓存锁)和 stale-if-error。
  2. 键、元数据与可缓存性:CacheKey 只哈希 primary,user_tag 不参与,0.9.0 删掉 namespace 后照 rustc 的提示迁移会让所有 URL 撞成一个键;CacheMeta 的时间字段;Cache-Control 的宽松解析和 resp_cacheable 的判断顺序;POST 命中 GET 的缓存、Set-Cookie 串号、Vary 默认关闭。
  3. 缓存锁:锁在 cache_lookup 里拿,写者释放时写下七种状态之一;10 个和 100 个并发未命中只打上游一次;写者一直失败、锁龄或等待时间比上游慢、响应不可缓存时,上游都被打满;MemCache 下写者中途失败,读者拿到截断的响应。
  4. 存储与淘汰:Storage、HandleHit、HandleMiss 和 MemCache;put.rs 不是代理的写入路径;三种淘汰管理器都不看过期时间;预测器、0.9.0 的准入策略;最大文件大小遇到分块响应会掐断下游。
flowchart TB
  pr[process_request] --> pc[proxy_cache]
  pc --> key["键与元数据"]
  pc --> lookup[查找]
  lookup --> lock[缓存锁]
  lookup -->|命中| hit[proxy_cache_hit]
  hit --> store[存储]
  lookup -->|未命中| up[去上游]
  up --> task[cache_http_task]
  task --> key
  task --> store
  store --> ev["淘汰与预测器"]

第一页是骨架,第二页是骨架上的两个判断(存哪、能不能存),第三页处理并发,第四页是数据最终落在哪里。process_request 里缓存分支前后的代码见 process_request;十几个缓存钩子的默认实现见 ProxyHttp;pingora-lru 和 TinyUFO 见 TinyUFO 与内存缓存。hyper、tower 和 tower-http 都不带 HTTP 缓存,这一部分没有和它们的对照。

💬 关联讨论

发起讨论
加载中...