一、Varnish概述


  • 一款高性能、开源的HTTP反向代理服务器和缓存服务器,挪威最大的在线报纸 Verdens Gang 使用3台Varnish代替了原来的12台Squid,性能比以前更好。
  • Varnish使用内存做为缓存设备(纯内存缓存服务器方案),相对于Squid(采用硬盘缓存),拥有更快的缓存速度(varnish内存管理完全交给内核,但当缓存内容超过阈值时,内核会自动将一部分缓存存入swap中,让出内存)

1.Varnish进程

Varnish与一般服务器软件类似,分为master(management)进程和child(主要做cache的工作)

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

Child进程包含多种类型的线程,常见的如:

  • Acceptor线程:接收新的连接请求并响应
  • Worker线程:child进程会为每个会话启动一个worker线程,因此,在高并发的场景中可能会出现数百个worker线程甚至更多
  • Expiry 线程:从缓存中清理过期内容;Varnish依赖“工作区(workspace)”以降低线程在申请或修改内存时出现竞争的可能性 在varnish内部有多种不同的工作区,其中最关键的当属用于管理会话数据的session 工作区

2.Varnish日志

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

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

3.Varnish中的VCL

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

  • 使用VCL编写的缓存策略通常保存至.vcl文件中,其需要编译成二进制的格式后才能由 varnish调用。事实上,整个缓存策略就是由几个特定的子例程如vcl_recv、vcl_fetch等组成,它们分别在不同的位置(或时间)执行,如果没有事先为某个位置自定义子例程,varnish将会执行默认的定义
  • VCL 策略在启用前,会由management进程将其转换为C代码,而后再由gcc编译器将C代码编译成二进制程序。编译完成后,management负责将其连接至varnish实例,即child进程。正是由于编译工作在child进程之外完成,它避免了装载错误格式VCL的风险。因此,varnish修改配置的开销非常小,其可以同时保有几份尚在引用的旧版本配置,也能够让新的配置即刻生效。编译后的旧版本配置通常在varnish重启时才会被丢弃,如果需要手动清理,则可以使用varnishadm的vcl.discard命令完成

4.Varnish后端存储(缓存数据的方式)

varnish支持多种不同类型的后端存储,这可以在varnishd启动时使用-s选项指定

存储的类型包括:

  • file:使用特定的文件存储全部的缓存数据,并通过操作系统的mmap()系统函数调用将整个缓存文件映射至内存区域(如果条件允许);
  • malloc:使用malloc()函数调用在varnish启动时向操作系统申请指定大小的内存空间以存储缓存对象
  • persistent(experimental):与file的功能相同,但可以持久存储数据(即重启 varnish数据时不会被清除),仍处于测试期 varnish无法追踪某缓存对象是否存入了缓存文件,从而也就无从得知磁盘上的缓存文件是否可用,因此file存储方法在varnish停止或重启时会清除数据。而persistent方法的出现对此有了一个弥补,但persistent仍处于测试阶段,例如目前尚无法有效处理要缓存对象总体大小超出缓存空间的情况,所以其仅适用于有着巨大缓存空间的场景

选择使用合适的存储方式有助于提升系统性,从经验的角度来看,建议在内存空间足以存储所有的缓存对象时使用malloc的方法,反之file存储将有着更好的性能的表现。然而需要注意的是,varnishd实际上使用的空间比使用-s选项指定的缓存空间更大,一般说来其需要为每个缓存对象多使用差不多1K左右的存储空间,这意味着,对于100万个缓存对象的场景来说,其使用的缓存空间将超出指定大小1G左右。另外为了保存数据结构等,varnish 自身也会占去不小的内存空间

为 varnishd 指定使用的缓存类型时,-s 选项可接受的参数格式如下: malloc[,size] 或file[,path[,size[,granularity]]] 或persistent,path,size {experimental} file中的granularity用于设定缓存空间分配单位,默认单位是字节,所有其它的大小都会被圆整

5.Varnish 的特点

  • 基于内存进行缓存,重启后数据将消失
  • 利用虚拟内存方式,I/O 性能好
  • 支持设置 0~60 秒精确缓存时间
  • VCL 配置管理比较灵活
  • 32 位机器上缓存文件大小为最大 2GB
  • 具有强大的管理功能
  • 状态机设计巧妙,结构清晰
  • 利用二叉堆管理缓存文件,可达到积极删除目的

6.Varnish与Squid对比

优点

  • Varnish稳定性高,当Squid和Varnish同时完成相同负荷工作时,Squid发生故障的几率高于Varnish,因为使用Squid要经常重启
  • Varnish访问速度快,Varnish采用Visual Page Cache技术,所有缓存数据都直接从内存中读取,而Squid从硬盘中读取
  • Varnish支持更多的并发连接,因为Varnish的TCP连接释放要比Squid快,因此在高并发情况下可以支持更多的TCP连接
  • Varnish可以通过管理端口,使用正则表达式批量的清除部分缓存
  • Squid属于单进程使用单核CPU,但Varnish通过fork形式打开多进程来处理,所以可以合理的使用所有核来处理相应的请求

缺点

  • 高并发下,Varnish消耗更多的CPU、I/O和内存资源
  • Varnish进程一旦挂起、崩溃或重启,缓存的数据会从内存中释放,此时所有的请求都会转发到后端服务器上,给后端服务器造成很大压力
  • Varnish使用中如果单个url的请求通过HA/F5(负载均衡)每次请求到不同的Varnish服务器中,被请求的Varnish服务器都会被穿透到后端,而且同样的请求会在多台服务器上缓存,造成Varnish缓存的资源浪费,也造成性能下降
  • Varnish不支持正向代理缓存 综上所述在访问量很大的情况下推荐使用varnish的内存缓存方式启动,而且后面需要跟多台squid服务器。主要为了防止前面的varnish服务、服务器被重启的情况下,前期肯定会有很多的穿透这样squid可以担当第二层CACHE,而且也弥补了varnish缓存在内存中重启都会释放的问题 资源浪费的问题可以在负载均衡上做url哈希,让单个url请求固定请求到一台varnish服务器上,可以解决该问题

7.Varnish与其余主流软件对比

二、Varnish工作原理


  • Receive状态:请求处理入口状态,根据VCL规则判断该请求应该Pass或Pipe,还是进入Lookup(本地查询)
  • Lookup状态:进入此状态后,会在hash表中查找数据,若找到则进入Hit状态,否则进入Miss状态
  • Fetch状态:在Fetch状态下,对请求进行后端获取、发送请求、获得数据,并进行本地存储
  • Deliver状态:将获取到的数据发送给客户端,然后完成本次请求
  • Pipe状态:不通过varnish,开通“管道”,直接有后端真实的web 节点回复客户端请求

三、部署Varnish负载均衡高可用群集


主机 系统 IP 网卡 软件
Varnish Centos 6.7 64Bit 192.168.1.10 vmnet1 varnish
Web1(Apache) Centos 6.7 64Bit 192.168.1.100 vmnet1 httpd
Web2(Apache) Centos 6.7 64Bit 192.168.1.200 vmnet1 httpd

Varnish

1.环境准备

vim /etc/sysconfig/network-scripts/ifcfg-eth0
        DEVICE=eth0								//网卡名称
        TYPE=Ethernet							//网卡类型为以太网
        ONBOOT=yes							//开机自启该网卡
        NM_CONTROLLED=no						//关闭NetworkManager
        BOOTPROTO=static						//网卡设置为静态方式
        IPADDR=192.168.1.10						//IP地址配置
        NETMASK=255.255.255.0					//子网掩码配置
/etc/init.d/network restart

2.安装Varnish

yum -y install libtool ncurses-devel pcre-devel libxslt groff pkgconfig gcc gcc-c++
//安装varnish依赖的开发环境
rpm -ivh libedit-devel-2.11-4.20080712cvs.1.el6.x86_64.rpm
rpm -ivh python-imaging-1.1.6-19.el6.x86_64.rpm
rpm -ivh python-docutils-0.6-1.el6.noarch.rpm    //安装rpm依赖包
tar -zxvf varnish-4.0.1.tgz -C /usr/src/
cd /usr/src/varnish-4.0.1/
./autogen.sh					//运行varnish脚本,会自动设置libtool变量等信息
./configure --prefix=/usr/local/varnish --enable-debugging-symbols --enable-developer-warnings

选项 --enable-debugging-symbols:开启调试,调试一些bug信息的标志,默认为“NO” --enable-developer-warnings:启用提示警告,默认为"NO"

make && make install
echo "PATH=$PATH:/usr/local/varnish/bin:/usr/local/varnish/sbin">>/etc/profile
//将varnish命令路径加入PATH变量,这时在任意位置都可使用varnish相关命令
source /etc/profile				//立即生效该PATH变量

3.配置Varnish

cp /usr/local/varnish/share/doc/varnish/example.vcl /usr/local/varnish/default.vcl
//拷贝默认提供的varnish配置文件到varnish目录下
vim /usr/local/varnish/default.vcl
        import directors;					//加载directors模块,提供负载均衡
        backend web1 {					//定义后端服务器的标识名称
            .host = "192.168.1.100";		//定义后端服务器的IP
            .port = "80";					//定义后端服务器监听端口
            .probe = {					//开启健康检查
            .url = "/";				//检查请求的URL(请求服务器的网页根目录)
            .interval = 5s;				//查询的间隔时长(每隔几秒检测一次)
            .timeout = 1s;			//超时时间,即等待后端1s都无响应即为故障
            .window = 5;			//判断健康状态时,依最近多少次的检测作为依据
            .threshold = 3;		//.window指定次数中,3次成功,才代表后端健康
            }
        }
        backend web2 {
            .host = "192.168.1.200";
            .port = "80";
            .probe = {
            .url = "/";
            .interval = 5s;
            .timeout = 1s;
            .window = 5;
            .threshold = 3;
            }
        }
        sub vcl_init {					//VCL初始化VMODs模块,定义director
            new bar = directors.round_robin();	//定义调度算法,该处为加权轮询
            bar.add_backend(web1);			//添加标识名称为web1的加入director
            bar.add_backend(web2);			//添加标识名称为web2的加入director
        }
        sub vcl_recv {
            set req.backend_hint = bar.backend();	//将所有的流量转发给dictctors
        }
varnishd -C -f /usr/local/varnish/default.vcl
//检测VCL配置是否有误,如输出一系列的内置配置,即无问题,反之
varnishd -f /usr/local/varnish/default.vcl -a 0.0.0.0:80
//启动varnish,并监听当前服务器的80端口

-f /usr/local/etc/varnish/default.vcl 这个 –f 选项指定varnishd使用哪个配置文件。 -s malloc,1G 这个 –s 选项用来确定varnish使用的存储类型和存储容量,我使用的是malloc类型(malloc是一个C函数,用于分配内存空间), 1G 定义多少内存被malloced,1G = 1gigabyte。 -T 127.0.0.1:2000 Varnish有一个基于文本的管理接口,启动它的话可以在不停止varnish的情况下来管理varnish。您可以指定管理软件监听哪个接口。当然您不能让全世界的人都能访问您的varnish管理接口,因为他们可以很轻松的通过访问varnish管理接口来获得您的root访问权限。我推荐只让它监听本机端口。如果您的系统里有您不完全信任的用户,您可以通过防火墙规则来限制他访问varnish的管理端口。 -a 0.0.0.0:8080 这一句的意思是制定varnish监听所有IP发给8080端口的http请求,如果在生产环境下,您应该让varnish监听80,这也是默认的。 vcl配置文件的介绍请执行如何命令查看: man /usr/local/varnish/share/man/man7/vcl.7

netstat -utpln | grep varnish
varnishlog					//动态输出varnish的缓存及客户端访问完整情况

varnish 命令参数 -f:指定varnish服务器的配置文件 -aaddress:port:表示varnish对httpd的监听地址及端口 -Taddress:port:设定varnish的 telnet管理地址及端口 -baddress:port:表示后端服务器的地址及端口 -d:表示使用debug调试模式 -Pfile:varnish进程PID文件存放路径 -s:varnish缓存文件位置与大小(-s file,文件路径,大小) -w:最小,最大线程和超时时间(例:-w 1200,5 1200 10)

Web1

vim /etc/sysconfig/network-scripts/ifcfg-eth0
        DEVICE=eth0								//网卡名称
        TYPE=Ethernet							//网卡类型为以太网
        ONBOOT=yes							//开机自启该网卡
        NM_CONTROLLED=no						//关闭NetworkManager
        BOOTPROTO=static						//网卡设置为静态方式
        IPADDR=192.168.1.100					//IP地址配置
        NETMASK=255.255.255.0					//子网掩码配置
/etc/init.d/network restart
yum -y install httpd					//安装apache服务
echo "This is Web1">/var/www/html/index.html
//给第一台Web服务器编写测试页面
/etc/init.d/httpd start && chkconfig --level 35 httpd on
//启动httpd服务并设置为开机自启

Web2

vim /etc/sysconfig/network-scripts/ifcfg-eth0
        DEVICE=eth0								//网卡名称
        TYPE=Ethernet							//网卡类型为以太网
        ONBOOT=yes							//开机自启该网卡
        NM_CONTROLLED=no						//关闭NetworkManager
        BOOTPROTO=static						//网卡设置为静态方式
        IPADDR=192.168.1.200					//IP地址配置
        NETMASK=255.255.255.0					//子网掩码配置
/etc/init.d/network restart
yum -y install httpd					//安装apache服务
echo "This is Web2">/var/www/html/index.html
//给第二台Web服务器编写测试页面
/etc/init.d/httpd start && chkconfig --level 35 httpd on
//启动httpd服务并设置为开机自启

测试

IE --> http://192.168.1.10(Varnish服务器IP) 测试负载均衡

注:测试负载均衡时,由于采用加权轮询算法,因此有可能刷新几次都在Web1服务器上,可多刷新几次 测试高可用 ifdown eth0 //在Web1上输入,模拟Web1故障

注:测试高可用时,由于Varnish强大的缓存能力,Web1的页面会缓存,因此当将Web1停止时,可以还可以访问到Web1,这是等待一会,让Varnish自动清空缓存即可