后浪笔记一零二四

0. 验证器响应头部

  1. ETag响应头部: 资源的指纹 以强调性能为主的Nginx,把修改时间与文件大小拼接在一起作为ETag。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    ETag = entity-tag
    entity-tag = [ weak ] opaque-tag
    weak = %x57.2F   也就是W/
    opaque-tag = DQUOTE *(etagc) DQUOTE
    etagc = %x21-7E / obs-text
    
    例如: 
         强验证器:   ETag: "xyzzy"
              服务器上的资源表述只要有变动,那么以旧的验证头部访问一定会导致验证不过
         弱验证器:   ETag: W/"xyzzy"
              服务器上资源变动时,允许一定程度上仍然可以验证通过
    

    nginx中的etag指令:

    Syntax:   etag on| off;
    Default:  etag on;
    Context:  http,server,location
    
    nginx中ETag指纹的生成规则:
    ngx_sprintf(etag->value.data, "\"%xT-%xO\"",
                        r->headers_out.last_modified_time,
                        r->headers_out.content_length_n)
    
  2. Last-Modified响应头部 Last-Modified = HTTP-date

    • 表示对应资源表述的上次修改时间
    • Last-Modified不能晚于Date的值 Date = HTTP-date
    • 表示响应包体生成的时间

1. 条件请求

1.1 响应头部

  1. Accept-Range响应头部:表示是否支持Range

    • 例如:
      • Accept-Ranges:bytes: 支持
      • Accept-Ranges:none: 不支持
  2. Content-Range响应头

    • 使用206 Partial Content状态码 显示当前片断包体在完整包体中的位置
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    Content-Range = byte-content-range
       byte-content-range = bytes-unit SP (byte-range-resp / unsatisfied-range)
            byte-range-resp = byte-range "/" (complete-length / "*")
                 byte-range = first-byte-pos "-" last-byte-pos
                 complete-length = 1*DIGIT
            unsatisfied-range:
                 * 示例: */1234
    例如:
      Content-Range: bytes 42-1233/1234
      Content-Range: bytes 42-1233/*
    
    • 使用416 Range Not Satisfiable状态码 请求范围不满足实际资源的大小,其中Content-Range中的complete-length 显示完整响应的长度,例如: Content-Range:bytes */1234

    • 使用200 OK状态码 服务器不支持Range请求时,则以200返回完整的响应包体

1.2 请求头部

  1. Range请求头的使用方法:Range: bytes=0-499 基于字节,设包体总长度为10000

    • 第1个500字节: bytes=0-499
    • 第2个500字节: bytes=500-999 bytes=500-600,601-999 bytes=500-700,601-999 * 有100个字节的重合,有逗号以后,会分别取到两块的资源表述,并需要客户端进行组合。
    • 最后1个500字节: bytes=-500 bytes=9500-
    • 仅要第1个和最后1个字节: bytes=0-0,-1 注意:Range头部常常与If-Unmodified-Since或者If-Match头部一起使用
  2. If-Range请求头 如果客户端已经下载了部分的响应,过了段时间继续下载,由于有一个时间差,所以下载完的那一部分, 在服务器端可能被修改了,这时候就需要使用If-Range条件请求去判断,前一部分,是否已经过期了。

1
2
3
If-Range = entity-tag / HTTP-date
   entity-tag:已下载body的指纹,即ETag
   HTTP-date: Last-Modified
  1. 多重范围与multipart

    • 请求: Range: bytes=0-50,100-150
    • 响应: Content-Type: multipart/byteranges; loundary=…
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    $ curl xxx/letter.txt -H 'Range: bytes=0-5,10-15'
    HTTP/1.1 206 Partial Content
    Connection: keep-alive
    ETag: "5cc3f0b5-1b"
    Content-Type: multipart/byteranges; boundary=0...6096
    Content-Length: 238
    
    响应体: --00000000000000006097
            Content-Type: text/plain; charset=utf-8
            Content-Range: bytes 0-5/27
    
            abcdef
            --00000000000000006097
            Content-Type: text/plain; charset=utf-8
            Content-Range: bytes 10-15/27
    
            klmnop
            --00000000000000006097--
    
  2. 其他条件头部

1
2
3
4
5
* If-Match = "*" / 1#entity-tag
    * 用在GET/HEAD/PUT请求中
* If-Unmodified-Since = HTTP-date
    * 只用在GET/HEAD请求中
    *If-Range请求头搭配使用时,用来确保新的请求片段来自于未经修改的文档。

1.3 下载和更新

场景1: 下载资源
              Client                                    Server
                |                                          |
首次下载:        |----------------------------------------->|
不是条件请求     |GET /doc HTTP1.1                          |
                |                                          |返回资源,同时
                |<-----------------------------------------|设置验证响应头部
                |               HTTP/1.1 200 OK            |Accept-Ranges头部表明该下载是可恢复的
                |               Last-Modified: data        |
                |               ETag: "xyz"                |
                |               Accept-Ranges:bytes        |
                |                                          |
                |                                          |
                | /\   /\   /\   /\   /\  /\  /\  /\  /\  /|
                |/  \ /  \ /  \ /  \ /  \/  \/  \/  \/  \/ |
                |在接收完全部资源之前,下载被中断,           |
                |但是已经接收到一些数据(假设是2782字节)     |
                |                                          |
恢复下载:        |----------------------------------------->|
基于条件头部     |GET /doc HTTP/1.1                         |
和Range头部      |Ranges: 2783-                            |客户端对服务器说,如果你的本地该资源的最后修改时间等于或小于我给你的这个时间,
验证资源的       |If-Unmodified: date                       |如果你的本地该资源的指纹和我的匹配,才需要206响应我要求的range范围的字节内容,
完整性           |If-Match: "xyz"                           |否则200响应完整的文件给我
                |                                          |
                |<-----------------------------------------|发回部分资源,(资源没有发生变化)
                |         HTTP/1.1 206 Partial Content     |从之前下载停止的
                |         Last-Modified:date               |地方开始
                |         ETag: "xyz"                      |
                |         Accept-Ranges:bytes              |
                |         Content-Range: 2783-5678/5679    |
                |                                          |

恢复下载:        |----------------------------------------->|
基于条件头       |GET /doc HTTP/1.1                         |
和Range头部      |Ranges: 2783-                            |
验证资源的       |If-Unmodified-Since: date                 |
完整性           |If-Match: "xyz"                           |
                |                                          |
                |<-----------------------------------------|资源发生了变化
                |       HTTP/1.1 412 Precondition Failed   |
                |                                          |
  重新下载       |----------------------------------------->|
                |GET /doc HTTP/1.1                         |
                |                                          |
                |<-----------------------------------------|发回新的资源
                |                    HTTP/1.1 200 OK       |而且是完整的响应
                |                    Last-modified:data2   |
                |                    ETag: "xyz2"          |

通过If-Range头部可以避免2次请求交互带来的损耗
              Client                                    Server
                |                                          |
恢复下载:        |                                          |
基于条件头       |----------------------------------------->|
和Range头部      |GET /doc HTTP/1.1                         |
验证资源的       |Ranges: 2783-                             |
完整性           |If-Range: "xyz"                           |
                |                                          |资源已经发生变更,
                |<-----------------------------------------|直接发回新的资源,
                |                   HTTP/1.1 200 OK        |省略发送412状态码的动作
                |                   Last-Modified: date2   |
                |                   ETag: "xyz2"           |

场景2:更新资源意味着2步操作:先获取资源,再把本地修改后的资源提交,由于是2步操作,所以会出现竞态条件:
            Client1                         Server                              Client2
               |                                |                                   |
 更新资源的请求 |------------------------------->|                                   |
               |GET /doc HTTP/1.1               |<----------------------------------|更新资源的请求
               |                                |               GET /doc HTTP/1.1   |
               |                                |---------------------------------->|
               |                                |HTTP/1.1 200 OK                   []
               |<-------------------------------|Last-Modified: date               []
              []          HTTP/1.1 200 OK       |ETag: "xyz"                       []
      本地修改 []          Last-Modified: date   |                                  []
              []          ETag: "xyz"           |                                  []
              []                                |                                  []本地更新
    被修改的资源|------------------------------->|                                  []
    更新了      |     PUT /doc HTTP/1.1          |                                  []
               |                                |                                  []
               |<-------------------------------|                                  []
               |    HTTP/1.1 204 No Content     |<----------------------------------|被修改的资源
               |    Last-Modified: date2       /|              PUT /doc HTTP/1.1    |更新了
               |    ETag: "xyz2"              / |                                   |
               |                          ->--  |---------------------------------->|
      _________|_________________________/      |HTTP/1.1 204 No Content            |
      |竞态条件!         |                       |Last-Modified: date3               |
      |客户端2的改变默认 |                        |ETag: "xyz3"                       |
      |覆盖了客户端1的   |                        |                                   |
      --------------------

使用乐观锁来解决上面的竞态条件:
            Client1                         Server                              Client2
               |                                |                                   |
 更新资源的请求 |------------------------------->|                                   |
               |GET /doc HTTP/1.1               |<----------------------------------|更新资源的请求
               |                                |               GET /doc HTTP/1.1   |
               |                                |---------------------------------->|
               |                                |HTTP/1.1 200 OK                   []
               |<-------------------------------|Last-Modified: date               []
               []         HTTP/1.1 200 OK       |ETag: "xyz"                       []
      本地修改  []         Last-Modified: date   |                                  []
               []         ETag: "xyz"           |                                  []
               []                               |                                  []本地更新
    被修改的资源|------------------------------->|                                  []
    更新了      |     PUT /doc HTTP/1.1          |                                  []
               |     If-Match: "xyz"            |                                  []
               |     If-Unmodified-Since:date   |                                  []
               |                                |                                  []
               |<-------------------------------|                                  []
               |    HTTP/1.1 204 No Content     |<----------------------------------|
               |    Last-Modified: date2        |          PUT /doc HTTP/1.1        |客户端对服务器说,如果你的本地资源的最后修改时间
               |    ETag: "xyz2"                |          If-Match: "xyz"          |等于或小于我给你的这个时间,如果你的本地该资源的
               |                                |          If-Unmodified-Since: data|指纹和我的匹配,才需要处理我的这个请求(并返回204
               |                                |                                   | No Content响应码),否则响应412错误码告诉我
               |                                |                                   |
               |                                |---------------------------------->|被拒绝了,因为
               |                                |HTTP/1.1 412 Precondition Failed   |服务器的资源已经改变了

乐观锁还能解决首次上传的问题:
            Client1                         Server                             Client2
               |                               |                                   |
 上传新的文档:  |------------------------------>|                                   |
               |PUT /doc HTTP/1.1              |                                   |
               |If-None-Match: *               |                                   |
               |                               |                                   |
    上传成功:   |<------------------------------|                                   |
               |   HTTP/1.1 201 Created        |<----------------------------------|上传新的文档
               |   Last-Modified: date         |PUT /doc HTTP/1.1                  |
               |   ETag: "xyz"                 |If-None-Match: *                   |
               |                               |                                   |
               |                               |---------------------------------->|被拒绝了,因为
               |                               | HTTP/1.1 412 Precondition Failed  |该文档已经存在

2. 缓存

2.1 什么样的响应才会被缓存RFC7234

  1. 什么样的响应会被缓存
  • 请求方法可以被缓存理解(不只于GET方法)
  • 响应码可以被缓存理解(404、206也可以被缓存)
  • 响应与请求的头部没有指明no-store
  • 响应中至少应含有以下头部中的1个或者多个:
    • Expires、max-age、s-maxage、public
    • 当响应中没有明确指示上面4个头部时,如果响应码非常明确,也可以缓存
  • 如果缓存在代理服务器上
    • 不含有private
    • 不含有Authorization
  • Pragma头部
    1
    2
    3
    4
    5
    
    Pragma = 1*pragma-directive
    pragma-directive = "no-cache"/extension-pragma
       extension-pragma = token["="(token/quoted-string)]
    
    * Pragma: no-cache与Cache-Control: no-cache意义相同
    
  1. 如何给请求匹配缓存
  • URI是匹配的
    • URI作为主要的缓存关键字,当一个URI同时对应多份缓存时,选择日期最近的缓存
    • 例如Nginx中默认的缓存关键字: proxy_cache_key $scheme$proxy_host$request_uri;
  • 缓存中的响应允许当前请求的方法使用缓存
  • 缓存中的响应Vary头部指定的头部必须与请求中的头部相匹配:
    1
    
    Vary = "*" / 1*field-name
    
    Vary: *意味着一定匹配失败
  • 当前请求以及缓存中的响应都不包含no-cache头部(Pragma:no-cache或者Cache-Control: no-cache)
  • 缓存中的响应必须是以下三者之一:
    • 新鲜的(时间上未过期)
    • 缓存中的响应头部明确告知可以使用过期的响应(如Cache-Control:max-stale=60)
    • 使用条件请求去服务器验证请求是否过期,得到304响应
  1. 支持复杂操作的Vary缓存(压缩场景)
    Client1                     nginx Cache                      upstream Server
请求  |                             |                                   |
某文档|---------------------------->|缓存是空的,把请求发往上游           |
      |GET /doc HTTP/1.1            |---------------------------------->|
      |Accept-Encoding: *           |                                   |
      |                             |                                   |
      |<----------------------------|<----------------------------------|
      |                             |   |  HTTP/1.1 200 ok              |
      |                             |   |  Content-Encoded: gzip        |
      |                             |   |  Vary: Content-Encoding       |
      |                             |   |                               |
      |                             |   +-->[gzip]{/doc}                |
      |                             |         ^ 使用URL和Encoding        |
      |                             |         | 作为缓存的key            |
   Client2                          |         |                         |
请求  |                             |         |                         |
某文档|---------------------------->|没有匹配的缓存,把请求发往上游        |
      |GET /doc HTTP/1.1            |---------------------------------->|
      |Accept-Encoding: br          |                                   |
      |<----------------------------|<----------------------------------|
      |                             |   |  HTTP/1.1 200 ok              |
      |                             |   |  Content-Encoded: br          |
      |                             |   |  Vary: Content-Encoding       |
      |                             |   |                               |
      |                             |   +-->[gzip]{/doc}                |
      |                             |       [br]{/doc}                  |
      |                             |       ^                           |
   Client3                          |       |缓存匹配,                  |
请求  |                             |       ^返回指定的数据               |
某文档|---------------------------->|------>+                            |
      |GET /doc HTTP/1.1            |       V                           |
      |Accept-Encoding: br          |       |                           |
      |<----------------------------|------<+                           |
  1. Warning头部:对响应码进行补充(缓存或包体转换)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
* Warning = 1#warning-value
    * warning-value = warn-code SP warn-agent SP warn-text [SP warn-date]
         warn-code = 3DIGIT
         warn-agent = (uri-host[ ":" port ])/pseudonym
         warn-text = quoted-string
         warn-date = DQUOTE HTTP-date DQUOTE
* 常见的warn-code
    * Warning: 110 - "Response is Stale"
    * Warning: 111 - "Revalidation Failed"
    * Warning: 112 - "Disconnected Operation"
    * Warning: 113 - "Heuristic Expiration" 预估的过期
    * Warning: 199 - "Msicellaneous Warning"
    * Warning: 214 - "Transformation Applied"
    * Warning: 299 - "Miscellaneous Persistent Warning"

2.2 私有缓存和共享缓存

2.2.1 私有缓存:仅供一个用户使用的缓存,通常只存在于如浏览器这样的客户端上

浏览器处理缓存的流程:

请求头部:
* If-Modified-Since = HTTP-date
    只用在GET/HEAD请求中
* If-None-Match = "*" / 1#entity-tag
    用在GET/HEAD/PUT请求中
响应头部:
* Vary
    它决定了对于未来的一个请求头,应该用一个缓存的回复,还是向源服务器请求一个新的回复
    它被服务器用来表明在内容协商算法中选择一个资源代表的时候应该使用哪些头部信息
    注意: 在响应状态码为304 Not Modified的响应中,也要设置Vary首部,而且要与相应的200 OK响应设置得一模一样
    - Vary: *
        * 等价于Cache-Control:private
        * nginx: 若没有通过proxy_ignore_headers设置忽略,则不对响应进行缓存
            - 例如默认是不缓存含Set-Cookie头部的响应的,除非在proxy_ignore_headers上设置
    - Vary: <header-name>,<header-name>,...

{浏览器请求}
     V
  [有缓存]
     V
<是否过期?>---Y---><ETag?>---Y--->[向Web服务器请求      ]->-+ 客户端对服务器说,如果你的本地文件的last modified
     |             N |            [带if-None-Match    ]   | 时间比我给的这个时间晚(等于或者小于),如果你的本地
     |N              V                                    | 文件的指纹和我给你的指纹不一致,才需要200响应我文件
     |        <Last-Modified?>-Y->[向web服务器请求     ]->-+ 内容,否则304响应我告诉文件没变化就好了。
     |             N |            [带If-Modified-Since]   |
     V               V                                    |
[从缓存读取] [向Web服务器请求]                     +--<200 or 304>
     |               |                     有更新|         |304无更新
     |               V                           |        V
     |      [请求响应,缓存协商]<---------200-----+   [从缓存读取]
     |               |                                    |
     +---------------+------------------------------------+
                     V
                  {呈现}

2.2.2 共享缓存:可以供多个用户的缓存,存在于网络中负责转发消息的代理服务器

nginx中的not_modified过滤模块处理条件请求头的流程:

Syntax:  if_modified_since off | exact | before;
Default: if_modified_since exact;
Context: http,server,location

* off
    * 忽略请求中的if_modified_since头部
* exact
    * 精确匹配if_modified_since头部与last_modified的值
* before
    * 若if_modified_since大于等于last_modified的值,则返回304

{含有if_unmodified_since?}
         V
        <?>---Y-->{比较last_modified_time和它}--><?>--大于---->{返回412}
         N                                        V            ^ Precondition Failed
         |<------------<------小于等于-----<-------+            |
         V                                                     |
    {含有if_match?}                                            |
         V                                                     ^
        <?>------Y----->{比较ETag}-----><?>-------不相等--------+
         |                              V
         |<------------------相等-------+
         |                                                  处理上传更新场景
---------|--------------------------------------------------------------------------
         |                                                  处理缓存场景
         V
{含有if_modified_since或者if_none_match?}
         V
        <?>----N------------------------------------------>{返回200}
         |                                                     ^
         Y                                                     |
         V                                                     |
{判断if_modified_since指令的值:off|exact|before}                |
{比较if_modified_since与last_modified_time     }               |
         |  1.off                                              |
         |  2.exact &&if_modified_since!=last_modified_time    |
         |  3.before&&if_modified_since< last_modified_time    |
        <?>----------------------------------------------------+
         |其他                                                 |
         |                                                     |
{比较if_none_match与ETag的值}                                   |
         V                                                     |
         |    其他                                             |
        <?>----------------------------------------------------+
         |
         |1.不含有if_none_match头部
         |or
         |2.ETag值与if_none_match值相同
         V
    {返回304}Not Modified
  1. nginx对客户端请求的缓存处理流程 哪些请求不使用缓存
Syntax:  proxy_cache_bypass string ...;
Default: --
Context: http,server,location
功能:参数为真时,不使用缓存内容

对哪个method方法使用缓存:

Syntax:  proxy_cache_methods GET | HEAD | POST ...;
Default: proxy_cache_methods GET HEAD;
Context: http,server,location

变更HEAD方法

Syntax:  proxy_cache_convert_head on | off;
Default: proxy_cache_convert_head on;
Context: http,server,location
on: 表示默认把HEAD方法转换为GET方法

表示缓存是否命中的变量upstream_cache_status

  • MISS: 未命中缓存
  • HIT: 命中缓存
  • EXPIRED: 缓存已经过期
  • STALE: 命中了陈旧的缓存
  • UPDATING: 内容陈旧,但正在更新
  • REVALIDATED: Nginx验证了陈旧的内容依然有效
  • BYPASS: 响应是从原始服务器获得的
  1. nginx接收上游响应的缓存处理流程 不缓存哪些响应
Syntax:  proxy_no_cache string ...;
Default: --
Context: http,server,location
功能:参数为真时,响应不存入缓存

缓存什么样的响应

Syntax:  proxy_cache_valid [code...] time;
Default: --
Context: http,server,location

* 对不同的响应码缓存不等的时长
    * 例如:proxy_cache_valid 404 5m;
* 只标识时间
    * 仅对以下响应码缓存
        * 200
        * 301
        * 302
  1. nginx处理请求和响应的流程:
   接收到客户端发来的请求                                           接收上游响应
       V                                                                V
{是否开启proxy_cache指令?}                                      {是否匹配proxy_no_cache指令?}
       V                                                                V
      <?>----N----------------------------------------+                <?>----Y--------------------------------+
      Y|                                              |                N|                                      |
       v                                              |                 v                                      |
{方法是否匹配proxy_cache_methods指令?}                 |        {方法是否匹配proxy_cache_valid指令?}              |
       V                                              |                 V                                      |
      <?>----N----------------------------------------+                <?>----N--------------------------------+
      Y|                                              |                Y|                                      |
       v                                              |                 v                                      |
{根据proxy_cache_convert_head转换GET方法}              |        {判断响应码是否为200或者206?}                     |
       V                                              |                 v                                      |
{根据proxy_key生成关键字并执行md5}                      |         +----N-<?>                                     |
       V                                              |         |       |Y                                     |
{检查proxy_cache_bypass是否指明不使用缓存?}             |         |       v                                      |
       V                                              |         | {更新cache中的etag和last_modified}            |
      <?>----Y----------------------------------------+         |       v                                      |
      N|                                              |         +>{处理缓存相关的响应头部,      }               |
       v                                              |           {并检查是否有响应头部不使用缓存 }               |
{在共享内存中查询缓存是否存在}                           |                |                                      |
       v                                              |                <?>-----Y-------------------------------|
      <?>----N-------------{共享内存中分配节点}         |               N|                                      |
      Y|                            V                 |         {读取、转发上游响应}<------+                    |
{更新LRU链表及节点计数}              <?>---成功---------+                 |                 |                    |
       V                            |失败             |                 v      未读取完响应|                    |
{是否错误类响应且过期}                V                 |                <?>---------->-----+                    |
       |                 {淘汰已过期的缓存,再次分配}----+                 |                                      |
       V                                              |                 |已读取全部响应                         |
      <?>------Y--------------------------------------+                 v                                      |
      N|                                              |         {将临时文件改名移至缓存目录}                     |
       v                                              |                 v                                      |
{文件存在且使用次数超出proxy_cache_min_uses}            |         {更新共享内存状态}          {不更新缓存,转发上游响应}
       V                                              |
      <?>------N--------------------------------------+
      Y|                                              |
       v                                              |
{根据proxy_cache_background_update生成子请求}          |
       V                                              V
{向下游发送缓存中的响应}                  {向上游发送请求}

2.3 如何判断缓存是否过期

          Client1                     Nginx Cache                         Upstream Server
            |                            |                                      |
The document|--------------------------->|缓存是空的,请求发给上游                |
is requested|GET /doc HTTP/1.1           |------------------------------------->|
            |                            |                                      |
            |<---------------------------|<-------------------------------------|
            |HTTP/1.1 200 OK             |  |HTTP/1.1 200 OK                    |
            |Cache-Control: max-age=100  |  |Cache-Control: max-age=100         |
            |Age: 0                      |  |                                   |
            |                            |  |>[ ]{/doc, max-age=100, age=0}     |

          Client2                        |                                      |
            |                       10 seconds later                            |
            |                            |                                      |
The document|--------------------------->|----+                                 |
is requested|                            |    V    缓存是新鲜的,直接返回         |
            |                            |   [ ]{/doc, max-age=100, age=10}     |
            |<---------------------------|-<--+                                 |
            |HTTP/1.1 200 OK             |                                      |
            |Cache-Control: max-age=100  |                                      |
            |Age: 10                     |                                      |

          Client3                        |                                      |
            |                     100 seconds later                             |
            |                            |                                      |
The docuemnt|--------------------------->|----+          缓存过期了              |
is requested|                            |   {/doc, max-age=100, age=110}       |
            |                            |   [ ]------------------------------->|
            |                            |    |  GET /doc HTTP/1.1              |
            |                            |    |  If-None-Match:...              |
            |                            |    |                                 |
            |                            |    |<--------------------------------|
            |<---------------------------|----+        HTTP/1.1 304 Not Modified|
            | HTTP/1.1 200 OK            |                                      |
            | Cache-Control: max-age=100 |                                      |
            | Age: 0                     |                                      |

* Upstream server会返回Cache-Control响应头部,但是不会返回Age响应头部
* Nginx Cache既会返回Cache-Control响应头部,还会返回Age响应头部
* 无论是浏览器还是nginx代理服务器,都只有在本地缓存过期之后,才会向[代理服务器/上游服务器]发起请求

response_is_fresh = (freshness_lifetime > current_age)
* freshness_lifetime: 按优先级,取以下响应头部的值
     * s-maxage > max-age > Expires > 预估过期时间
       s-maxage的优先级是最高的,它是用于共享缓存的(shared)
        * 例如:
           * Cache-Control: s-maxage = 3600
           * Cache-Control: max-age = 86400
           * Expires: Fri, 03 May 2019 03:15:20 GMT
               Expires = HTTP-date, 指明缓存的绝对过期时间

     什么是预估过期时间:
     * RFC7234推荐:` (DownloadTime-LastModified)*10% `
       DownloadTime就是浏览器获取到这个响应的时间(客户端的系统时钟)
       LastModified就是服务器端显示的它的资源的上次修改时间(服务器端的系统时钟)

* current_age = corrected_initial_age(修正后的initial_age) + resident_time(停留时间);
     # response_time和request_time都是客户端的时间,
     # response_time是接收到响应的时间,request_time是发出请求的时间
     * corrected_initial_age = max(corrected_age_value, apparent_age);
        * 一般取corrected_age_value,而不是apparent_age
        * corrected_age_value = age_value + response_delay;
            * age_value是缓存在代理服务器呆的时间,会存放在Age响应头部中
              # Age表示从源服务器发出响应(Date响应头的值),到使用缓存的响应被nginx发出时经过的秒数。
              # 多级代理服务器中,Age的值是如何变更的:
                          客户端
                           |   ^
                           |   | Date: date3
                           |   | Age: 初始Age+now3-response_time2
                           |   |      =now2-date1-request_time2+response_time2+now3-response_time2
                           |   |      =now2-data1-request_time2+now3
                           V   |      =now3-data1-request_time2+now2
                        代理服务器2----->storage  初始Age: 
              request_time2|   ^response_time2    now-date1+response_time2-request_time2+now2-response_time2
                           |   |                  =now-date1-request_time2+now2
                           |   |                  =now2-date1-request_time2+now
                           |   |                  ≈now2-date1-request_time2+response_time2
                           |   |
                           |   |     Date: date2
                           |   |     Age: 初始Age+now-response_time1
                           |   |          =response_time1-date1+now-response_time1
                           v   |          =now-date1
                        代理服务器1------->storage  初始Age: response_time1-date1
                           |   ^response_time1
                           |   |
                           |   |
                           V   |Date: date1
                          源服务器
            * response_delay = response_time - request_time;
        * apparent_age(明显的age) = max(0, response_time - date_value);
            # date_value是服务器端的系统时间,会存放在Date响应头部中
            * 用于处理客户端系统时间远大于服务器系统时间这样的特殊场景
            * 用于处理和源服务器交互时的场景,这个时候是没有Age响应头部的
     * resident_time = now - response_time;
* Cache-Control = 1*cache-directive
    * cache-directive = token [ "=" ( delta-seconds(红色)/quoted-string(蓝色) ) ]
        * token:
             * 请求中的头部: 
                   max-age(红色)、max-stale(红色)、init-fresh(红色)、
                   no-cache、no-store、no-transform、only-if-cached
             * 响应中的头部:
                   max-age(红色)、s-maxage(红色)、
                   must-revalidate、proxy-revalidate、
                   no-cache(蓝色)、no-store、no-transform、public、
                   private(蓝色)
             # 红色意味着使用delta-seconds
             # 黑色表示单单只需要token
             # 蓝色意味着使用quoted-string或者单单只需要token
        * delta-seconds = 1*DIGIT
    * 各token的含义(请求头中)
        * max-age(红色): 告诉服务器,客户端不会接受Age超出max-age秒的缓存
        * max-stale(红色): 告诉服务器,即使缓存不再新鲜,但陈旧秒数没有超出max-stale时,
              客户端仍打算使用。若max-stale后没有值,则表示无论过期多久客户端都可使用。
        * min-fresh(红色): 告诉服务器,Age至少经过min-fresh秒后缓存才可使用
        * no-cache: 告诉服务器,不能直接使用已有缓存作为响应返回,除非带着缓存条件到
              上游服务器端得到304验证返回码才可使用现有缓存
        * no-store: 告诉各代理服务器不要对该请求的响应缓存(实际有不少不遵守该约定的代理服务器)
        * no-transform: 告诉代理服务器不要修改消息包体的内容
        * only-if-cached: 告诉服务器仅能返回缓存的响应,否则若没有缓存则返回504错误码

    * 各token的含义(响应头中)
        * max-age(红色): 告诉客户端缓存Age超出max-age秒后则缓存过期
        * s-maxage(红色): 与max-age相似,但仅针对共享缓存,且优先级高于max-age和Expires
        * no-cache(蓝色): 告诉客户端不能直接使用缓存的响应,
              使用前必须在源服务器验证得到304返回码。
              如果no-cache后指定头部,
              则若客户端的后续请求及响应中不含有这些头则可直接使用缓存。
        * private(蓝色):表示该响应不能被代理服务器作为共享缓存使用。
              若private后指定头部,则在告诉代理服务器不能缓存指定的头部,但可缓存其他部分。
        * no-store: 告诉所有下游节点不能对响应进行缓存
        * no-transform: 告诉代理服务器不能修改包体的内容。
        * public:表示无论私有缓存或者共享缓存,皆可将该响应缓存
        * must-revalidate: 告诉客户端一旦缓存过期,必须向服务器验证后才可使用
        * proxy-revalidate: 与must-revalidate类似,但它仅对代理服务器的共享缓存有效

nginx的expire指令:用于处理Expire头部

Syntax:  expires time;
         expires epoch | max | off;
Default: expires off;
Context: http,server,location,if in location

* max:
   * Expires: Thu,31 Dec 2037 23:55:55 GMT
   * Cache-Control: max-age=315360000
* off: 不添加或者修改Expires和Cache-Control字段
* epoch:
   * Expires: Thu,01 Jan 1970 00:00:01 GMT
   * Cache-Control: no-cache
* time: 设定具体时间,可以携带单位
   * 一天内的具体时刻可以加@,比如下午六点半:@18h30m
      * 把time赋给Expires,自动计算Cache-Control
      * 如果当前时间超过当天的time,则把第二天的time时刻赋给Expires
   * 正数
      * 把time赋给Cache-Control,计算出Expires=now+time
   * 负数
      * 相当于Cache-Control: no-cache,然后计算出Expires=now+time

2.4 与缓存的载体相关的指令

缓存的实现:结合平衡二叉查找树和链表 其中,平衡二叉查找树用于存放key(并含有一个指向具体body的指针),链表用于存放实际缓存的body(并使用链表实现LRU算法)

  1. proxy_cache指令
Syntax:  proxy_cache zone | off
Default: proxy_cache off
Context: http,server,location
值设置为:proxy_cache_path指令的keys_zone设置的name的值
  1. proxy_cache_path指令
Syntax:  proxy_cache_path path [levels=levels] [use_temp_path=off|on]
         keys_zone=name:size [inactive=time] [max_size=size] [manager_files=number]
         [manager_sleep=time] [manager_threshold=time] [loader_files=number]
         [loader_sleep=time] [loader_threshold=time] [purger=on|off] [purger_files=number]
         [purger_sleep=time] [purger_threshold=time];
Default: --
Context: http

* path
      * 定义缓存文件存放位置
* levels
      * 定义缓存路径的目录层级,最多3级,每层目录长度为1或者2字节
* use_temp_path
      如果nginx所在机器上有多个文件系统,如果proxy_temp_path和proxy_cache_path
      不在同一磁盘上,会有两个操作,先删除再创建。
      * on使用proxy_temp_path定义的临时目录
      * off直接使用path路径存放临时文件
* keys_zone
      * name是共享内存名字,由proxy_cache指令使用
      * size是共享内存大小,1MB大约可以存放8000个key
* inactive
      * 在inactive时间内没有被访问的缓存,会被淘汰掉
      * 默认10分钟
* max_size
      * 设置最大的缓存文件大小,超出后由cache manager进程按LRU链表淘汰
* manager_files
      * cache manager进程在1次淘汰过程中,淘汰的最大文件数
      * 默认100
* manager_sleep
      * 执行一次淘汰循环后cache manager进程的休眠时间
      * 默认200毫秒
* manager_threshold
      * 执行一次淘汰循环的最大耗时
      * 默认50毫秒
* loader_files
      * cache loader进程载入磁盘中缓存文件至共享内存,每批最多处理的文件数
      * 默认100
* loader_sleep
      * 执行一次缓存文件至共享内存后,进程休眠的时间
      * 载入默认200毫秒
* loader_threshold
      * 每次载入缓存文件至共享内存的最大耗时
      * 默认50毫秒
  1. 缓存的关键字
Syntax:   proxy_cache_key string;
Default:  proxy_cache_key $scheme$proxy_host$request_uri;
Context:  http,server,location

本文发表于 0001-01-01,最后修改于 0001-01-01。

本站永久域名「 jiavvc.top 」,也可搜索「 后浪笔记一零二四 」找到我。


上一篇 « 下一篇 »

赞赏支持

请我吃鸡腿 =^_^=

i ysf

云闪付

i wechat

微信

推荐阅读

Big Image