最近在折腾varnish 4.0,话说从3.0到4.0变化挺大的,许多配置做了调整

对比Varnish 3.x的主要改进点
(1)、完全支持流对象;
(2)、可后台获取失效的对象,即Client/backend分离;
(3)、新的vanishlog查询语言,允许对请求进行自动分组;
(4)、复杂的请求时间戳和字节计数;
(5)、安全方面的提升;


涉及VCL语法的改变点
(1)、vcl配置文件需明确指定版本:即在vcl文件的第一行写上 vcl 4.0;
(2)、vcl_fetch函数被vcl_backend_response代替,且req.*不再适用vcl_backend_response;
(3)、后端源服务器组director成为varnish模块,需import directors后再在vcl_init子例程中定义;
(4)、自定义的子例程(即一个sub)不能以vcl_开头,调用使用call sub_name;
(5)、error()函数被synth()替代;
(6)、return(lookup)被return(hash)替代;
(7)、使用beresp.uncacheable创建hit_for_pss对象;
(8)、变量req.backend.healty被std.healthy(req.backend)替代;
(9)、变量req.backend被req.backend_hint替代;
(10)、关键字remove被unset替代;

详细可以参考:

https://www.varnish-cache.org/docs/4.0/whats-new/upgrading.html


工作流程

Varnish 4.0.3详细配置_第1张图片

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

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

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

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

Varnish 4.0.3详细配置_第2张图片

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


请求处理流程

Varnish 4.0.3详细配置_第3张图片

 

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 状态下,对请求进行后端获取,发送请求,获得数据,并根据设置进行本地存储;
Deliver(vcl_deliver)状态:将获取到的数据发给客户端,然后完成本次请求;

注:Varnish4中在vcl_fetch部分略有出入,已独立为vcl_backend_fetch和vcl_backend_response 2个函数;

内置函数(也叫子例程)
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模块;


以上内容为在他人基础上学习总结而来,并非全部原创,望多多指教

废话就不多说了,以下是我目前整理出来的配置,有些参数还需要进一优化调整:

1、后端服务器健康检查

# vim /etc/varnish/health_check.vcl

probe backend_healthcheck {
    .interval = 5s;
    .timeout = 3s;
    .window = 10;
    .threshold = 8;
    
    .request =
    "GET /favicon.ico HTTP/1.1"
    "Host: www.xxx.com"
    "Connection: close"
    "Accept-Encoding: foo/bar";
}

2、后端服务器地址池配置

# vim /etc/varnish/backends.vcl

import directors;
include "health_check.vcl";

backend d102_app_07 {
    .host = "10.0.11.145";
    .port = "80";
    
    .first_byte_timeout = 9s;
    .connect_timeout = 3s;
    .between_bytes_timeout = 1s;
    
    .probe = backend_healthcheck;
}

backend d102_app_08 {
    .host = "10.0.11.146";
    .port = "80";
    
    .first_byte_timeout = 9s;
    .connect_timeout = 3s;
    .between_bytes_timeout = 1s;
    
    .probe = backend_healthcheck;
}

sub vcl_init {
    new web = directors.random();
    
    web.add_backend(d102_app_07, 1);
    web.add_backend(d102_app_08, 1);
}

3、缓存规则主配置

# vim /etc/varnish/default.vcl

vcl 4.0;

import std;
include "backends.vcl";

acl allow_purge_cache {
    "127.0.0.1";
    "10.0.0.0"/8;
    "172.0.0.0"/8;
}

sub vcl_recv {
    if (req.method == "PURGE") {
        if (!client.ip ~ allow_purge_cache) {
            return (synth(405, "Not Allowed."));
        }
        
        return (purge);
    }
    
    set req.backend_hint = web.backend();
    
    if (req.url ~ "\.(php|asp|aspx|jsp|do|ashx|shtml)($|\?)") {
        return (pass);
    }
    
    if (req.url ~ "\.(css|js|html|htm|bmp|png|gif|jpg|jpeg|ico|gz|tgz|bz2|tbz|zip|rar|mp3|mp4|ogg|swf|flv)($|\?)") {
        unset req.http.cookie;
        return (hash);
    }
    
    if (req.restarts == 0) {
        if (req.http.x-forwarded-for) {
            set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip;
        } else {
            set req.http.X-Forwarded-For = client.ip;
        }
    }
    
    if (req.http.Cache-Control ~ "(?i)no-cache") {
        if (!(req.http.Via || req.http.User-Agent ~ "(?i)bot" || req.http.X-Purge)) {
            return (purge);
        }
    }
    
    if (req.method != "GET" && 
        req.method != "HEAD" && 
        req.method != "PUT" && 
        req.method != "POST" && 
        req.method != "TRACE" && 
        req.method != "OPTIONS" && 
        req.method != "PATCH" && 
        req.method != "DELETE") {        
        return (pipe);
    }
    
    if (req.method != "GET" && req.method != "HEAD") {
        return (pass);
    }
    
    if (req.http.Authorization) {
        return (pass);
    }
    
    if (req.http.Accept-Encoding) {
        if (req.url ~ "\.(bmp|png|gif|jpg|jpeg|ico|gz|tgz|bz2|tbz|zip|rar|mp3|mp4|ogg|swf|flv)$") {
            unset req.http.Accept-Encoding;        
        } elseif (req.http.Accept-Encoding ~ "gzip") {
            set req.http.Accept-Encoding = "gzip";
        } elseif (req.http.Accept-Encoding ~ "deflate") {
            set req.http.Accept-Encoding = "deflate";
        } else {
            unset req.http.Accept-Encoding;
        }
    }
    
    if (req.http.Upgrade ~ "(?i)websocket") {
        return (pipe);
    }
    
    if (!std.healthy(req.backend_hint)) {
        unset req.http.Cookie;
    }
    
    if (req.http.x-pipe && req.restarts > 0) {
        unset req.http.x-pipe;
        return (pipe);
    }
    
    return (hash);
}

sub vcl_pipe {
    if (req.http.upgrade) {
        set bereq.http.upgrade = req.http.upgrade;
    }
    
    return (pipe);
}

sub vcl_pass {
    if (req.method == "PURGE") {
        return (synth(502, "PURGE on a passed object."));
    }
}

sub vcl_hash {
    hash_data(req.url);
    
    if (req.http.host) {
        hash_data(req.http.host);
    } else {
        hash_data(server.ip);
    }
    
    if (req.http.Cookie) {
        hash_data(req.http.Cookie);
    }
    
    if (req.http.Accept-Encoding ~ "gzip") {
        hash_data("gzip");
    } elseif (req.http.Accept-Encoding ~ "deflate") {
        hash_data("deflate");
    }
}

sub vcl_hit {
    if (req.method == "PURGE") {
        return (synth(200, "Purged."));
    }
    
    if (obj.ttl >= 0s) {
        return (deliver);
    }
    
    if (std.healthy(req.backend_hint)) {
        if (obj.ttl + 10s > 0s) {
            return (deliver);
        } else {
            return(fetch);
        }
    } else {
        if (obj.ttl + obj.grace > 0s) {
            return (deliver);
        } else {
            return (fetch);
        }
    }
    
    return (deliver);
}

sub vcl_miss {
    if (req.method == "PURGE") {
        return (synth(404, "Purged."));
    }
    
    return (fetch);
}

sub vcl_backend_response {
    set beresp.grace = 5m;
    
    set beresp.ttl = std.duration(regsub(beresp.http.Cache-Control, ".*s-maxage=([0-9]+).*", "\1") + "s", 0s);
    if (beresp.ttl > 0s) {
        unset beresp.http.Set-Cookie;
    }
    
    if (beresp.http.Set-Cookie) {
        set beresp.uncacheable = true;
        return (deliver);
    }
    
    if (beresp.http.Cache-Control && beresp.ttl > 0s) {
        set beresp.grace = 1m;
        unset beresp.http.Set-Cookie;
    }
    
    if (beresp.http.Content-Length ~ "[0-9]{8,}") {
        set bereq.http.x-pipe = "1";
        return (retry);
    }
    
    if (bereq.url ~ "\.(php|asp|aspx|jsp|do|ashx|shtml)($|\?)") {
        set beresp.uncacheable = true;
        return (deliver);
    }
    
    if (bereq.url ~ "\.(css|js|html|htm|bmp|png|gif|jpg|jpeg|ico|gz|tgz|bz2|tbz|zip|rar|mp3|mp4|ogg|swf|flv)($|\?)") {
        unset beresp.http.set-cookie;
    }
    
    if (bereq.url ~ "^[^?]*\.(mp[34]|rar|tar|tgz|gz|wav|zip|bz2|xz|7z|avi|mov|ogm|mpe?g|mk[av])(\?.*)?$") {
        unset beresp.http.set-cookie;
        set beresp.do_stream = true;
        set beresp.do_gzip = false;
    }
    
    if ((!beresp.http.Cache-Control && !beresp.http.Expires) || 
         beresp.http.Pragma ~ "no-cache" || 
         beresp.http.Cache-Control ~ "(no-cache|no-store|private)") {
        set beresp.ttl = 120s;
        set beresp.uncacheable = true;
        return (deliver);
    }
    
    if (beresp.ttl <= 0s || beresp.http.Set-Cookie || beresp.http.Vary == "*") {
        set beresp.ttl = 120s;
        set beresp.uncacheable = true;
        return (deliver);
    }
    
    if (bereq.url ~ "\.(css|js|html|htm|bmp|png|gif|jpg|jpeg|ico)($|\?)") {
        set beresp.ttl = 15m;
    } elseif (bereq.url ~ "\.(gz|tgz|bz2|tbz|zip|rar|mp3|mp4|ogg|swf|flv)($|\?)") {
        set beresp.ttl = 30m;
    } else {
        set beresp.ttl = 10m;
    }
    
    return (deliver);
}

sub vcl_purge {
    if (req.method != "PURGE") {
        set req.http.X-Purge = "Yes";
        return (restart);
    }
}

sub vcl_deliver {
    if (obj.hits > 0) {
        set resp.http.X-Cache = "HIT from " + req.http.host;
        set resp.http.X-Cache-Hits = obj.hits;
    } else {
        set resp.http.X-Cache = "MISS from " + req.http.host;
    }
    
    unset resp.http.X-Powered-By;
    unset resp.http.Server;
    
    unset resp.http.Via;
    unset resp.http.X-Varnish;
    
    unset resp.http.Age;
}

sub vcl_backend_error {
    if (beresp.status == 500 || 
        beresp.status == 501 || 
        beresp.status == 502 || 
        beresp.status == 503 || 
        beresp.status == 504) {
        return (retry);
    }
}

sub vcl_fini {
    return (ok);
}

4、启动参数配置

# vim /etc/sysconfig/varnish

NFILES=131072
MEMLOCK=25165824
NPROCS="unlimited"

RELOAD_VCL=1
VARNISH_VCL_CONF=/etc/varnish/default.vcl

VARNISH_LISTEN_ADDRESS=0.0.0.0
VARNISH_LISTEN_PORT=6081

VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1
VARNISH_ADMIN_LISTEN_PORT=6082

VARNISH_SECRET_FILE=/etc/varnish/secret

VARNISH_MIN_THREADS=240
VARNISH_MAX_THREADS=4800
VARNISH_THREAD_TIMEOUT=120

VARNISH_STORAGE_SIZE=24G
VARNISH_STORAGE="malloc,${VARNISH_STORAGE_SIZE}"

VARNISH_TTL=120

DAEMON_OPTS="-a ${VARNISH_LISTEN_ADDRESS}:${VARNISH_LISTEN_PORT} \
        -f ${VARNISH_VCL_CONF} \
        -T ${VARNISH_ADMIN_LISTEN_ADDRESS}:${VARNISH_ADMIN_LISTEN_PORT} \
        -t ${VARNISH_TTL} \
        -p thread_pools=24 \
        -p thread_pool_min=${VARNISH_MIN_THREADS} \
        -p thread_pool_max=${VARNISH_MAX_THREADS} \
        -p thread_pool_timeout=${VARNISH_THREAD_TIMEOUT} \
        -u varnish -g varnish \
        -S ${VARNISH_SECRET_FILE} \
        -s ${VARNISH_STORAGE} \
        -p timeout_idle=60 \
        -p timeout_linger=1 \
        -p http_resp_hdr_len=16k \
        -p http_max_hdr=256 \
        -p http_req_hdr_len=16k \
        -p lru_interval=120 \
        -p listen_depth=8192"

5、启动脚本调整

# vim /etc/init.d/varnish


exec="/usr/sbin/varnishd"
修改为
exec="/usr/bin/numactl --interleave all /usr/sbin/varnishd"