第5章
分布式架构Varnish
Varnish是一款高性能且开源的反向代理服务器和HTTP加速器,主要通过缓存来实现Web访问加速。它基于内存进行缓存,支持精确缓存时效,性能高效。其VCL配置管理比较灵活,支持后端服务器负载和健康检查,内部实现了负载均衡轮询调用服务器。
本章重点内容如下:
- Varnish工作原理
- Varnish源码编译安装
- Varnish配置
- Varnish核心指令
- Varnish缓存
- Varnish处理策略
- Varnish健康检查
- Varnish优化
- Varnish高可用
5.1 Varnish工作原理
Varnish主要有两个进程,管理进程(Management)和子进程(Child)。其中,管理进程主要负责配置变更、编译VCL、监控运行、初始化、定期检查子进程(子进程宕机会重新开启);子进程包括Worker线程、Acceptor线程、Expiry线程,内部使用workspace工作区来减少多个线程间对内存的竞争。
Varnish工作模型图如图5-1所示。
图5-1 Varnish工作模型图
如图5-1所示,Varnish的工作流程如下。
1)Varnish的某个负责接收新HTTP连接的线程开始等待用户,如果有新的HTTP连接过来,它会负责接收,然后叫醒某个等待中的线程,并把具体的处理过程交给它。Worker线程读入HTTP请求的URI,查找已有的object。如果命中则直接返回并回复用户;如果没有命中,则需要从后端服务器中读取所请求的内容并存到缓存中,然后再回复。
2)分配缓存的过程:Varnish缓存对象时,默认会忽略体积过大的对象,因此会事先读取对象(object)的大小,经过默认配置的验证和筛选,保证其自身缓存策略的高效性。
为了读写高效性,会将筛选后合适的对象(object)压缩,压缩过程中会从现有的空闲存储结构体中查找,找到最合适大小的空闲存储块并将object分配给它。如果空闲块没有用完,就用多余的内存另外组成一个空闲存储块,挂到管理结构体上。如果缓存已满,就根据LRU机制,把最旧的object释放掉。
3)释放缓存的过程:有一个超时线程,检测缓存中所有object的生存期,如果超出设定的TTL(Time To Live)还没有被访问,就删除该object,并且释放相应的结构体及存储内存。注意释放时会检查该存储内存块前后空闲内存块,如果前后空闲内存和该释放内存是连续的,就将它们合并成一块更大的内存。
4)整个文件缓存的管理,没有考虑文件与内存的关系,实际上认为所有的object都在内存中,如果内存不足,系统会自动将其换到swap空间,而不需要Varnish程序去控制。
5)日志:为了与系统的其他部分进行交互,Child进程使用了可以通过文件系统接口进行访问的共享内存日志(shared memory log),因此,如果某线程需要记录信息,其仅需要持有一个锁,而后向共享内存中的某内存区域写入数据,再释放持有的锁即可。为了减少竞争,每个worker线程都使用了日志数据缓存。
6)共享内存日志大小一般为90MB,其分为两部分,前一部分为计数器,后一部分为客户端请求的数据。Varnish提供了多个不同的工具,如varnishlog、varnishncsa、varnishstat等,用以来分析共享内存日志中的信息并以指定的方式进行显示。
Varnish的优势如下:
- Varnish支持更多的并发连接,因为Varnish的TCP连接比squid快;
- Varnish访问速度快,因为其采用了Visual Page Cache技术,直接从内存中读取数据;
- Varnish通过管理端口,使用正则表达式批量清除部分缓存;
- Varnish量级轻且开源。
Varnish的缺点如下:
- 进程一旦挂掉或重启,缓存的数据将从内存中完全释放;
- 用多台Varnish实现负载均衡时,每次请求都会落到不同的Varnish服务器中,可能会造成URL请求穿透到后端。
5.2 Varnish源码编译安装
以Centos平台编译环境为例,安装Make并编译Varnish以及相关依赖插件的方法如代码清单5-1所示。
代码清单5-1 编译Varnish以及依赖插件
#需要安装编译gcc,已安装忽略 yum -y install gcc automake autoconf libtool make yum install gcc gcc-c++ #安装pcre 正规表达式,已安装忽略 wget http:// www.programming.cn/pcre/pcre-8.36.tar.gz tar -zxvf pcre-8.36.tar.gz cd pcre-8.36 ./configure --prefix=/usr/local/pcre/ make make install #安装libedit-dev, 已安装忽略 yum install libedit-dev* #安装Varnish wget -c http:// repo.varnish-cache.org/source/varnish-3.0.1.tar.gz tar xzvf varnish-3.0.1.tar.gz cd varnish-3.0.1 ./configure --prefix=/usr/local/varnish PKG_CONFIG_PATH=/usr/lib/pkgconfig make make install
在Varnish的configure命令中,--prefix=path定义一个目录,也就是Varnish的安装目录,用于存放服务器上的文件。默认使用/usr/local/ varnish。
安装成功后 /usr/local/varnish目录如下:
bin etc include lib sbin share var
设置软连接ln -s /usr/local/varnish/sbin/varnishd /usr/sbin/,ln -s /usr/local/varnish/bin/* /usr/local/bin/,目的是让里面的内容暴露到外层,方便查看。
通过/usr/sbin -V查看varnish版本号,复制核心配置文件(default.vcl)到外层,如:cp /usr/local/varnish/share/doc/varnish/example.vcl /usr/local/varnish/default.vcl
启动varnish的方法如下:
进入cd /usr/sbin/ 运行./varnished -f /usr/local/varnish/default.vcl -s malloc,32M -T 192.168.10.101:2000 -a 0.0.0.0:2222
其中,-s malloc表示存储类型和容量,-T 192.168.10.101:2000表示指定管理IP和端口,-a 0.0.0.0:2222表示对外界提供Web服务的IP和端口。
关闭varnish的方法如下:
进入cd /usr/sbin/ 运行pkill varnished
通过Varnish代理运行的方法如下:
外部地址端口(http://192.168.10.101:2222/zachary/demo/showtime)被Varnish代理成内部地址端口(http://192.168.10.101:9021/zachary/demo/showtime),并可直接访问Tomcat页面。
Varnish提供了基于端口的管理方式,用户可以通过telnet方式登录到管理端口,对Varnish子进程进行启动、关闭、查看状态和清除缓存等操作,如代码清单5-2所示。
代码清单5-2 Varnish后台管理
[root@varnish ~]#telnet 192.168.10.101 2000 Trying 192.168.10.101... Connected to localhost.localdomain (192.168.10.101). Escape character is '^]'. 200 154 ----------------------------- Varnish HTTP accelerator CLI. ----------------------------- Type 'help' for command list. Type 'quit' to close CLI session. #输入"help"即可得到如下帮助信息 help 200 377 help [command] ping [timestamp] auth response quit banner status #显示服务运行状态 start #启动Varnish的子服务 stop #关闭Varnish的子服务 stats #显示服务的全部状态 #操作VCL配置文件的相关操作,如果要修改VCL文件 vcl.load <configname> <filename> vcl.inline <configname> <quoted_VCLstring> vcl.use <configname> #载入指定的配置文件 vcl.discard <configname> #丢弃指定的VCL配置文件 vcl.list #显示当前载入的VCL配置文件信息 vcl.show <configname> #可以显示某个VCL文件的内容 param.show [-l] [<param>] #用于显示程序的运行参数 param.set <param> <value> #用于动态更改某个运行参数 purge.url <regexp> #用来清除指定规则的URL缓存 purge <field> <operator> <arg> purge.list #列出执行过的规则