在说到缓存 varnish 前, 我们首先来了解下对于 web 服务缓存到底是什么? 它有哪些特点, 基础原理是什么?
http 是 Web 应用协议, 通常我们说的一次 http 事务, 不外乎就是客户端请求, 服务端响应, 通常我们是这样去理解 http 一次事务的过程; 其实对于 Web 服务器来说, 一个客户端访问服务端的某资源时, 往往客户端的请求没有到达真正提供 Web 服务的服务器上, 就被响应了, 这是为什么呢? 我们知道一个 Web 站点在提供对外访问的页面上在一定时间内都不会发生变化, 而对于这些不经常变化的资源, 访问又特别大的情况, 如果所有客户端的请求都到真正提供 Web 服务的服务器上请求资源, 可以想象, 对于提供 Web 服务的服务器所在网络是需要一个巨大带宽才能够足以支撑并发很多用户去访问; 所以我们的站点不应该也不能够让所有的客户端直接访问后端真正的 Web 服务器; 在现实生活中我们访问某度的网站, 感觉很快的样子, 其实这背后就是缓存的作用; 那什么是缓存呢? 所谓缓存, 对于 http 协议来说, 就是把那些经常被访问到的热区资源, 通过某种机制把它存放在离客户端最近的地方, 客户端访问该资源时, 直接从缓存中响应即可, 这样的机制就是缓存; 简单讲缓存就是把热区资源放到离客户端最近的地方, 方便客户端访问时, 直接从缓存的资源响应给客户端, 这样一来就解决了, 后端真正提供 Web 服务的服务器所在网络带宽的问题, 同时也加速了客户端的访问请求; 对于 http 协议来说, 程序的运行具有时间局部性和空间局部性特征, 所谓时间局部性特征就是一个数据被访问过之后, 可能很快会被第二次访问到; 空间局部性指的是一个数据被访问时, 其周边的数据也有可能别访问到; 如果我们把这些很受欢迎的资源 (可能被多次访问到的资源) 和其周围的一些资源存起来, 放到离客户端最近的地方, 这样一来, 大多数的客户端访问我们的站点就会被缓存响应, 能够真正到达后端真正提供 Web 服务的服务器上的客户端请求就大大减少; 这就是缓存的作用, 它一方面是减少后端服务器的压力, 同时加速了客户端的访问; 通常情况下缓存是有生命周期的, 什么意思呢? 就是说缓存不是说一直存放在哪里不变的, 它是有时效性的, 如果一个缓存项过期了, 那么该缓存就会被识别为失效的缓存项, 对于失效的缓存通常会有一个缓存管理机制处理该缓存项, 最常见的处理方式就是把失效的缓存项直接清理出去, 除此之外在 http1.1 协议中, 对于这种失效的缓存来讲, 通常客户端请求该资源时, 一看缓存时间过期了, 那么此时缓存服务器会向后端或上游服务器发送一个条件式请求问下真正的服务器, 说 "我这里的缓存失效了, 请问你那里的资源的时间戳改变了吗? 如果没有改变我就用我这里的缓存响应给客户端咯", 如果后端服务器用 304 响应缓存服务器, 就说明后端服务器上对于该资源在时间戳上没有发生改变, 缓存服务器收到 304 的响应后, 它会把自己的缓存生命周期往后延长一段时间, 同时把客户端的请求从该缓存中响应给客户端; 如果后端服务器响应的是 200 的状态码, 缓存服务器收到该响应, 会把后端服务器的响应资源缓存到自己本地, 然后在从缓存中响应客户端请求; 这就是所谓的条件是请求; 对于缓存来说, 缓存的命中至关重要, 所谓命中就表示客户端访问某资源, 能够从缓存中响应, 那么我们就说该次 http 事务被缓存命中; 一个缓存如果命中率极低, 那么我们就会认为该缓存缓存的意义不大, 所以一个缓存项缓存是否有意义, 关键还要看它的命中率如何; 命中率有两种, 第一种是基于页面数量来进行衡量的, 我们叫做页面命中率; 一个是基于页面的体积进行衡量的, 我们叫这种为字节命中率; 同时缓存又分私有缓存和公有缓存, 所谓私有缓存就表示某客户端的私有数据, 通常该缓存只能被某一客户端命中, 所以通常情况下, 这种私有缓存是在浏览器上存储; 而公有缓存是指没有私有属性的缓存, 对于所有客户端都可用, 我们这里通常说的缓存都是指这种公有缓存;
了解了缓存的大概介绍, 我们再来看看在 http 协议中缓存是如何被控制的; http1.0 中的缓存控制机制吧! 在 http1.0 协议中, 缓存控制机制是通过后端服务器的响应首部 expires 给定一个到期时间, 来明确告诉客户端该资源在给定的这个时间内都是有效的; 我们知道对于一个站点来说, 它可能是全球的, 这样一来如果通过绝对时间来控制缓存, 很明显, 如果客户端的时间和服务端的时间有误差, 很有可能存在一部分客户端访问某资源时, 得到服务端响应 expires 首部的时间在客户端就是失效的. 这样一来对于这部分客户端很明显缓存对于他们是无缘的; 所以在 http1.0 中这种绝对时间控制机制对于那种全球性的网站是不太适合的; 为了解决这样的困境, 在 http1.1 协议中引入了相对时间控制机制, 这种缓存控制机制是利用响应报文首部的 cache-control 特定值 maxage 或 s-maxage 的值控制的; 什么意思呢? 就是客户端请求某一资源时, 服务端会明确告诉客户端该资源可缓存, 缓存的时间是多长时间, 这样一来就不存在客户端系统时间和服务器时间不兼容或者有误差的情况导致缓存天前或延后失效的问题; maxage 指定的时间适用于那些公有缓存和私有缓存, 而 s-maxage 只适用于公有缓存; 在如今普片 http1.1 或 2.0 协议中为了兼容 http1.0, 通常这两种时间控制机制都是共同存在的; 我们可以随便打开一网站来看看这两个首部;
提示: 以上响应首部就是告诉浏览器下次请求该资源时, 如果在 expires 首部给定的时间之前访问该资源, 都会从缓存中直接响应; 如果我们客户端时间和服务端的时间有误差或不兼容, 那么在本次请求的后 7776000 秒之前, 如果该资源再次被访问都从缓存中直接响应; 也就说在本次访问到下次访问的时间如果小于 7776000 秒就直接从缓存中响应;
在 http1.1 协议中除了上面基于时间的缓存控制机制外, 还引入了条件式请求的机制来判断缓存有效方式, 在上面我们已经大概阐述过条件式请求的逻辑; 这里再大概的描述下, 所谓条件式请求就是客户端每访问一次服务端的资源, 都会去问真正的服务器该资源的时间戳改变了没有, 如果没有改变服务端就用 304 响应告诉客户端没有变, 从而客户端的请求就会被缓存直接响应; 如果服务端上的对应资源发生了变化, 那么服务端会以 200 的响应码告诉客户端资源的时间戳发生改变了, 同时把改变后的资源发送给客户端, 客户端收到服务端的响应后, 它会把新的资源缓存起来, 然后再用新的资源响应客户端; 其实对于这种基于时间戳的形式来判断资源是否改变, 其实是不是很精准的; 比如服务器上的某一资源在一秒钟变化了 8 次, 如果基于时间戳来判断资源是否改变(精确到秒), 很可能服务端会告诉客户端资源没变, 其实服务端上的资源改变了, 为了解决这种问题, 在 http1.1 还引进了扩展标记的控制机制, 这种控制机制和基于时间戳控制机制流程都是一样的, 只是在基于时间戳控制是以资源的时间戳来判断该资源是否发生改变, 而扩展标记控制机制是以资源的校验码来判别资源是否发生改变, 很明显后者对于资源是否发生改变的控制更加精准, 当同时对服务端的 CPU 消耗也就更多一点, 因为每一次响应都要把对应资源校验下, 拿到其校验码进行和客户端的校验码对比; 在 http1.1 中基于时间戳的条件是请求表现形式在客户端请求首部 if-modified-since, 表示告诉服务自从某某时间后服务端对应资源发生变化了吗, 响应报文中以 last-modified 首部来告诉客户端服务器上对应资源的最近修改时间戳, 如果响应报文中的时间戳比请求报文中的时间戳新, 那么客户端就会收到 200 的响应码, 如果响应报文中的时间戳小于或等于请求报文中的时间戳, 那么客户端就会收到 304 的响应码, 意思就是指资源没有发生变化, 该缓存可以继续使用; 通常客户端请求某资源服务端会用 last-modified 首部告诉客户端资源的时间戳, 以便判断资源是否发生改变; 基于扩展标记的方式在 http1.1 协议中的表现形式就是在请求报文首部以 if-none-match 来告诉服务端客户端请求资源的校验, 响应报文首部用 Etag 首部告诉客户端对应资源的校验码, 如果两者相等表示资源没有发生改变, 对应响应码就是 304, 如果两者不等, 表示资源发生了改变, 响应码就是 200; 扩展标记机制通过对比校验码的形式来判断资源是否发生改变;
提示: 从上面的响应报文和请求报文首部的值来对比, 很容易判断该资源没有发生改变; 通常情况下我们只是基于某一种机制来控制缓存就好, 如果你想精度高一点, 我们直接使用扩展标记机制即可, 如果精度没有那么高, 可以使用基于资源时间戳的机制来控制就好;
了解了 http 协议中的缓存控制机制, 我们这里在着重说一下 cache-control 在请求报文中的值, 和响应报文中的值代表意义(如何处理缓存内容)
http1.1 协议中请求首部 cache-control 的值如果是 "no-cache" 即表示告诉缓存服务器本次请求不要用缓存里的数据响应;
提示: 以上请求就表示告诉缓存服务器不要用缓存响应本次请求, 所以我们能看到本次请求的状态码是 200 而非 304; 通常我们使用浏览器对资源强刷, 请求报文中 cache-control 的值就是 "no-cache"
当然对于请求报文中 cache-control 的值还有多, 但是用的比较少, 这里就不过多强调其他值的意思, 我们着中来说说 http 协议 1.1 中, 响应首部 cache-control 的值; 在响应报文首部中 cache-control 的值主要是告诉缓存服务器或缓存系统怎么处理缓存数据; 比如在响应报文首部 cache-control 的值是 "public" 就表示该数据可以被公用缓存系统或服务器缓存, 对于私有缓存服务器或系统当然也是可以缓存该数据; 如果响应首部 cache-control 的值是 "private" 就表示告诉缓存系统该数据只能用于私有缓存系统中, 公有缓存系统不能缓存该数据; 如果是 "no-cache" 就表示告诉缓存系统该数据可缓存, 当在响应给客户端之前需要做条件式请求进行缓存有效性验证;"no-store" 表示告诉缓存系统该资源或数据不允许存储在缓存中;"must-revalidate" 这个意思同 "no-cache" 一样表示可缓存, 但响应必须做条件式请求验证缓存的有效性;"max-age" 表示告诉缓存系统该资源或数据的缓存时长是多少秒;"s-maxage" 表示告诉缓存系统该资源在公有缓存系统上的生命时长(该值表示指定公有缓存系统或者代理缓存服务器上的缓存时长);
在了解了 http1.1 协议中是如何控制缓存, 接下来我们聊一聊针对 Web 服务缓存应用的开源实现; 对于 Web 应用缓存系统开源实现有两款软件 squid 和 varnish, 这两款软件有点像 apache httpd 和 nginx 关系, 他们分别是不同年代的产品, 对于 varnish 是来讲, 可以说它是 squid 的替代品, 因为 varnish 相比之下要比 squid 性能更稳定, 响应速度更快, 支持的并发连接更多等等;
varnish 的官方站点是 http://www.varnish-cache.org/ , 它和其他主流开源产品类似有 community 和 enterprise 两个版本, 前者免费, 后者你懂的; varnish 它是一款高性能的 http 缓存服务器, 其程序架构如下
提示: varnish 程序架构如上图, 主要由四部分组成, 配置接口 VCL,manager 进程, cache 进程和共享内存日志; 其中 manager 进程主要是管控 cache 进程的, 配置文件的编译并加载, 而 cache 进程包含多种类型的线程, 每个线程处理一类事务, 如 command line 处理命令行命令的, log/stats 记录日志和统计数据, accept 用于接收请求, storage/hashing 用于管理存储和缓存中的键的, backend communication 用于与后端服务器进行通信用的, object expiry 用于管理缓存项和过期时间的; 对于 varnish 的日志, 为了更高效的提供服务, 它的日志是记录在共享内存的, 而共享内存的大小是固定的, 日志不会越记越大, 它采取的是轮转机制, 日志记录满了就覆盖最前边的日志, 是得日志最大只能占用给定大小的空间; 这也是 varnish 的日志管理的独特地方, 如果我们需要保存日志, 需要周期性的去共享内存中把日志读出来保存到文件中即可; 当然用于日志管理的工具有很多, varnish 提供了五种工具, 分别用于管理日志的, 其中 varnishlog 是把共享内存中的日志记录成 varnish 原始格式的日志, varnishncsa 是把共享内存中的日志记录成 http combined 格式的日志, varnishtop 就是用来对共享内存中的日志排序用到的工具, varnishhist 用于查看日志历史信息, varnishstat 用于统计数据的; varnish 的配置文件有两个, 一个是定义 varnish 程序自身的工作特性(/etc/varnish/varnish.params), 一个是定义缓存工作特性(/etc/varnish/default.vcl), 通常情况下配置个 child/cache 线程的缓存策略, 需要用专用的配置语言 vcl(varnish configuration lanuage), 通常这种配置文件在定义好后, 不能直接使用, 需要用 vcl 编译器将其编译成 C 代码, 然后通过 c 编译器编译编译成共享对象, 提供给子进程中的线程链接加载使用;
varnish 安装
[root@test_node1-centos7 ~]# yum install -y varnish
提示: 安装可以是编译也可以是 yum 安装, 如果是 yum 安装, 需要提前把 epel 仓库配好, varnish 包它来源 epel 仓库;
varnish 的程序环境说明
- [root@test_node1-centos7 ~]# rpm -ql varnish
- /etc/logrotate.d/varnish
- /etc/varnish
- /etc/varnish/default.vcl
- /etc/varnish/varnish.params
- /run/varnish.pid
- /usr/bin/varnishadm
- /usr/bin/varnishhist
- /usr/bin/varnishlog
- /usr/bin/varnishncsa
- /usr/bin/varnishstat
- /usr/bin/varnishtest
- /usr/bin/varnishtop
- /usr/lib/systemd/system/varnish.service
- /usr/lib/systemd/system/varnishlog.service
- /usr/lib/systemd/system/varnishncsa.service
- /usr/sbin/varnish_reload_vcl
- /usr/sbin/varnishd
...... 省略部分内容......
提示:/etc/varnish/varnish.params: 配置 varnish 服务进程的工作特性, 例如监听的地址和端口, 缓存机制;/etc/varnish/default.vcl: 配置各 Child/Cache 线程的缓存策略;/usr/sbin/varnishd 是主程序,/usr/bin/varnishadm 是命令行管理工具;/usr/bin/varnishhist,varnishlog,varnishncsa,varnishstat,varnishtop 都是共享内存日志交互工具;/usr/bin/varnishtest 是测试工具程序;/usr/sbin/varnish_reload_vcl 是 vcl 配置文件重载程序;/usr/lib/systemd/system/varnish.service 是 varnish 服务 unit 文件;/usr/lib/systemd/system/varnishlog.service 和 varnishncsa.service 是日志持久服务 unit 文件;
以上就是 http 协议中的缓存控制机制说明和 varnish 程序架构和环境的简单说明, 后续本人会持续更新 varnish 系列使用说明; 有兴趣的朋友可以关注, 评论, 共同探讨和学习;
来源: https://www.cnblogs.com/qiuhom-1874/p/12620538.html