Linux 缓存加速 Varnish

Varnish是一款高性能的开源HTTP加速器挪威最大的在线报纸 Verdens Gang 使用3台Varnish代替了原来的12台Squid,性能比以前更好。作者Poul-Henning Kamp是FreeBSD的内核开发者之一,他认为现在的计算机比起1975年已经复杂许多。在1975年时,储存媒介只有两种:内存与硬盘。但现在计算机系统的内存除了主存外,还包括了CPU内的L1、L2,甚至有L3快取。硬盘上也有自己的快取装置,因此Squid Cache自行处理物件替换的架构不可能得知这些情况而做到最佳化,但操作系统可以得知这些情况,所以这部份的工作应该交给操作系统处理,这就是 Varnish cache设计架构。

Varnish项目是2006年发布的第一个版本0.9.距今已经有十多年了,此文档之前也提过varnish还不稳定,那是2007年时候编写的,经过varnish开发团队和网友们的辛苦耕耘,现在的varnish已经很健壮。很多门户网站已经部署了varnish,并且反应都很好,甚至反应比squid还稳定,且效率更高,资源占用更少。相信在反向代理,web加速方面,varnish已经有足够能力代替squid。

Varnish与一般服务器软件类似,分为master(management)进程和child(worker,主要做cache的工作)进程。master进程读入命令,进行一些初始化,然后fork并监控child进程。child进程分配若干线程进行工作,主要包括一些管理线程和很多woker线程。

针对文件缓存部分,master读入存储配置(-s file[,path[,size[,granularity]]] ),调用合适的存储类型,然后创建/读入相应大小的缓存大文件。接着,master初始化管理该存储空间的结构体。这些变量都是全局变量,在fork以后会被child进程所继承(包括文件描述符)。

在child进程主线程初始化过程中,将前面打开的存储大文件整个mmap到内存中(如果超出系统的虚拟内存,mmap失败,进程会减少原来的配置mmap大小,然后继续mmap),此时创建并初始化空闲存储结构体,挂到存储管理结构体,以待分配。

接着,真正的工作开始,Varnish的某个负责接受新HTTP连接的线程开始等待用户,如果有新的HTTP连接过来,它总负责接收,然后叫醒某个等待中的线程,并把具体的处理过程交给它。Worker线程读入HTTP请求的URI,查找已有的object,如果命中则直接返回并回复用户。如果没有命中,则需要将所请求的内容,从后端服务器中取过来,存到缓存中,然后再回复。

分配缓存的过程是这样的:它根据所读到object的大小,创建相应大小的缓存文件。为了读写方便,程序会把每个object的大小变为最接近其大小的内存页面倍数。然后从现有的空闲存储结构体中查找,找到最合适的大小的空闲存储块,分配给它。如果空闲块没有用完,就把多余的内存另外组成一个空闲存储块,挂到管理结构体上。如果缓存已满,就根据LRU机制,把最旧的object释放掉。

释放缓存的过程是这样的:有一个超时线程,检测缓存中所有object的生存期,如果超初设定的TTL(Time To Live)没有被访问,就删除之,并且释放相应的结构体及存储内存。注意释放时会检查该存储内存块前面或后面的空闲内存块,如果前面或后面的空闲内存和该释放内存是连续的,就将它们合并成更大一块内存。

整个文件缓存的管理,没有考虑文件与内存的关系,实际上是将所有的object都考虑是在内存中,如果系统内存不足,系统会自动将其换到swap空间,而不需要varnish程序去控制。

Varnish特点与Squid的对比

Varnish特点:

  基于内存缓存,重启后数据将消失。 
  利用虚拟内存方式,I/O性能好。 
  支持设置0~60秒内的精确缓存时间。 
  VCL(全称varnish config language,这是Varnish自己领域的特定语言)配置管理比较灵活。 
  32位机器上缓存文件大小为最大2G。 
  具有强大的管理功能,例如top,stat,admin,list等。 
  状态机设计巧妙,结构清晰。 
  利用二叉堆管理缓存文件,达到积极删除目的。

Varnish与Squid的对比:

 相同点:

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

 Varnish相较于Squid的优点:

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

 Varnish相较于Squid的缺点:

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


实验环境

  • 192.168.1.2 CentOS 7 Varnish
  • Web1 192.168.1.3 CentOS 7 Nginx+PHP

  • Web2 192.168.1.4 CentOS 7 Nginx+PHP

  • Web3 192.168.1.5 CentOS 7 Nginx+PHP

  • Varnish官网地址:http://varnish-cache.org/

  • Yum 安装方式,https://packagecloud.io/varnishcache

  • yum -y install varnish

  • varnishd -V 查看版本信息

  • /etc/varnish              #varnish工作目录
    /etc/varnish/default.vcl    #默认配置各Child/Cache线程的缓存策略
    /usr/bin/varnishadm     #varnish内置的管理工具,端口默认为 6082
    /usr/bin/varnishhist      #Shared Memory Log交互工具
    /usr/bin/varnishlog
    /usr/bin/varnishncsa
    /usr/bin/varnishstat
    /usr/bin/varnishtop
    /usr/bin/varnishtest    #内置测试工具程序
    /usr/lib/systemd/system/varnish.service  #varnish服务启动脚本
    /usr/lib/systemd/system/varnishncsa.service  #日志持久服务启动脚本

  • /usr/sbin/varnishreload    #VCL配置文件重载程序

  • varnish 主配置文件 /etc/varnish/default.vcl  varnish使用了一种叫VCL的语言去对其进行配置
  • vcl_recv 函数: 接收和处理请求,当请求到达并成功接收后被调用,判断请求的数据并决定如何处理请求;

          pass 表示进入pass模式,将请求交给vcl_pass 函数

        pipe 表示进入pipe模式,将请求交个vcl_pipe函数

        error code[reason]  表示返回'code'给客户端并放弃请求, 'code'是错误标示例如200 405等,'reason'是错误原因

  • vcl_pipe函数:该函数在进入pipe模式时被调用,用于将请求直接传递给后端主机,在请求和返回内容没有改变的情况下,将不变的内容返回给客户端,直到这个链接关闭;该函数有这么些返回值=>

        error code[reason]  同vcl_recv

        pipe 

  • vcl_pass函数: 该函数进入pass模式被调用,用于直接将请求发送给后端主机,后端主机响应后发送给客户端,不进行任何缓存,每次都返回最新内容,该函数有几个返回值=>

        error code[reason] 同vcl_recv

        pass

  • lookup函数: 表示在缓存中查找到被请求的对象,并且根据查找的结果交给vcl_hit函数(命中)或vcl_miss(未命中)函数处理
  • vcl_hit函数:在执行lookup后,如果在缓存中找到对象,该函数将会被自动调用,该函数有以下几个返回值 =>

      deliver 表示找到内容发送给客户端,把控制权交给vcl_deliver函数

      error code[reason] 同vcl_recv

      pass 

  • vcl_miss函数: 在执行lookup后,在缓存中没有找到对象,该函数被调用,该函数可用于判断是否从后端请求内容,该函数有以下几个返回值 =>

      fetch 表示从后端获取内容,并把控制权交给vcl_fetch函数

      error code[reason]  同vcl_recv

      pass

  • vcl_fetch函数:从后端主机更新缓存获取内容后调用该函数,接着判断获取的内容是放入缓存还是直接给客户端,该函数有以下几个返回值=》

      error code[reason]  同vcl_recv

      pass 

      deliver

  • vcl_deliver函数:将在缓存中找到的内容发送给客户端调用的方法,该函数有以下几个返回值=》

      error code[reason]  同vcl_recv

      deliver

  • vcl_timeout函数:在缓存内容到期前调用该函数,该函数有以下几个返回值=》

      discard 表示中缓存中清除该内容

      fetch

  • vcl_discard函数:缓存内容到期后或者缓存空间不够时自动被调用,该函数有以下几个返回值=》

      keep 表示在内容中保留该缓存

      discard

  • VCL处理流程可以用下面这张图表示

Receive状态:请求处理的入口状态,根据VCL规则判断该请求应该在Pass或Pipe,还是进入Lookup(到本地缓存中查询)

Lookup状态:进入此状态后,会在hash表中查询数据,如果找到则进入Hit状态,否则进入Miss状态

Pass状态:在此状态下会进入后端获取内容,既进入Fetch状态

Fetch状态:从后端获取请求,发送请求,获得数据,并存储到本地

Deliver状态:将获取的数据发送给客户端,然后完成本次请求

  • VCL里面还有些变量,可以用在VCL函数中,用于逻辑处理

请求到达后可以使用的内置公用变量:

req.backend    =>    指定对应的后端主机

server.ip      =>    表示服务器端IP

client.ip      =>    表示客户端IP

req.request    =>    指定请求的类型,如GET POST PUT DELETE

req.url      =>    知道请求的URL

req.proto      =>    表示客户端发起请求的HTTP协议版本

req.http.header  =>    表示请求中的头部信息

req.restarts    =>    表示请求重启的次数,默认最大值为4

  • VCL向后端主机请求时使用的内置变量

beresp.request   =>    指定请求的类型,如GET POST 

beresp.url      =>    指定请求的地址

beresp.proto    =>    指定请求的协议版本号

beresp.http.header =>    对应请求的HTTP头信息

beresp.ttl      =>    表示缓存的周期,单位秒

  • 从cache或后端主机获取内容后可以使用的内置变量

obj.status     =>    表示返回的状态码,如200 404 500 等

obj.cacheable   =>    表示返回的内容是否可以缓存

obj.valid     =>    表示是否是有效的HTTP应答

obj.response   =>    表示应答的状态信息

obj.ttl      =>    表示返回内容的生存周期,单位秒

obj.lastuse    =>    表示上次请求到现在的间隔,单位秒

  • 客户端应答时可以使用的变量

resp.status    =>    表示返回给客户端的状态码

resp.proto    =>    表示返回给客户端的HTTP协议版本

resp.http.header  =>        表示返回给客户端的头信息

resp.response   =>    表示返回给客户端的状态信息

  • Varnish可以检测后端主机的健康状态,在判定后端主机失效时能自动将其从可用后端主机列表中移除,而一旦其重新变得可用还可以自动将其设定为可 用。为了避免误判,Varnish在探测后端主机的健康状态发生转变时(比如某次探测时某后端主机突然成为不可用状态),通常需要连续执行几次探测均为新 状态才将其标记为转换后的状态。

   每个后端服务器当前探测的健康状态探测方法通过.probe进行设定,其结果可由req.backend.healthy变量获取,也可通过varnishlog中的Backend_health查看或varnishadm的debug.health查看。

.probe中的探测指令常用的有:
   (1) .url:探测后端主机健康状态时请求的URL,默认为“/”;
   (2) .request: 探测后端主机健康状态时所请求内容的详细格式,定义后,它会替换.url指定的探测方式;比如:
   .request =
       "GET /.healthtest.html HTTP/1.1"
       "Host: www.myweb.com"
       "Connection: close";
   (3) .window:设定在判定后端主机健康状态时基于最近多少次的探测进行,默认是8;
   (4) .threshold:在.window中指定的次数中,至少有多少次是成功的才判定后端主机正健康运行;默认是3;
   (5) .initial:Varnish启动时对后端主机至少需要多少次的成功探测,默认同.threshold;
   (6) .expected_response:期望后端主机响应的状态码,默认为200;
   (7) .interval:探测请求的发送周期,默认为5秒;
   (8) .timeout:每次探测请求的过期时长,默认为2秒;


  • 设置后台健康状态检查

  • vim /etc/varnish/health_check.vcl

  • probe chk {
        .interval = 5s;     # 每隔1秒钟尝试一次
        .timeout = 3s;    #每次探测请求的过期时长
        .window = 10;   # 最多尝试10次,判断采样的样本
        .threshold = 8;  # 如果采样10次,如果有8次是错误的,就认为是失败的,上线也是一样
     
        .request = 
        "GET /.test.html HTTP/1.1"   #测试根目录下的 .test.html ,这个文件必须存在且能够访问
        "Host: www.myweb.com"
        "Connection: close"
        "Accept-Encoding: foo/bar";
    }

  • 后端服务器地址池配置
    vim /etc/varnish/backends.vcl

  • import directors;
    include "health_check.vcl";

    backend web1 {
        .host = "192.168.1.3";
        .port = "80";
        .probe = chk;
    }

    backend web2 {
        .host = "192.168.1.4";
        .port = "80";
        .probe = chk;
    }

    backend web3 {
        .host = "192.168.1.5";
        .port = "80";
        .probe = chk;
    }
    ## 负载均衡池
    sub vcl_init {
        new web = directors.round_robin();  #以轮询方式调度
        web.add_backend(web1);
        web.add_backend(web2);
        web.add_backend(web3);
    }

  • 缓存规则主配置
    # vim /etc/varnish/default.vcl

  • vcl 4.1;
    import std;
    include "backends.vcl";
    acl purge_ip {   # 定义acl访问控制,只允许以下网段或主机执行purgers操作
        "127.0.0.1";
        "192.168.0.0"/16;
    }
    sub vcl_recv {
        if (req.method == "PURGE"){  # 如果请求方法是PURGE,并且客户端IP在上面定义的网段内的就允许执行PURGE操作,否则生成一个错误页面返回给用户
                if (!client.ip ~ purge_ip) {
                return(synth(405,"Purging not allowed for "+client.ip));
                }
        return(purge);    
        }
        if (req.http.host ~ "^(www.)?myweb.com") {
        set req.http.host = "www.myweb.com";
        set req.backend_hint = web.backend();
        }
    }

    sub vcl_purge{
        return (synth(200,"success"));
    }
    sub vcl_deliver {
        if (obj.hits > 0) {
            set resp.http.X-Cache = "HIT cache" + " " + server.ip;
        }
        else {
            set resp.http.X-Cache = "Miss cache";
        }
    }

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

  • 为了方便测试,将 varnish 默认启动端口 6081 更改为 80

  • 编辑 vim /usr/lib/systemd/system/varnish.service

  • [Unit]
    Description=Varnish Cache, a high-performance HTTP accelerator
    After=network-online.target

    [Service]
    Type=forking
    KillMode=process

    # Maximum number of open files (for ulimit -n)
    LimitNOFILE=131072

    # Locked shared memory - should suffice to lock the shared memory log
    # (varnishd -l argument)
    # Default log size is 80MB vsl + 1M vsm + header -> 82MB
    # unit is bytes
    LimitMEMLOCK=85983232

    # Enable this to avoid "fork failed" on reload.
    TasksMax=infinity

    # Maximum size of the corefile.
    LimitCORE=infinity

    #ExecStart=/usr/sbin/varnishd -a :6081 -f /etc/varnish/default.vcl -s malloc,256m
    ExecStart=/usr/sbin/varnishd -a :80 -f /etc/varnish/default.vcl -s malloc,256m  #修改默认监听端口为 :80
    ExecReload=/usr/sbin/varnishreload

    [Install]
    WantedBy=multi-user.target

  • systemctl daemon-reload

  • systemctl restart varnish

  • 使用 varnishadm 进行后端健康检查
  • backend.list 
    200        
    Backend name   Admin    Probe    Health     Last change
    boot.web1      probe    10/10    healthy    Sat, 28 Mar 2020 08:03:18 GMT
    boot.web2      probe    10/10    healthy    Sat, 28 Mar 2020 08:01:58 GMT
    boot.web3      probe    10/10    healthy    Sat, 28 Mar 2020 08:01:58 GMT
    boot.web       probe    3/3      healthy    Sat, 28 Mar 2020 08:03:18 GMT

  • 编辑 /etc/hosts 添加 192.168.1.2 www.myweb.com
  • curl www.myweb.com
  • X-Cache: HIT cache192.168.1.2

  • 清除缓存  curl -X PURGE www.myweb.com
  • curl www.myweb.com

  • 将其中一台的 web 服务停掉
  • systemctl stop nginx

  • web2 的状态为 sick 不可用
  • 重新启动 web2 的nginx

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值