缓存
帖子数 4
关注者 28

守望 发表了文章

Nginx下关于缓存控制字段cache-control的配置说明 - 运维小结

HTTP协议的Cache -Control指定请求和响应遵循的缓存机制。在请求消息或响应消息中设置 Cache-Control并不会影响另一个消息处理过程中的缓存处理过程。 请求时的缓存指令包括: no-cache、no-store、max-age、 max-stale、min-fresh、only-if-cached等。 响应消息中的指令包括: public、private、no-cache、no- store、no-transform、must-revalidate、proxy-revalidate、max-age。下面做一详细总结, 方便在以后的运维工作中理解和运用.一.   浏览器中关于Cache的3属性: 1. Cache-Control: 设置相对过期时间, max-age指明以秒为单位的缓存时间. 若对静态资源只缓存一次, 可以设置max-age的值为315360000000 (一万年). 比如对于提交的订单,为了防止浏览器回退重新提交,可以使用Cache-Control之no-store绝对禁止缓存,即便浏览器回退依然请求的是服务器,进而判断订单的状态给出相应的提示信息!Http协议的cache-control的常见取值及其组合释义: no-cache: 数据内容不能被缓存, 每次请求都重新访问服务器, 若有max-age, 则缓存期间不访问服务器. no-store: 不仅不能缓存, 连暂存也不可以(即: 临时文件夹中不能暂存该资源). private(默认): 只能在浏览器中缓存, 只有在第一次请求的时候才访问服务器, 若有max-age, 则缓存期间不访问服务器. public: 可以被任何缓存区缓存, 如: 浏览器、服务器、代理服务器等. max-age: 相对过期时间, 即以秒为单位的缓存时间. no-cache, private: 打开新窗口时候重新访问服务器, 若设置max-age, 则缓存期间不访问服务器. -  private, 正数的max-age: 后退时候不会访问服务器. -  no-cache, 正数的max-age: 后退时会访问服务器.2. Expires: 设置以分钟为单位的绝对过期时间, 优先级比Cache-Control低, 同时设置Expires和Cache-Control则后者生效. 也就是说要注意一点:  Cache-Control的优先级高于Expiresexpires起到控制页面缓存的作用,合理配置expires可以减少很多服务器的请求, expires的配置可以在http段中或者server段中或者location段中.  比如控制图片等过期时间为30天, 可以配置如下:1234location ~ \.(gif|jpg|jpeg|png|bmp|ico)$ {root /var/www/img/;expires 30d;}再比如:1234location ~ \.(wma|wmv|asf|mp3|mmf|zip|rar|swf|flv)$ {root /var/www/upload/;expires max;}3. Last-Modified: 该资源的最后修改时间, 在浏览器下一次请求资源时, 浏览器将先发送一个请求到服务器上, 并附上If-Unmodified-Since头来说明浏览器所缓存资源的最后修改时间, 如果服务器发现没有修改, 则直接返回304(Not Modified)回应信息给浏览器(内容很少), 如果服务器对比时间发现修改了, 则照常返回所请求的资源. 需要注意: 1) Last-Modified属性通常和Expires或Cache-Control属性配合使用, 因为即使浏览器设置缓存, 当用户点击”刷新”按钮时, 浏览器会忽略缓存继续向服务器发送请求, 这时Last-Modified将能够很好的减小回应开销.2) ETag将返回给浏览器一个资源ID, 如果有了新版本则正常发送并附上新ID, 否则返回304, 但是在服务器集群情况下, 每个服务器将返回不同的ID, 因此不建议使用ETag.以上描述的客户端浏览器缓存是指存储位置在客户端浏览器, 但是对客户端浏览器缓存的实际设置工作是在服务器上的资源中完成的. 虽然上面介绍了有关于客户端浏览器缓存的属性, 但是实际上对这些属性的设置工作都需要在服务器的资源中做设置. 通常有两种操作手段对浏览器缓存进行设置, 一个是通过页面指令声明来设置, 另外一个是通过编程方式来设置.下面是相关页面设置Cache-Control头信息的几个简单配置: 例一:123if ($request_uri ~* "^/$|^/search/.+/|^/company/.+/") {add_header    Cache-Control  max-age=3600;}个人理解的max-age意思是:客户端本地的缓存,在配置的生存时间内的,客户端可以直接使用,超出生存时间的,到服务器上取新数据。当然这些还要看客户端浏览器的设置。例二:123location ~ .*\.(css|js|swf|php|htm|html )$ {add_header Cache-Control no-store;}例三:123location ~ .*\.(js|css)$ {expires 10d;}例四: 将html结尾的请求加上no-cache12345678location / {access_log /data/nginx/log/xxx.log api;root /home/www/html;if ($request_filename ~ .*\.(htm|html)$){add_header Cache-Control no-cache;}}二.   http Headers模块 (设置HTTP报文的头标) Nginx的ngx_http_headers_module模块可以对Cache-Control头相关的东西进行配置, 比如:12345expires     24h;expires     0;expires     -1;expires     epoch;add_header  Cache-Control  private;指令 add_header add_header expires expires增加头标 语法: add_header name value 默认值: none 作用域: http, server, location 当HTTP应答状态码为 200、204、301、302 或 304 的时候,增加指定的HTTP头标。其中头标的值可以使用变量。expires 语法: expires [time|epoch|max|off 默认值: expires off 作用域: http, server, location 使用本指令可以控制HTTP应答中的“Expires”和“Cache-Control”的头标,(起到控制页面缓存的作用)。可以在time值中使用正数或负数。“Expires”头标的值将通过当前系统时间加上您设定的 time 值来获得。1epoch指定“Expires”的值为 1 January, 1970, 00:00:01 GMT。1max指定“Expires”的值为 31 December 2037 23:59:59 GMT,“Cache-Control”的值为10年。1指定“Expires”的值为 服务器当前时间 -1s,即永远过期."Cache-Control"头标的值由您指定的时间来决定:     - 负数1Cache-Control: no-cache   - 正数或零1Cache-Control: max-age = ## 为您指定时间的秒数。"off" 表示不修改“Expires”和“Cache-Control”的值;三.   Cache-Control Cache-Control 通用消息头字段被用于在http 请求和响应中通过指定指令来实现缓存机制。缓存指令是单向的, 这意味着在请求设置的指令,在响应中不一定包含相同的指令。响应头:Cache-Control:no-cache,强制每次请求直接发送给源服务器,而不经过本地缓存版本的校验。这对于需要确认认证应用很有用(可以和public结合使用),或者严格要求使用最新数据 的应用(不惜牺牲使用缓存的所有好处). 通俗解释:浏览器通知服务器,本地没有缓存数据.cache-control :        max-age>0时 直接从游览器缓存中提取;        max-age<=0 时向server发送http请求确认 ,该资源是否有修改, 有的话 返回200 , 无的话 返回304。通俗解释:响应头中的 Cache-Control:max-age=315360000 是通知浏览器: 315360000 秒之内不要烦我, 就自己从缓冲区中刷新。语法 指令不区分大小写,并且具有可选参数,可以用令牌或者带引号的字符串语法。多个指令以逗号分隔。指令 -   可缓存性 public      表明响应可以被任何对象(包括:发送请求的客户端,代理服务器,等等)缓存。表示相应会被缓存,并且在多用户间共享。默认是public。 private      表明响应只能被单个用户缓存,不能作为共享缓存(即代理服务器不能缓存它),可以缓存响应内容。响应只作为私有的缓存,不能在用户间共享。如果要求HTTP认证,响应会自动设置为private。 no-cache      在释放缓存副本之前,强制高速缓存将请求提交给原始服务器进行验证。指定不缓存响应,表明资源不进行缓存。但是设置了no-cache之后并不代表浏览器不缓存,而是在缓存前要向服务器确认资源是否被更改。因此有的时候只设置no-cache防止缓存还是不够保险,还可以加上private指令,将过期时间设为过去的时间。 only-if-cached      表明客户端只接受已缓存的响应,并且不要向原始服务器检查是否有更新的拷贝.-   到期 max-age=<seconds>      设置缓存存储的最大周期,超过这个时间缓存被认为过期(单位秒)。与Expires相反,时间是相对于请求的时间。max-age会覆盖掉Expires。 s-maxage=<seconds>      覆盖max-age 或者 Expires 头,但是仅适用于共享缓存(比如各个代理),并且私有缓存中它被忽略。也就是说s-maxage只用于共享缓存,比如CDN缓存(s -> share)。与max-age 的区别是:         max-age用于普通缓存,而s-maxage用于代理缓存。如果存在s-maxage,则会覆盖max-age 和 Expires. max-stale[=<seconds>]      表明客户端愿意接收一个已经过期的资源。 可选的设置一个时间(单位秒),表示响应不能超过的过时时间。 min-fresh=<seconds>      表示客户端希望在指定的时间内获取最新的响应。 stale-while-revalidate=<seconds>      表明客户端愿意接受陈旧的响应,同时在后台异步检查新的响应。秒值指示客户愿意接受陈旧响应的时间长度。 stale-if-error=<seconds>      表示如果新的检查失败,则客户愿意接受陈旧的响应。秒数值表示客户在初始到期后愿意接受陈旧响应的时间。-   重新验证和重新加载 must-revalidate      缓存必须在使用之前验证旧资源的状态,并且不可使用过期资源。表示如果页面过期,则去服务器进行获取。 proxy-revalidate      与must-revalidate作用相同,但它仅适用于共享缓存(例如代理),并被私有缓存忽略。 immutable      表示响应正文不会随时间而改变。资源(如果未过期)在服务器上不发生改变,因此客户端不应发送重新验证请求头(例如If-None-Match或If-Modified-Since)来检查更新,即使用户显式地刷新页面。在Firefox中,immutable只能被用在 https:// transactions.-   其他 no-store      缓存不应存储有关客户端请求或服务器响应的任何内容。表示绝对禁止缓存! no-transform      不得对资源进行转换或转变。Content-Encoding, Content-Range, Content-Type等HTTP头不能由代理修改。例如,非透明代理可以对图像格式进行转换,以便节省缓存空间或者减少缓慢链路上的流量。 no-transform指令不允许这样做。两个小示例 -  禁止缓存 发送如下指令可以关闭缓存。此外,可以参考Expires 和 Pragma 标题。1Cache-Control: no-cache, no-store, must-revalidate-  缓存静态资源节 对于应用程序中不会改变的文件,通常可以在发送响应头前添加积极缓存。这包括例如由应用程序提供的静态文件,例如图像,CSS文件和JavaScript文件。另请参阅Expires标题。1Cache-Control:public, max-age=31536000这里扩展一下: HTTP1.0 HTTP1.0中通过Pragma 控制页面缓存,通常设置的值为no- cache,不过这个值不这么保险,通常还加上Expires置为0来达到目的。但是如我们刻意需要浏览器或缓存服务器缓存住我们的页面这个值则要设置为 Pragma。HTTP1.1 HTTP1.1中启用Cache-Control 来控制页面的缓存与否,Cache-Control是http1.1 中的标准,可以看成是 expires 的补充, 使用的是相对时间的概念。注意几个常用的参数: no-cache:  浏览器和缓存服务器都不应该缓存页面信息; public:  浏览器和缓存服务器都可以缓存页面信息; no-store:  请求和响应的信息都不应该被存储在对方的磁盘系统中; must-revalidate:  对于客户机的每次请求,代理服务器必须想服务器验证缓存是否过时目前Cache-Control请求字段被各个浏览器支持的较好,其优先级也比较高,当和别的字段(如Expires)一起用时,会覆盖其他字段。四. nginx配置管理浏览器静态缓存策略 浏览器缓存: expires,  cache-control, last-modified,  etag.   先来看一张图:每个状态的详细说明如下: 1、Last-Modified 在浏览器第一次请求某一个URL时,服务器端的返回状态会是200,内容是你请求的资源,同时有一个Last-Modified的属性标记(HttpReponse Header)此文件在服务期端最后被修改的时间,格式类似这样:1Last-Modified:Tue, 24 Feb 2019 08:01:04 GMT客户端第二次请求此URL时,根据HTTP协议的规定,浏览器会向服务器传送If-Modified-Since报头(HttpRequest Header),询问该时间之后文件是否有被修改过:1If-Modified-Since:Tue, 24 Feb 2019 08:01:04 GMT如果服务器端的资源没有变化,则自动返回HTTP304(NotChanged.)状态码,内容为空,这样就节省了传输数据量。当服务器端代码发生改变或者重启服务器时,则重新发出资源,返回和第一次请求时类似。从而保证不向客户端重复发出资源,也保证当服务器有变化时,客户端能够得到最新的资源。注意: 如果If-Modified-Since的时间比服务器当前时间(当前的请求时间request_time)还晚,会认为是个非法请求2、Etag工作原理 HTTP协议规格说明定义ETag为“被请求变量的实体标记”(参见14.19)。简单点即服务器响应时给请求URL标记,并在HTTP响应头中将其传送到客户端,类似服务器端返回的格式:1Etag:“5d8c72a5edda8d6a:3239″客户端的查询更新格式是这样的:1If-None-Match:“5d8c72a5edda8d6a:3239″如果ETag没改变,则返回状态304。即: 在客户端发出请求后,HttpReponse Header中包含Etag:“5d8c72a5edda8d6a:3239″ 标识,等于告诉Client端,你拿到的这个的资源有表示ID:5d8c72a5edda8d6a:3239。当下次需要发Request索要同一个URI的时候,浏览器同时发出一个If-None-Match报头(Http RequestHeader)此时包头中信息包含上次访问得到的Etag:“5d8c72a5edda8d6a:3239″标识。1If-None-Match:“5d8c72a5edda8d6a:3239“这样,Client端等于Cache了两份,服务器端就会比对2者的etag。如果If-None-Match为False,不返回200,返回304(Not Modified) Response。3、Expires 给出的日期/时间后,被响应认为是过时。如 Expires:Thu, 02 Apr 2009 05:14:08 GMT需和Last-Modified结合使用。用于控制请求文件的有效时间,当请求数据在有效期内时客户端浏览器从缓存请求数据而不是服务器端.当缓存中数据失效或过期,才决定从服务器更新数据。4、Last-Modified和Expires Last-Modified标识能够节省一点带宽,但是还是逃不掉发一个HTTP请求出去,而且要和Expires一起用。而Expires标识却使得浏览器干脆连HTTP请求都不用发,比如当用户F5或者点击Refresh按钮的时候就算对于有Expires的URI,一样也会发一个HTTP请求出去,所以,Last-Modified还是要用的,而且要和Expires一起用。5、Etag和Expires 如果服务器端同时设置了Etag和Expires时,Etag原理同样,即与 Last-Modified/Etag 对应的 HttpRequestHeader:If-Modified-Since 和 If-None-Match。我们可以看到这两个Header的值和WebServer发出的Last-Modified,Etag值完全一样;在完全匹配If-Modified-Since和If-None-Match即检查完修改时间和Etag之后,服务器才能返回304.6、Last-Modified和Etag 分布式系统里多台机器间文件的last-modified必须保持一致,以免负载均衡到不同机器导致比对失败. 分布式系统尽量关闭掉Etag(每台机器生成的etag都会不一样)Last-Modified和ETags请求的http报头一起使用,服务器首先产生Last-Modified/Etag标记,服务器可在稍后使用它来判断页面是否已经被修改,来决定文件是否继续缓存过程如下: 1) 客户端请求一个页面(A)。 2) 服务器返回页面A,并在给A加上一个Last-Modified/ETag。 3) 客户端展现该页面,并将页面连同Last-Modified/ETag一起缓存。 4) 客户再次请求页面A,并将上次请求时服务器返回的Last-Modified/ETag一起传递给服务器。 5) 服务器检查该Last-Modified或ETag,并判断出该页面自上次客户端请求之后还未被修改,直接返回响应304和一个空的响应体。需要注意: 1) Last-Modified和Etag头都是由WebServer发出的HttpReponse Header,WebServer应该同时支持这两种头。 2) WebServer发送完Last-Modified/Etag头给客户端后,客户端会缓存这些头; 3) 客户端再次发起相同页面的请求时,将分别发送与Last-Modified/Etag对应的HttpRequestHeader:If-Modified-Since和If-None-Match。我们可以看到这两个Header的值和WebServer发出的Last-Modified,Etag值完全一样; 4) 通过上述值到服务器端检查,判断文件是否继续缓存;7、关于 Cache-Control: max-age=秒 和 Expires Expires = 时间,HTTP 1.0 版本,缓存的载止时间,允许客户端在这个时间之前不去检查(发请求) max-age = 秒,HTTP 1.1版本,资源在本地缓存多少秒。 如果max-age和Expires同时存在,则被Cache-Control的max-age覆盖。Expires 的一个缺点: 就是返回的到期时间是服务器端的时间,这样存在一个问题,如果客户端的时间与服务器的时间相差很大,那么误差就很大,所以在HTTP 1.1版开始,使用Cache-Control: max-age=秒替代。Expires =max-age + “每次下载时的当前的request时间”所以一旦重新下载的页面后,expires就重新计算一次,但last-modified不会变化.8、基于nginx配置使用总结 分布式系统(有ng-ha 和 应用的负载均衡),最好使用Last-Modified和Expires,把Etag关闭掉。 1) 关闭etag12http {etag off;关闭etag, 使用Last-Modified和Expires2) 配置last-modified(默认开启)和expires123456789location ~.*\.(gif|jpg|jpeg|png|bmp|swf)$        {                expires      30d;        }location ~.*\.(js|css)?$        {                expires      12h;        }对于配置了多个location(upstream)的,可以:12345678910111213141516location /filebase/ {            root /hskj/file/;            autoindex on;            if ($request_filename ~* ^.*?\.(txt|doc|pdf|rar|gz|zip|docx|exe|xlsx|ppt|pptx)$){                add_header Content-Disposition: ‘attachment;‘;            }if ($request_filename ~* ^.*?\.(gif|jpg|jpeg|png|bmp|swf)$){                expires      30d;            }            if ($request_filename ~* ^.*?\.(js|css)$){                expires      12h;            }}效果如下:这里顺便看一个配置实例: nginx设置不使用缓存 add_header Cache-Control no-cache                                                123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293server {listen       443;server_name  www.kevin.com;charset utf-8;ssl                  on;ssl_certificate      /daka/program/nginx/conf/server.cer;ssl_certificate_key  /daka/program/nginx/conf/server.key;ssl_session_timeout  5m;ssl_protocols  SSLv2 SSLv3 TLSv1;ssl_ciphers  HIGH:!aNULL:!MD5;ssl_prefer_server_ciphers   on;#设置浏览器缓存add_header Cache-Control no-cache;add_header Cache-Control private;  location /yp {proxy_redirect off;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_pass http://192.168.0.221:8082/yp/yp; if ($request_filename ~* .*.(html|htm)$){expires -1s;}if ($request_filename ~* .*.(gif|jpg|jpeg|png|bmp|swf)$){expires 30d;}if ($request_filename ~ .*.(js|css)$){expires 12h;}}location /static {proxy_redirect off;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_pass http://192.168.0.221:8082/static; if ($request_filename ~* .*.(html|htm)$){expires -1s;}if ($request_filename ~* .*.(gif|jpg|jpeg|png|bmp|swf)$){expires 30d;}if ($request_filename ~ .*.(js|css)$){expires 12h;}} location / {proxy_redirect off;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_pass http://192.168.0.221:8080/;#         if (-e $request_filename){#           rewrite ^/$ https://www.kevin.com:443/invest/index.jhtml permanent;#         }  if ($request_filename ~* .*.(html|htm)$){#         expires -1s;}if ($request_filename ~* .*.(gif|jpg|jpeg|png|bmp|swf)$){expires 30d;}if ($request_filename ~ .*.(js|css)$){expires 12h;} }}                                                               运维案例分享:Nginx增加缓存控制字段cache-control   开发发过来的需求: 1) 对于html文件,cache control设置为no-cache; 2) 对于js,图片,css,字体等,设置max-age=2592000. 也就是30天;注意点:    -  缓存控制字段cache-control的配置(add_header)要放在http, server, location区域, 或是放在location的if判断里, 例如"add_header Cache-Control no-cache;".    -  如果前面有LB负载代理层, 则缓存控制字段cache-control配置要放在后端的真实服务器nginx的location区域, 并且要指定root根路径, 否则访问会出现404 (即找不到访问路径);针对上面的案例需求, 操作记录如下: 1) 本案中在实际场景中, 有LB层. LB层的nginx配置不需要配置, 这里只是粘贴下负载配置:12345678910111213141516171819202122232425262728293031323334353637[root@fvtlb01 ~]# cat /data/nginx/conf/vhosts/fvtkevin-web.veredholdings-inc.com.confupstream fvtkevin-web-inc {ip_hash;server 172.16.50.73:80  max_fails=3 fail_timeout=15s;server 172.16.50.74:80  max_fails=3 fail_timeout=15s;}server {listen      80;server_name fvtkevin-web.veredholdings-inc.com; access_log  /data/nginx/logs/fvtkevin-web.veredholdings-inc.com-access.log main;error_log  /data/nginx/logs/fvtkevin-web.veredholdings-inc.com-error.log;location / {proxy_pass http://fvtkevin-web-inc;proxy_redirect off ;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header REMOTE-HOST $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_connect_timeout 300;proxy_send_timeout 300;proxy_read_timeout 600;proxy_buffer_size 256k;proxy_buffers 4 256k;proxy_busy_buffers_size 256k;proxy_temp_file_write_size 256k;proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504 http_404;proxy_max_temp_file_size 128m;#proxy_cache mycache;                               #proxy_cache_valid 200 302 1h;#proxy_cache_valid 301 1d;#proxy_cache_valid any 1m;}} 2) 缓存控制字段cache-control的配置要放在后端两台真实服务器172.16.50.73和172.16.50.74上. a) 172.16.60.73 (即fvtkevin-dmz01.veredholdings.cn)服务器上缓存控制字段cache-control的配置如下:123456789101112131415161718192021222324252627282930313233343536373839[root@fvtkevin-dmz01 ~]# cat /data/nginx/conf/vhosts/fvtkevin-web01.veredholdings.cn.confserver {listen      80;server_name fvtkevin-dmz01.veredholdings.cn; access_log  /data/nginx/logs/fvtkevin-dmz01.veredholdings.cn-access.log main;error_log  /data/nginx/logs/fvtkevin-dmz01.veredholdings.cn-error.log;location / {root /data/web/kevin;index index.php index.html index.htm;}location ~ \.(css|js|gif|jpg|jpeg|png|bmp|swf|ttf|woff|otf|ttc|pfa)$ {root /data/web/kevin;expires 30d;}location ~ \.(html|htm)$ {root /data/web/kevin;add_header Cache-Control no-cache;}location /document/ {alias /data/web/document/;}location ~ \.(css|js|gif|jpg|jpeg|png|bmp|swf|ttf|woff|otf|ttc|pfa)$ {root /data/web/document;expires 30d;}location ~ \.(html|htm)$ {root /data/web/document;add_header Cache-Control no-cache;}}b) 172.16.60.74 (即fvtkevin-dmz02.veredholdings.cn)服务器上缓存控制字段cache-control的配置如下:123456789101112131415161718192021222324252627282930313233343536373839[root@fvtkevin-dmz02 ~]# cat /data/nginx/conf/vhosts/fvtkevin-web02.veredholdings.cn.confserver {listen      80;server_name fvtkevin-web02.veredholdings.cn; access_log  /data/nginx/logs/fvtkevin-web02.veredholdings.cn-access.log main;error_log  /data/nginx/logs/fvtkevin-web02.veredholdings.cn-error.log; location / {root /data/web/kevin;index index.php index.html index.htm;}location ~ \.(css|js|gif|jpg|jpeg|png|bmp|swf|ttf|woff|otf|ttc|pfa)$ {root /data/web/kevin;expires 30d;}location ~ \.(html|htm)$ {root /data/web/kevin;add_header Cache-Control no-cache;}location /document/ {alias /data/web/document/;}location ~ \.(css|js|gif|jpg|jpeg|png|bmp|swf|ttf|woff|otf|ttc|pfa)$ {root /data/web/document;expires 30d;}location ~ \.(html|htm)$ {root /data/web/document;add_header Cache-Control no-cache;}}以上配置中, 关于缓存控制字段cache-control的配置主要有两个: 1) http://fvtkevin-web.veredholdings-inc.com/ 下:      对于html, html格式的文件,cache control设置为no-cache;      对于js,图片,css,字体等,设置max-age=2592000;      这是基于/data/web/kevin的root根目录下的 2) http://fvtkevin-web.veredholdings-inc.com/document 下:      对于html, html格式的文件,cache control设置为no-cache;      对于js,图片,css,字体等,设置max-age=2592000;      这是基于/data/web/document的root根目录下的以上配置后, 访问http://fvtkevin-web.veredholdings-inc.com/ 或者 http://fvtkevin-web.veredholdings-inc.com/document 进行验证. 这里验证下http://fvtkevin-web.veredholdings-inc.com/, 效果如下: 上面显示了http://fvtkevin-web.veredholdings-inc.com/ 首页(即index.html文件)访问头部信息里有"no-cache"信息! 上面显示了http://fvtkevin-web.veredholdings-inc.com/*.js文件访问的头部信息里的缓存时间设置!  

点赞 2
60 次浏览
0 条评论

守望 发表了文章

nginx缓存原理及配置

1.nginx缓存原理及配置nginx的http_proxy模块,可以 实现类似于 squid的 缓存功能。基于nginx静态缓存的解决方案:1.1 nginx cache的基本配置 proxy_cache_path /data/nginx/cache_item levels=1:1:2 keys_zone=cache_item:10m max_size=10g inactive=60m; server {           location ~ \.(gif|jpg|jpeg|png|bmp|ico)$ { proxy_set_header Host $host; proxy_set_header X-Forwarded-For $remote_addr; proxy_pass http://127.0.0.1:8080;                proxy_cache cache_item;                 proxy_cache_key $host$uri$is_args$args;#以全路径md5值做做为Key proxy_cache_valid 200 304 12h; #对不同的HTTP状态码设置不同的缓存时间 expires 7d; #总体缓存时间 } } 123456789101112上述参数说明如下:父元素名称描述httpproxy_cache_path指定缓存区的根路径levels缓存目录级最高三层,每层1-2个字符表示。如1:1:2三层。keys_zone缓存块名称及内存块大小。如cache_item:500m表示声明一个名为cache_item大小为500m。超出大小 后最早的数据将被清除。max_size缓存区硬盘的 最大值。超出闲置数据将被清除inactive最长闲置时间 如10d 如果一个 数据被闲置10天则将被清除locationproxy_cache指定缓存区,对应keys_zone中设定的值proxy_cache_key通过参数拼装参数key如:$host$uri$is_args$args则会以全部领md5值做为keyproxy_cache_valid对不同的状态码设置缓存有效期1.2 缓存的清除该功能 可以采用第三方模块ngx_cache_purge实现:为nginx添加ngx_cache_purge模块#下载ngx_cache_purge 模块包 ,这⾥nginx 版本为1.6.2 purge 对应2.0版wget http://labs.frickle.com/files/ngx_cache_purge-2.0.tar.gz#查看已安装模块./sbin/nginx -V#进⼊nginx安装包⽬录 重新安装 --add-module为模块解压的全路径./configure --prefix=/root/svr/nginx --with-http_stub_status_module --with-http_ssl_module --add-module=/root/svr/nginx/models/ngx_cache_purge-2.0#重新编译make#拷⻉ 安装⽬录/objs/nginx ⽂件⽤于替换原nginx ⽂件#检测查看安装是否成功nginx -t123456789101112131415161718192021清除配置:location ~ /purge(/.*) { #允许访问的IP allow 127.0.0.1; allow 192.168.0.193; #禁⽌访问的IP deny all; #配置清除指定缓存区和路径(与proxy_cache_key⼀⾄) proxy_cache_purge cache_item $host$1$is_args$args;}1234567891.3 测试1、首次访问:http://localhost8080/demo/images/logo.jpg2、缓存清理http://localhost8080/purge/demo/images/logo.jpg

点赞 2
84 次浏览
0 条评论

陶辉 发表了文章

巧用 NGINX 实现大规模分布式集群的高可用性

本文是我对2019年GOPS深圳站演讲的文字整理。这里我希望带给各位读者的是,如何站在整个互联网背景下系统化地理解Nginx,因为这样才能解决好大流量分布式网络所面临的高可用问题。标题里有“巧用”二字,何谓巧用?同一个问题会有很多种解决方案,但是,各自的约束性条件却大不相同。巧用就是找出最简单、最适合的方案,而做到这一点的前提就是必须系统化的理解Nginx!本文分四个部分讲清楚如何达到这一目的:首先要搞清楚我们面对的是什么问题。这里会谈下我对大规模分布式集群的理解;Nginx如何帮助集群实现可伸缩性;Nginx如何提高服务的性能;从Nginx的设计思路上学习如何用好它。1. 大规模分布式集群的特点互联网是一个巨大的分布式网络,它有以下特点:多样化的客户端。网络中现存各种不同厂商、不同版本的浏览器,甚至有些用户还在使用非常古老的浏览器,而我们没有办法强制用户升级;多层代理。我们不知道用户发来的请求是不是通过代理翻墙过来的;多级缓存。请求链路上有很多级缓存,浏览器、正反向代理、CDN等都有缓存,怎么控制多级缓存?RFC规范中有明确的定义,但是有些Server并不完全遵守;不可控的流量风暴。不知道用户来自于哪些地区,不知道他们会在哪个时间点集中访问,不知道什么事件会触发流量风暴;网络安全的高要求:信息安全问题要求通信数据必须加密;快速迭代的业务需求:BS架构使软件开发方式发生了巨大变化,我们可以通过快速迭代、发布来快速验证、试错。上图是典型的REST架构,图中包括客户端、正反向代理、源服务器,$符号代表缓存可以服务于上游,也可以服务于下游。通过IP地址标识主机,通过域名系统简化使用,URI则指向具体资源,每种资源有许多种表述,而服务器通过HTTP协议将表述转移至客户端上展示。这便是REST名为表述性状态转移的缘由,我在极客时间《Web协议详解与抓包实战》课程第7、8节课中对此有详细的介绍。设计架构时有许多关注点,与本文主题相关的有4个要点:可伸缩性。核心点在于如何有效的、动态的、灰度的均衡负载。可扩展性指功能组件的独立进化。可以理解为某个Nginx模块独立升级后,并不影响Nginx整体服务的属性。网络效率,也就是如何提升信息传输的效率。HTTP协议功能的全面支持。HTTP1的RFC规范非常多,毕竟它经历了20多年的变迁,而这20多年里互联网的巨大变化是HTTP1的设计者无法预料到的,这些规范也并不被所有Server、Client支持。 当然HTTP2和HTTP3相对情况会好很多。Nginx有优秀的可插拔模块化设计,它基于统一管道架构。其中有一类模块我称它为upstream负载均衡模块,官方Nginx便提供了最小连接、RoundRobin、基于变量控制的hash、一致性hash等负载均衡策略,而大量的第三方模块更提供了许多定制化的负载均衡算法。基于Lua语言的Openresty有自己的生态,这些Lua模块也提供了更灵活的实现方式。Nginx在性能优化上做得非常极致,大家知道最近F5收购了Nginx公司,为什么要收购?因为Nginx的性能可以与基于硬件的、价格昂贵的F5媲美!Nginx对HTTP协议的支持是比较全面的,当我们使用一些小众的替代解决方案时,一定要明确自己在HTTP协议有哪些独特需求。优秀的可配置性,在nginx.conf配置文件里我们可以使用脚本指令与变量实现复杂的功能。2. Nginx与scalability在讨论Nginx的负载均衡策略前,我们先来了解AKF扩展立方体,它能使我们对此建立整体思维。AKF扩展立方体有X、Y、Z轴,这三个轴意味着可以从3个角度实现可伸缩性:X轴指只需要增加应用进程,不用改代码就能水平的扩展。虽然最方便 ,但它解决不了数据不断增长的问题。Y轴按功能切分应用,它能解决数据增长的问题,但是,切分功能意味着重构代码,它引入了复杂性,成本很高。Z轴基于用户的属性扩展服务,运维Nginx时这招我们最常用,通常我们基于变量取到用户的IP地址、URL或者其他参数来执行负载均衡。当然,这三个轴可以任意组合以应对现实中的复杂问题。当然,要想解决可伸缩性问题,还必须在功能上支持足够多的协议。面向下游客户端主要是HTTP协议,当然Nginx也支持OSI传输层的UDP协议和TCP协议。受益于Nginx优秀的模块化设计,对上游服务器Nginx支持非常多的应用层协议,如grpc、uwsgi等。上图是Nginx执行反向代理的流程图,红色是负载均衡模块,任何一个独立的开发者都可以通过开发模块来添加新的LB策略。Nginx必须解决无状态HTTP协议带来的信息冗余及性能低下问题,而Cache缓存是最重要的解决手段,我们需要对Cache在反向代理流程中的作用有所了解。当下游是公网带宽并不稳定,且单用户信道较小时,通常Nginx应缓存请求body,延迟对上游应用服务建立连接的时间;反之,若上游服务的带宽不稳定,则应缓存响应body。理解nginx配置文件的3个关键点是:多级指令配置。通过大括号{},我们可以层层嵌套指令,借用父子关系来模块化的配置代码。变量,这是我们实现复杂功能,且不影响Nginx模块化设计的关键。变量是不同模块间低耦合交互的最有效方式!脚本引擎。脚本指令可以提供应用编程功能。很多人说Nginx的if指令是邪恶的,比如上图中的代码,其实我们只有理解if指令是如何影响父子嵌套关系后,才能正确的使用if。在《Nginx核心知识150讲》第141课我有详细介绍。Nginx官方迭代速度很快,在前两年差不多是两周一个版本,现在是一个月一个版本。频繁的更新解决了Bug也推出了新功能。但我们更新Nginx时却不能像更新其他服务一样,因为Nginx上任一时刻处理的TCP连接都太多了,如果升级Nginx时不能很好的应对就会出现大规模的用户体验问题。Nginx采用多进程结构来解决升级问题。它的master进程是管理进程,为所有worker进程保留住Syn半连接队列,所以升级Nginx时不会导致大规模三次握手失败。相反,单进程的HAProxy升级时就会出现连接建立失败问题。3. Nginx与集群performance缓存有两个实现维度:时间与空间。基于空间的缓存需要基于信息来预测,提前把用户可能请求的字节流准备好。而基于时间的缓存如上图所示,蓝色线条的请求触发了缓存(public share cache),这样红色线条的第二次请求可以直接命中缓存。浏览器中的是私有缓存,私有缓存只为一个用户服务。Nginx上实现了共享缓存,同时Nginx也可以控制浏览器中私有缓存的有效时间。RFC规范定义了许多缓存相关的头部,如果我们忽略了这些规则会很难理解Nginx如何基于下游的请求、上游的响应控制私有缓存及共享缓存,而且不了解这些规则其实不容易读懂nginx.conf中缓存相关指令的说明文档。在《Web协议详解与抓包实战》课程第29到32课我详细的介绍了缓存相关的规则。有些同学会问我,为什么部署Nginx之后没有看到上图中的Cache Loader和Cache Manger进程呢?因为我们没有启用Nginx的缓存。当然,即使我们开启缓存后,Cache Loader进程可能还是看不到的。为什么呢?因为Nginx为了高性能做了很多工作。当重启Nginx时,之前保存在磁盘上的缓存文件需要读入内存建立索引,但读文件的IO速度是很慢的,读缓存文件(文件很大很多)这一步骤可能耗时非常久,对服务器的负载很大,这会影响worker进程服务用户请求的能力。CL进程负责每次只读一小部分内容到共享内存中,这大大缓解了读IO慢的问题。CM进程负责淘汰过期缓存。当下游有一份过期资源时,它会来询问Nginx时:此资源还能用吗?能用的话,通过304告诉我,不要返回响应body(可能很大!)了。当Nginx缓存的资源可能过期时,它也可以问上游的web应用服务器:缓存还能用吗?能用的话通过304告诉我,我来更新缓存Age。RFC7033文档详细定义了这一过程,我在《Web协议详解与抓包实战》第28课有详细介绍。Nginx的not_modified过滤模块便负责执行这一功能。我在《Nginx核心知识150讲》课程第97、98课对此有详细介绍。如果我们突然发布了一个热点资源,许多用户请求瞬间抵达访问该资源,可是该资源可能是一个视频文件尺寸很大,Nginx上还没有建立起它的缓存,如果Nginx放任这些请求直达上游应用服务器(比如可能是Tomcat),非常可能直接把上游服务器打挂了。因为上游应用服务器为了便于功能的快速迭代开发,性能上是不能与Nginx相提并论的。这就需要合并回源请求。怎么合并回源请求呢?第一个请求过来了,放行!第二个请求也到了,但因为第1个请求还没有完成,所以上图中的请求2、4、5都不放行,直到第6步第1个请求的响应返回后,再把缓存的内容作为响应在第8、9、10中返回。这样就能缓解上游服务的压力。减少回源请求是一个解决方案,但如果Nginx上有过期的响应,能不能先将就着发给用户?当然,同时也会通过条件请求去上游应用那里获取最新的缓存。我们经常提到的互联网柔性、分级服务的原理与此是相同的。既然最新内容暂时由于带宽、性能等因素不能提供,不如先提供过期的内容,当然前提是不对业务产生严重影响。Nginx中的proxy_cache_use_stale指令允许使用stale过期缓存,上图中第1个请求放行了,第2、3请求使用旧缓存。从这里可以看出Nginx应对大流量有许多成熟的方案。我们在网页上会使用播放条拖动着看视频,这可以基于Http Range协议实现。但是,如果不启用Slice模块Nginx就会出现性能问题,比如现在浏览器要访问一个视频文件的第150-249字节,由于满足了缓存条件,Nginx试图先把文件拉取过来缓存,再返回响应。然而,Nginx会拉取完整的文件缓存!这是很慢的。怎么解决这个问题呢?使用Nginx的slice模块即可,如果配置100字节作为基础块大小,Nginx会基于100-199、200-299产生2个请求,这2个请求的应用返回并存入缓存后再构造出150-249字节的响应返回给用户。这样效率就高很多!通常,Nginx作为CDN使用时都会打开这一功能。互联网解决信息安全的方案是TLS/SSL协议,Nginx对其有很好的支持。比如,Nginx把下游公网发来的TLS流量卸载掉TLS层,再转发给上游;同时,它也可以把下游传输来的HTTP流量 ,根据配置的证书转换为HTTPS流量。在验证证书时,在nginx.conf中我们可以通过变量实现证书或者域名验证。虽然TLS工作在OSI模型的表示层,但Nginx作为四层负载均衡时仍然可以执行同样的增、删TLS层功能。Nginx的Stream模块也允许在nginx.conf中通过变量验证证书。Nginx处理TLS层性能非常好,这得益于2点:Nginx本身的代码很高效,这既因为它基于C语言,也由于它具备优秀的设计。减少TLS握手次数,包括:session缓存。减少TLS1.2握手中1次RTT的时间,当然它对集群的支持并不好,而且比较消耗内存。Ticket票据。Ticket票据可应用于集群,且并不占用内存。当然,减少TLS握手的这2个策略都面临着重放攻击的危险,更好的方式是升级到TLS1.3。我在《Web协议详解与抓包实战》第80课有详细介绍。4. 巧用NginxNginx模块众多,我个人把它分为四类,这四类模块各自有其不同的设计原则。请求处理模块。负责生成响应或者影响后续的处理模块,请求处理模块遵循请求阶段设计,在同阶段内按序处理。过滤模块。生成了HTTP响应后,此类模块可以对响应做再加工。仅影响变量的模块。这类模块为其他模块的指令赋能,它们提供新的变量或者修改已有的变量。负载均衡模块。它们提供选择上游服务器的负载均衡算法,并可以管理上游连接。请求处理模块、过滤模块、负载均衡模块均遵循unitform pipe and filter架构,每个模块以统一的接口处理输入,并以同样的接口产生输出,这些模块串联在一起提供复杂的功能。Nginx把请求处理流程分为11个阶段,所有请求处理模块必须隶属于某个阶段,或者同时在多个阶段中工作。每个处理阶段必须依次向后执行,不可跳跃阶段执行。同阶段内允许存在多个模块同时生效,这些模块串联在一起有序执行。当然,先执行的模块还有个特权,它可以决定忽略本阶段后续模块的执行,直接跳跃到下一个阶段中的第1个模块执行。每个阶段的功能单一,每个模块的功能也很简单,因此该设计扩展性很好。上图中的灰色模块Nginx框架中的请求处理模块。上图中右边是Openresty默认编译进Nginx的过滤模块,它们是按序执行的。图中用红色框出的是关键模块,它们是必须存在的,而且它们也将其他模块分为三组,开发第三方过滤模块时必须先决定自己应在哪一组,再决定自己应在组内的什么位置。Nginx中的变量分为:提供变量的模块和使用变量的模块。其含义我在《Nginx核心知识150讲》第72课有介绍,关于框架提供的变量在第73、74课中有介绍。无论我们使用了哪些模块,Nginx框架中的变量一定是默认提供的,它为我们提供了基础功能,理解好它们是我们使用好Nginx变量的关键。框架变量分为5类:HTTP 请求相关的变量TCP 连接相关的变量Nginx 处理请求过程中产生的变量发送 HTTP 响应时相关的变量Nginx 系统变量最后我们来谈谈Openresty,它其实是Nginx中的一系列模块构成的,但它由于集成了Lua引擎,又延伸出Lua模块并构成了新的生态。看看Openresty由哪些部分组成:Nginx,这里指的是Nginx的框架代码。Nginx官方模块,以及各类第三方(非Openresty系列)C模块。Openresty生态模块,它包括直接在Nginx中执行的C模块,例如上图中的绿色模块,也包括必须运行在ngx_http_lua_module模块之上的Lua语言模块。当然,Openresty也提供了一些方便使用的脚本工具。Openresty中的Lua代码并不用考虑异步,它是怎么在Nginx的异步C代码框架中执行的呢?我们知道,Nginx框架由事件驱动系统、HTTP框架和STREAM框架组成。而Openresty中的ngx_http_lua_module和ngx_stream_lua_module模块给Lua语言提供了编程接口,Lua语言通过它们编译为C代码在Nginx中执行。我们在nginx.conf文件中嵌入Lua代码,而Lua代码也可以调用上述两个模块提供的SDK调动Nginx的功能。Openresty的SDK功能强大,我个人把它分为以下8大类:Cosocket提供了类似协程的网络通讯功能,它的性能优化很到位,许多其他Lua模块都是基于它实现的。基于共享内存的字典,它支持多进程使用,所有worker进程间同步通常通过Shared.DICT。定时器。基于协程的并发编程。获取客户端请求与响应的信息修改客户端请求与响应,包括发送响应子请求,官方Nginx就提供了树状的子请求,用于实现复杂功能,Openresty用比C简单的多的Lua语言又实现了一遍。工具类,大致包含以下5类:正则表达式日志系统配置编解码时间类最后做个总结。在恰当的时间做恰当的事,听起来很美好,但需要我们有大局观。我们要清楚大规模分布式网络通常存在哪些问题,也要清楚分布式网络的常用解决方案,然后才能谈如何用Nginx解决上述问题。而用好Nginx,必须系统的掌握Nginx的架构与设计原理,理解模块化设计、阶段式设计,清楚Nginx的核心流程,这样我们才能恰到好处地用Nginx解决掉问题。

点赞 10
428 次浏览
3 条评论

陶辉 发表了文章

如何善用缓存提升系统的健壮性?(上)

分布式系统提升可用性时,最有效的方案就是在空间维度上,将资源复制一份作为缓存,并把缓存放在离用户更近的地方。这样,通过缩短用户的访问路径,不只可以降低请求的时延,多份资源还能提升系统的健壮性。比如WEB服务中的CDN就是这样一个缓存系统。 Nginx由于具有下面3个特性,因此是最合适的缓存系统:l  首先,高并发、低延迟赋予了Nginx优秀的性能;l  其次,多进程架构让Nginx具备了很高的稳定性;l  最后,模块化的开源生态,以及从开放中诞生的Openresty、Kong等其他体系,这都让Nginx的功能丰富而强大。 所以,Nginx往往部署在企业最核心的边缘位置,在最外层的Nginx上部署共享缓存,能够给服务带来更大的收益,更短的访问路径带来了更佳的用户体验。然而,当整个系统的可用性极度依赖Nginx的缓存功能时,我们必须仔细地配置Nginx,还得使用到缓存的许多进阶功能。 比如,在超大流量下如果热点资源的缓存失效,那么在巨大的流量穿透Nginx缓存后,非常有可能把脆弱的上游服务打挂。此时合并回源请求功能,就是你的最佳应对手段!再比如,在源服务器暂时不可用时,使用失效过期的缓存,为用户提供有限的服务,可以通过降级体验来提升系统的健壮性,这要比给用户返回“系统暂时不可用”要好得多。再比如,Nginx重启后,需要为磁盘上大量的缓存文件,在共享内存中建立起索引,这一过程可能很漫长,我们必须防止它对正常的服务产生过大的影响,降低用户体验。 如果你不清楚Nginx缓存的这些用法,就无法在超大流量场景中,用Nginx缓存保障服务的高可用性。当然,你可能觉得加机器扩容简单粗暴又有效,可是,一旦Nginx缓存正常工作后,这些新增的机器利用率又变得极低,这提升了公司的IT成本。因此,Nginx缓存的这些进阶用法,是你必须掌握的知识点! 这篇文章将分为上、下两个部分,先带着你回顾下Nginx缓存的基本用法,再来看如何使用缓存的进阶功能。 缓存的基本工作流程时间局部性原理,决定了缓存的有效性。比如,当客户端首次获取到/url1对应的资源时,它可以用key/value的形式,将请求与响应同时缓存到磁盘上,其中,key就是/url1,而value就是响应中的资源。只要服务器没有更改/url1对应的资源内容,那么在时间维度上,后续访问/url1的请求,就一直可以使用磁盘上的缓存代替网络访问来提升性能,如下图所示:通过缓存,请求的时延得到极大的下降,因为访问磁盘不过几十毫秒,但经过以秒级计量的网络再到达源服务器,并由源服务器再读取磁盘,速度就慢了很多,尤其在网络是不稳定、不可控的时候。 然而,服务器上的资源一旦发生了更新,客户端的缓存就失效了,使用这样的缓存有可能导致业务逻辑出错,怎么解决这一问题呢?通常,由于服务器最清楚资源的变更频率,这样,在服务器发送的响应中,设置一个预估缓存失效时间的HTTP头部,就可以解决这一问题。就像Cache-Control: max-age头部,或者Expires头部,等等。 客户端基于服务器告知的失效时间设立定时器,当定时器归零触发时,再将缓存设置为失效即可。注意,失效的缓存仍然有多方面的价值。 首先,客户端的失效定时器,毕竟来源于服务器的预估时间,时间过期后服务器上的资源也可能并未发生变化。这样,如果我们给每份资源生成唯一的ID(也就是缓存摘要,RFC规范用etag头部表示摘要),而客户端在判定缓存失效后,能够携带着摘要访问服务器,这就允许服务器通过304 Not Modified空包体回复客户端。如果资源是一个数百MB的视频文件,这一下就省去了数百MB字节的网络传输,如下图所示: Etag摘要究竟是怎样生成的呢?Nginx会将文件大小和最近修改时间,拼接为一个字符串作为文件摘要(详见《Nginx核心知识100讲》第97课),虽然区分度稍差,但优点是生成速度非常快。 其次,在某些场景下,给用户提供不那么及时的过期页面,要比返回500 服务器内部错误好得多。Nginx的proxy_cache_use_stale指令就可以完成这一功能,我们下一篇再细说。 缓存可以存放在任何位置!其中,仅存放于终端、只为一个用户服务的缓存,叫做私有缓存(Private Cache),而存放于服务器上,可以被多个用户共享使用的缓存,叫做共享缓存(Public Cache)。比如下图的REST架构中,浏览器User Agent中的$符号($表示现金cash,与cache发音很接近,故常用来表示缓存)表示私有缓存,而Proxy正向代理、Gateway反向代理中的$符号表示共享缓存: Nginx上的缓存就是共享缓存,接下来我们看看如何配置共享缓存。 如何开启基础版的共享缓存功能?接下来,我将按照缓存的工作流程,介绍配置基础共享缓存的Nginx指令。 首先,我们要确定每个资源对应的key关键字。这里的原则是,既要确保能够区分开不同的资源(正确性),又要尽量被更多的同类用户共享使用(高效率)。 比如,若多个域名下不同的资源,使用了相同的URL,那么你仅用URL作为key,一定会导致缓存被错误地共享使用。再比如,如果你把客户端的IP地址也写进了key中,那么不同的客户端就无法共享缓存,导致缓存的利用率大幅下降。再比如,若你的服务器同时提供HTTP和 HTTPS服务,那么你将$scheme写入key中,就会导致HTTP和HTTPS不能使用对方产生的缓存。 在Nginx的HTTP反向代理中,可以使用proxy_cache_key指令配置缓存的key关键字,其中值可以使用变量,比如:proxy_cache_key $proxy_host$request_uri;其他反向代理也有类似的配置(比如python语言的ngx_http_uwsgi_module反向代理模块中,使用相似的uwsgi_cache_key指令完成这一功能)。 其次,我们要决定缓存对应的文件放置在磁盘中的哪个位置,这可以通过proxy_cache_path指令完成,例如:proxy_cache_path /data/nginx/cache levels=2:2 keys_zone=one:10m;这行指令要求Nginx把缓存文件放在/data/nginx/cache目录下。注意,1个目录中不能存放太多的文件(会影响性能),所以levels选项要求Nginx使用2级缓存子目录,其中每级目录用文件的MD5摘要中2个字符命名。 虽然缓存内容是存放在磁盘中的,但为了加快访问速度,Nginx会在共享内存中为缓存建立红黑树索引,这可以为多个worker进程加快处理速度。keys_zone选项定义了共享内存的大小和名称,其中每1MB的共享内存,大约可以存放8千个key关键字。 定义好了缓存及索引的存放方式后,还需要定义究竟缓存哪些资源。比如,404错误码表示找不到资源,302错误码表示临时重定向,这样的响应是否需要缓存呢?这就是具体情况具体分析,如果访问频率非常高,而且这些错误一时无法恢复,那么缓存它们也是个不错的选择。 通过proxy_cache_valid指令可以设置缓存哪些响应码,并可以设置缓存的有效时间,如下所示:proxy_cache_valid 200 302 10m;你可能在想,刚刚才说过源服务器会定义缓存的过期时间,为何代理服务器Nginx要多此一举,重新定义过期时间呢?这是因为Nginx的这个过期时间,是用于兜底的,它的优先级很低,仅针对不含有这些过期时间头部的响应。当上游源服务器的响应中含有过期时间时,会优先使用响应中的时间。 而且,响应头部中同时出现多个过期时间,Nginx还定义了不同的优先级,比如:Nginx私有的X-Accel-Expires头部优先级最高,其次才是RFC规范中定义的Expires和Cache-Control头部。另外,对于含有Set-Cookie头部的响应,出于安全性考虑,Nginx默认是不会缓存的,包括Vary: *头部也是如此,如果你希望缓存它们,那么可以通过proxy_ignore_headers指令实现。 这里还要注意,proxy_cache_valid指令中的时间是必填项,当只填写了时间时,proxy_cache_valid默认会缓存以下3个错误码的响应:200、301、302。 在定义好了缓存的处理方式后,最后还得通过proxy_cache指令,告诉Nginx针对哪些域名(在server{ }内配置)、URL(在location{ }内配置)才缓存请求。proxy_cache指令非常简单,指定proxy_cache指令中keys_zone选项的共享内存名即可,比如:proxy_cache one;至此,我们就配置好了可以基本工作的Nginx缓存。虽然此时的缓存已经能够大幅度提升性能,但Nginx在缓存失效后,还无法在回源请求中携带etag缓存摘要,自然也无法享受到源服务器返回304空包体响应带来的好处了。你还需要通过proxy_cache_revalidate指令打开这一功能,如下所示:proxy_cache_revalidate on;小结本文介绍了Nginx缓存的工作流程,以及如何配置一个基础版的共享缓存。 首先,Nginx作为代理服务器,它提供的是效率更高的共享缓存。因此,我们要通过proxy_cache_key指令,通过Nginx变量选定合适的缓存关键字,既要保证在功能上有正确的区分度,也要在效率上可以为同类用户共享使用。 其次,缓存内容存放在磁盘文件中,而为了加快处理速度,Nginx还在共享内存中建立了红黑树索引。通过proxy_pass指令,可以设定磁盘文件的存储方式,以及共享内存的名称及大小。 最后,服务器会通过响应头部,预估缓存的过期时间。而在Nginx中,proxy_cache_valid既可以设置通用的、优先级较低的过期时间,也可以缓存非200错误码的响应。当然,缓存过期后,打开proxy_cache_revalidate功能还可以减少源服务器的数据传输量。 篇幅所限,我将在下一篇文章中,再来讨论Nginx缓存的进阶功能。比如,如何设置缓存的最大容量,如何在Nginx重启后控制cache loader进程对CPU资源的消耗,如何控制回源请求的数量,如何使用过期缓存提供降级服务等等。 最后,留给你一道思考题,你知道Nginx的性能非常高,那么它在实现层面是怎样淘汰过期缓存的吗?欢迎你在留言中与大家一起分享你的看法。 感谢阅读,如果你觉得这篇文章对你有一些启发,也欢迎你把它分享给你的朋友。 

点赞 9
310 次浏览
4 条评论