# BasicTo Varnish

通过本篇文章,希望能够解惑如下问题

  • Varnish 是什么?
  • Varnish 解决了什么问题?
  • Varnish 特点?
  • Varnish 工作原理?
  • Varnish VCL
  • Varnish 如何安装?
  • Varnish 如何使用?
  • Varnish 和网站性能?
  • Varnish 学习资源?

# Varnish

Varnish Cache is a web application accelerator also known as a caching HTTP reverse proxy. Varnish Cache 是 ​​Web 应用程序加速器,也称为缓存 HTTP 反向代理。

通过在 Web 应用程序和服务器之间安装 varnish,可以提升 300-1000 倍的速度。 Varnish 的项目开始于 2005,Varnish Cache 1.0 在 2006 年 支持的平台:https://varnish-cache.org/docs/6.4/tutorial/introduction.html#supported-platforms

# Varnish 解决了什么问题

# Varnish 与其他方案的对比

varnish/varnish-001

# Varnish 特点

# 性能

Varnish 的性能非常好。当 Varnish 准备好缓存的响应时,通常在几微秒内就可以交付,比典型的后端服务器快两个数量级,因此,您需要确保 Varnish 直接从缓存中回答尽可能多的请求。它通常受网络速度的束缚,但是可以有效地将性能变的不再是问题。

# 配置灵活

VCL Varnish Configuration Language

the VCL is compiled to C code, and the C code is compiled to machine instructions, even very complex VCL programs execute in a few microseconds, without impacting performance at all

VMODS 扩展 Varnish 的 modules

# Varnish 优缺点

# 优点

  • 稳定性高,在完成相同负荷的工作时,Squid 服务器发生故障的几率要高于 Varnish,因为使用 Squid 要经常重启
  • Varnish 访问速度快,因为采用了"Visual Page Cache"技术,所有缓存数据都直接从内存读取
  • Varnish 可以通过管理端口,使用正则表达式批量的清除部分缓存

# 缺点

  • varnish 进程一旦 Hang、Crash 或者重启,缓存数据都会从内存中完全释放,此时所有请求都会发送到后端服务器,在高并发情况下,会给后端服务器造成很大压力
  • 在 varnish 使用中如果单个 url 的请求通过 HA/F5 等负载均衡,则每次请求落在不同的 varnish 服务器中,造成请求都会被穿透到后端;而且同样的请求在多台服务器上缓存,也会造成 varnish 的缓存的资源浪费,造成性能下降;

# Varnish 架构及文件缓存的工作流程

varnish/varnish-002 Varnish 分为 master 进程和 child 进程;

Master 进程读入存储配置文件,调用合适的存储类型,然后创建 / 读入相应大小的缓存文件,接着 master 初始化管理该存储空间的结构体,然后 fork 并监控 child 进程;

Child 进程在主线程的初始化的过程中,将前面打开的存储文件整个 mmap 到内存中,此时创建并初始化空闲结构体,挂到存储管理结构体,以待分配;

对外管理接口分为 3 种,分别是命令行接口、Telnet 接口和 Web 接口;

同时在运行过程中修改的配置,可以由 VCL 编译器编译成 C 语言,并组织成共享对象(Shared Object)交由 Child 进程加载使用;

Management 进程主要实现应用新的配置、编译 VCL、监控 varnish、初始化 varnish 以及提供一个命令行接口等。Management 进程会每隔几秒钟探测一下 Child 进程以判断其是否正常运行,如果在指定的时长内未得到 Child 进程的回应,Management 将会重启此 Child 进程

varnish/varnish-003

Child 进程分配若干线程进行工作,主要包括一些管理线程和很多 worker 线程,可分为:

Accept 线程:接受请求,将请求挂在 overflow 队列上;

Work 线程:有多个,负责从 overflow 队列上摘除请求,对请求进行处理,直到完成,然后处理下一个请求;

Epoll 线程:一个请求处理称为一个 session,在 session 周期内,处理完请求后,会交给 Epoll 处理,监听是否还有事件发生;

Expire 线程:对于缓存的 object,根据过期时间,组织成二叉堆,该线程周期检查该堆的根,处理过期的文件,对过期的数据进行删除或重取操作;

# Varnish 内置函数

vcl_recv:用于接收和处理请求;当请求到达并成功接收后被调用,通过判断请求的数据来决定如何处理请求;

vcl_pipe:此函数在进入 pipe 模式时被调用,用于将请求直接传递至后端主机,并将后端响应原样返回客户端;

vcl_pass:此函数在进入 pass 模式时被调用,用于将请求直接传递至后端主机,但后端主机的响应并不缓存直接返回客户端;

vcl_hit:在执行 lookup 指令后,在缓存中找到请求的内容后将自动调用该函数;

vcl_miss:在执行 lookup 指令后,在缓存中没有找到请求的内容时自动调用该方法,此函数可用于判断是否需要从后端服务器获取内容;

vcl_hash:在 vcl_recv 调用后为请求创建一个 hash 值时,调用此函数;此 hash 值将作为 varnish 中搜索缓存对象的 key;

vcl_purge:pruge 操作执行后调用此函数,可用于构建一个响应;

vcl_deliver:将在缓存中找到请求的内容发送给客户端前调用此方法;

vcl_backend_fetch:向后端主机发送请求前,调用此函数,可修改发往后端的请求;

vcl_backend_response:获得后端主机的响应后,可调用此函数;

vcl_backend_error:当从后端主机获取源文件失败时,调用此函数;

vcl_init:VCL 加载时调用此函数,经常用于初始化 varnish 模块(VMODs)

vcl_fini:当所有请求都离开当前 VCL,且当前 VCL 被弃用时,调用此函数,经常用于清理 varnish 模块;

# Varnish 内置变量

# req

# resp

# bereq

# beresp

# obj

# Varnish 工作原理

Varnish 是一个缓存的 HTTP 反向代理。它接收来自客户端的请求,并尝试从缓存中应答它们。如果 Varnish 无法从缓存中回答请求,它将把请求转发到后端,获取响应,将其存储在缓存中并将其交付给客户端

varnish/varnish-005

处理流程 (opens new window)

Varnish 处理 HTTP 请求的过程如下:

  • Receive 状态(vcl_recv):也就是请求处理的入口状态,根据 VCL 规则判断该请求应该 pass(vcl_pass)或是 pipe(vcl_pipe),还是进入 lookup(本地查询);

  • Lookup 状态:进入该状态后,会在 hash 表中查找数据,若找到,则进入 hit(vcl_hit)状态,否则进入 miss(vcl_miss)状态;

  • Pass(vcl_pass)状态:在此状态下,会直接进入后端请求,即进入 fetch(vcl_fetch)状态;

  • Fetch(vcl_fetch)状态:在 fetch 状态下,对请求进行后端获取,发送请求,获得数据,并根据设置进行本地存储;已独立为 vcl_backend_fetch 和 vcl_backend_response

  • Deliver(vcl_deliver)状态:将获取到的数据发给客户端,然后完成本次请求;

# VCL

Varnish Configuration Language (VCL)是 varnish 配置缓存策略的工具,它是一种基于“域”(domain specific)的简单编程语言,它支持有限的算术运算和逻辑运算操作、允许使用正则表达式进行字符串匹配、允许用户使用 set 自定义变量、支持 if 判断语句,也有内置的函数和变量等

VCL 的设计参考了 C 和 Perl 语言,因此,对有着 C 或 Perl 编程经验者来说,其非常易于理解。其基本语法说明如下:

(1)//、#或/_ comment _/用于注释

(2)sub $name 定义函数

(3)不支持循环,有内置变量

(4)使用终止语句,没有返回值

(5)域专用

(6)操作符:=(赋值)、==(等值比较)、~(模式匹配)、!(取反)、&&(逻辑与)、||(逻辑或)

VCL 的函数不接受参数并且没有返回值,因此,其并非真正意义上的函数,这也限定了 VCL 内部的数据传递只能隐藏在 HTTP 首部内部进行。VCL 的 return 语句用于将控制权从 VCL 状态引擎返回给 Varnish,而非默认函数,这就是为什么 VCL 只有终止语句而没有返回值的原因。同时,对于每个“域”来说,可以定义一个或多个终止语句,以告诉 Varnish 下一步采取何种操作,如查询缓存或不查询缓存等。

# Varnish 安装

版本 最新:6.4.0

官方安装指导 (opens new window)

Ubuntu: https://packagecloud.io/varnishcache/varnish64/install#manual-deb 手动安装 CentOS: https://packagecloud.io/varnishcache/ 通过 rpm 的方式安装 手动编译源码: https://varnish-cache.org/docs/6.4/installation/install_source.html#build-dependencies-on-debian-ubuntu

# Varnish 使用

# CLI

Varnish:

Debian, Ubuntu: /etc/default/varnish
Red Hat, Centos: /etc/sysconfig/varnish


DAEMON_OPTS="-a :6081 \
             -T localhost:6082 \
             -f /etc/varnish/default.vcl \
             -S /etc/varnish/secret \
             -s malloc,256m"
  • -a Varnish 监听的端口
  • -T Varnish 控制台端口
  • -f Varnish 配置文件
  • -s 分配的内存

默认的 vcl_fetch 放弃了缓存任何使用了 Set-Cookie 首部的响应

# 清除缓存

Varnish 为完成这类的任务提供了三种途径: HTTP 修剪(HTTP purging) 禁用某类缓存对象(banning) 强制缓存未命中(forced cache misses)。

# Varnish 和网站性能

# 提升命中率

使用 Varnishtop 查看哪些 url 命中了后端服务器,哪些没有命中。也可以查看请求最频繁的 url 是哪些。

# Varnish 命令

# varnishadm

控制台应用

# varnishlog

Varnish 不会将 log 记录到磁盘上。相反,它记录到一块内存中

# Varnish 日志

为了与系统的其它部分进行交互,Child 进程使用了可以通过文件系统接口进行访问的共享内存日志(shared memory log),因此,如果某线程需要记录信息,其仅需要持有一个锁,而后向共享内存中的某内存区域写入数据,再释放持有的锁即可。而为了减少竞争,每个 worker 线程都使用了日志数据缓存。

共享内存日志大小一般为 90M,其分为两部分,前一部分为计数器,后半部分为客户端请求的数据。varnish 提供了多个不同的工具如 varnishlog、varnishncsa 或 varnishstat 等来分析共享内存日志中的信息并能够以指定的方式进行显示。

# Varnish 配置

# Note

  • 默认情况下,Varnish 使用 100MB 大小的内存

# Varnish 学习资源

Tutorial: https://varnish-cache.org/docs/6.4/tutorial/index.html User Guide: https://varnish-cache.org/docs/6.4/users-guide/index.html#users-guide-index

https://yq.aliyun.com/articles/434366

陕ICP备20004732号-3