Linux进阶之Varnish 4.0 详解

2017-09-16  本文已影响0人  王王王小白

简介:

Varnish 是一款高性能且开源的反向代理服务器和 HTTP 加速器,其采用全新的软件体系机构,和现在的硬件体系紧密配合,与传统的 squid 相比,varnish 具有性能更高、速度更快、管理更加方便等诸多优点;

Varnish与Squid的对比

相同点

都是一个反向代理服务器;
都是开源软件;

Varnish的优势:

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

Varnish的劣势

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

Varnish劣势的解决方案

针对劣势一:在访问量很大的情况下推荐使用varnish的内存缓存方式启动,而且后面需要跟多台squid服务器。主要为了防止前面的varnish服 务、服务器被重启的情况下,大量请求穿透varnish,这样squid可以就担当第二层CACHE,而且也弥补了varnish缓存在内存中重启都会释 放的问题;
针对劣势二:可以在负载均衡上做url哈希,让单个url请求固定请求到一台varnish服务器上;

Varnish 进程的工作模式:

(1)Varnish 启动或有2个进程 master(management) 进程和 child(worker) 进程。master 读入存储配置命令,进行初始化,然后 fork,监控 child。child 则分配线程进行 cache 工作,child 还会做管理线程和生成很多 worker 线程。
(2)child 进程主线程初始化过程中,将存储大文件整个加载到内存中,如果该文件超出系统的虚拟内存,则会减少原来配置 mmap 大小,然后继续加载,这时候创建并初始化空闲存储结构体,放在存储管理的 struct 中,等待分配。
接着 Varnish 某个负责接受新 http 连接的线程开始等待用户,如果有新的 http 连接,但是这个线程只负责接收,然后唤醒等待线程池中的 work 线程,进行请求处理。
worker 线程读入 uri 后,将会查找已有的 object,命中直接返回,没有命中,则会从后端服务器中取出来,放到缓存中。如果缓存已满,会根据 LRU 算法,释放旧的 object。对于释放缓存,有一个超时线程会检测缓存中所有 object 的生命周期,如果缓存过期 (ttl),则删除,释放相应的存储内存。

Child进程特点:

Child 进程分配若干线程进行工作,主要包括一些管理线程和很多 worker 线程,可分为:
(1)Accept线程:接受请求,将请求挂在overflow队列上;
(2)Work线程:有多个,负责从overflow队列上摘除请求,对请求进行处理,直到完成,然后处理下一个请求;
(3)Epoll线程:一个请求处理称为一个session,在session周期内,处理完请求后,会交给Epoll处理,监听是否还有事件发生;
(5)Expire线程:对于缓存的object,根据过期时间,组织成二叉堆,该线程周期检查该堆的根,处理过期的文件,对过期的数据进行删除或重取操作;

Varnish 处理 HTTP 请求的过程如下


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

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

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

4.Fetch(vcl_fetch)状态:在 fetch 状态下,对请求进行后端获取,发送请求,获得数据,并根据设置进行本地存储;

5.Deliver(vcl_deliver)状态:将获取到的数据发给客户端,然后完成本次请求;
注:Varnish4中在vcl_fetch部分略有出入,已独立为vcl_backend_fetch和vcl_backend_response2个函数;

内置函数(也叫子例程)

VCL中内置公共变量

变量(也叫object)适用范围



req:The request object,请求到达时可用的变量
bereq:The backend request object,向后端主机请求时可用的变量
beresp:The backend response object,从后端主机获取内容时可用的变量
resp:The HTTP response object,对客户端响应时可用的变量
obj:存储在内存中时对象属性相关的可用的变量

优雅模式(Garce mode)

Varnish中的请求合并

安装配置

安装包下载地址:http://repo.varnish-cache.org/redhat/varnish-4.0/el6/

yum localinstall --nogpgcheck varnish-4.0.0-1.el6.x86_64.rpm varnish-libs-4.0.0-1.el6.x86_64.rpm varnish-docs-4.0.0-1.el6.x86_64.rpm
vim /etc/sysconfig/varnish # 编辑配置文件,修改如下项
VARNISH_STORAGE_SIZE=100M # 此值根据自身情况调整,测试环境可调低此值
VARNISH_STORAGE="malloc,${VARNISH_STORAGE_SIZE}" # Varnish 4中默认使用malloc(即内存)作为缓存对象存储方式;
systemctl start varnish  # 启动varnish,默认外部请求的监听端口6081,管理端口6082,后端主机127.0.0.1:80
===========
varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082 # 登录管理命令行
varnish> vcl.list                 # 列出所有的配置
varnish> vcl.load test1 test.vcl  # 加载编译新配置,test1是配置名,test.vcl是配置文件
varnish> vcl.use test1            # 使用配置,需指定配置名,当前使用的配置以最后一次vcl.use为准
varnish> vcl.show test1           # 显示配置内容,需指定配置名

实例解析

#
# This is an example VCL file for Varnish.
#
# It does not do anything by default, delegating control to the
# builtin VCL. The builtin VCL is called when there is no explicit
# return statement.
#
# See the VCL chapters in the Users Guide at https://www.varnish-cache.org/docs/
# and http://varnish-cache.org/trac/wiki/VCLExamples for more examples.
# Marker to tell the VCL compiler that this VCL has been adapted to the
# new 4.0 format.
vcl 4.0;
import directors;
probe backend_healthcheck { # 创建健康监测
    .url = /health.html;
    .window = 5;
    .threshold = 2;
    .interval = 3s;
}
backend web1 {    # 创建后端主机
    .host = "static1.lnmmp.com";
    .port = "80";
    .probe = backend_healthcheck;
}
backend web2 {
    .host = "static2.lnmmp.com";
    .port = "80";
    .probe = backend_healthcheck;
}
backend img1 {
    .host = "img1.lnmmp.com";
    .port = "80";
    .probe = backend_healthcheck;
}
backend img2 {
    .host = "img2.lnmmp.com";
    .port = "80";
    .probe = backend_healthcheck;
}
vcl_init {    # 创建后端主机组,即directors
    new web_cluster = directors.random();
    web_cluster.add_backend(web1);
    web_cluster.add_backend(web2);
    new img_cluster = directors.random();
    img_cluster.add_backend(img1);
    img_cluster.add_backend(img2);
}
acl purgers {    # 定义可访问来源IP
        "127.0.0.1";
        "192.168.0.0"/24;
}
sub vcl_recv {
    if (req.request == "GET" && req.http.cookie) {    # 带cookie首部的GET请求也缓存
        return(hash);
}
    if (req.url ~ "test.html") {    # test.html文件禁止缓存
        return(pass);
    }
    if (req.request == "PURGE") {    # PURGE请求的处理
        if (!client.ip ~ purgers) {
            return(synth(405,"Method not allowed"));
        }
        return(hash);
    }
    if (req.http.X-Forward-For) {    # 为发往后端主机的请求添加X-Forward-For首部
        set req.http.X-Forward-For = req.http.X-Forward-For + "," + client.ip;
    } else {
        set req.http.X-Forward-For = client.ip;
    }
    if (req.http.host ~ "(?i)^(www.)?lnmmp.com$") {    # 根据不同的访问域名,分发至不同的后端主机组
        set req.http.host = "www.lnmmp.com";
        set req.backend_hint = web_cluster.backend();
      } elsif (req.http.host ~ "(?i)^images.lnmmp.com$") {
            set req.backend_hint = img_cluster.backend();
      }
        return(hash);
    }
sub vcl_hit { # PURGE请求的处理
    if (req.request == "PURGE") {  
        purge;
        return(synth(200,"Purged"));
    }
}
sub vcl_miss {    # PURGE请求的处理
    if (req.request == "PURGE") {
        purge;
        return(synth(404,"Not in cache"));
    }
}
sub vcl_pass {    # PURGE请求的处理
    if (req.request == "PURGE") {
        return(synth(502,"PURGE on a passed object"));
    }
}
sub vcl_backend_response { # 自定义缓存文件的缓存时长,即TTL值
    if (req.url ~ "\.(jpg|jpeg|gif|png)$") {
        set beresp.ttl = 7200s;
    }
    if (req.url ~ "\.(html|css|js)$") {
        set beresp.ttl = 1200s;
    }
    if (beresp.http.Set-Cookie) { # 定义带Set-Cookie首部的后端响应不缓存,直接返回给客户端
        return(deliver);
    }
}
sub vcl_deliver {
    if (obj.hits > 0) {    # 为响应添加X-Cache首部,显示缓存是否命中
        set resp.http.X-Cache = "HIT from " + server.ip;
    } else {
        set resp.http.X-Cache = "MISS";
    }
}
上一篇 下一篇

猜你喜欢

热点阅读