热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Varnish服务器的安装配置与优化

最近公司做活动推广,流量暴增,后端服务器压力山大,导致用户的请求响应时间延长,客户因此抱怨声音很大。为尽快解决问题,在安排人员不断优化后端代码的同时,考虑在nginx前增加varnish缓存层,只透传部分动态请求过去,直接减少后端服务器的压力。在实
  最近公司做活动推广,流量暴增,后端服务器压力山大,导致用户的请求响应时间延长,客户因此抱怨声音很大。
  为尽快解决问题,在安排人员不断优化后端代码的同时,考虑在nginx前增加varnish缓存层,只透传部分动态请求过去,直接减少后端服务器的压力。
  在实际使用中,真正感受到了varnish服务器强大的威力!在不断的调优缓存命中率后,后端服务器cpu直接从80%降到了20%,再大的并发前端也可以直接消化,后端服务器表示毫无压力。有了这玩意,可以再也不用在后台写定时任务,不断重新生成静态页面了,直接丢缓存里完事!此外,varnish还支持一种叫“神圣模式”,在后端服务器报错返回500的时候,varnish还能继续优先返回过去缓存的内容,为用户屏蔽部分错误,这东东有时真算是救命稻草啊。
  但同时,也趟了n多的坑,varnish中的VCL语言太过强大和灵活,稍微运用不好就会中枪。而网上公开的大多数varnish配置文件都是一大抄,根本无法直接用于生产。在研究了几天,翻阅了大量各种资料后,才总算把遇到的问题都解决了。
  现将调优心得记录如下:
  Varnish是一种专业的网站缓存软件(其实就是带缓存的反向代理服务),它可以把整个HTTP响应内容缓存到内存或文件中,从而提高Web服务器的响应速度。
  Varnish内置强大的VCL(Varnish Configuration Language)配置语言,允许通过各种条件判断来灵活调整缓存策略。在程序启动时,varnish就把VCL转换成二进制代码,因此性能非常高。
  epel源里也有varnish,但是却2.x版本的。
  因为 varnish 3.0的配置文件与 2.x 的存在很大不同,因此varnish团队不能再更新epel里的软件源。如果你想安装最新版本,推荐使用 rpm 方式。
RPM安装   在redhat系服务器上可以很容易的直接通过rpm包安装:
wget http://repo.varnish-cache.org/redhat/varnish-3.0/el6/x86_64/varnish/varnish-libs-3.0.4-1.el6.x86_64.rpm
wget http://repo.varnish-cache.org/redhat/varnish-3.0/el6/x86_64/varnish/varnish-3.0.4-1.el6.x86_64.rpm
wget http://repo.varnish-cache.org/redhat/varnish-3.0/el6/x86_64/varnish/varnish-docs-3.0.4-1.el6.x86_64.rpm
yum localinstall *.rpm

  varnish的安装和配置路径 

/etc/varnish/default.vcl  #默认配置文件存文件
/etc/sysconfig/varnish   #服务启动参数脚本
/etc/init.d/varnish           #服务控制脚本

  可以通过调整 /etc/sysconfig/varnish 配置文件中的参数来调整启动参数,设置线程池、缓存到内存还是文件等。当然如果你乐意也可以在varnishd后面带上启动参数手工启动服务和管理。 

  现在能通过服务的方式启动 varnish了:
service varnish start (stop/restart)
  将varnish设为开机自启动:
chkconfig varnish on
编译安装 安装依赖包
yum install ncurses-devel.x86_64

  此步可选。 

  如果你在编译varnish 后bin目录中没有发现 varnishstat、varnishtop、varnishhist这三个程序的话,就是因为编译前没有安装与操作系统位数对应的 ncurses-devel。这些工具非常好用,因此建议先安装这个依赖包。
安装pcre   varnish 依赖pcre进行url正则匹配。
cd pcre-8.12
./configure --prefix=/usr/local/
make&&make install
编译   解压缩varnish源码包
wget http://repo.varnish-cache.org/source/varnish-3.0.4.tar.gz
cd /root
tar -zxvf varnish-3.0.4.tar.gz
cd varnish-3.0.4
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
./configure --prefix=/usr/local/varnish
make && make install
  先介绍一下Varnish处理请求的主要处理方法和流程
  VCL 需定义几个默认的函数,在Varnish处理HTTP请求的各个阶段会回调这些函数进行处理:

vcl_recv,请求入口,判断是否要进一步处理,还是直接转发给后端(pass)。 此过程中可以使用和请求相关的变量,例如客户端请求的url,ip,user-agent,COOKIE等,此过程中可以把不需缓存的地址,通过判断(相等、不相等、正则匹配等方法)透传给后端,例如POST请求,及jsp、asp、do等扩展名的动态内容;

vcl_fetch,当从后端服务器获取内容后会进入此阶段,除了可以使用客户端的请求变量,还可以使用从后端获取的信息(bersp),如后端返回的头信息,具体指定此信息的缓存时间TTL;

vcl_miss 缓存未命中时中要做的处理

vcl_hit 缓存命中后做的处理

vcl_delever 发送给客户端前的处理

vcl_pass 交给后端服务器

vcl_hash 设置缓存的键值key

  首次请求时过程如下:
  recv->hash->miss->fetch->deliver
  缓存后再次请求:
  recv->hash->hit->deliver(fetch的过程没了,这就是我们要做的,把要缓存的页面保存下来)
  直接交给后端pass的情况:
  recv->hash->pass->fetch->deliver(直接从后端获取数据后发送给客户端,此时Varnish相当于一个中转站,只负责转发)   安装完成后,默认的配置文件位于
  /etc/varnish/default.vcl
  我们可以参考缺省配置项学习vcl语言的使用,并进行不断的调优。

  但直接修改配置,不断的重启调优效率非常低下痛苦!经过不断摸索,我发现其实varnish里内置了日志模块,我们可以在 defalut.vcl 最上边引用std库,以便输出日志:

import std;
  在需要输出日志的地方,使用 std.log 即可:
std.log("LOG_DEBUG: URL=" + req.url);
  这样的话,就可以通过日志了解varnish的工作流程,很方便的优化啦,效率何止提高十倍!

  类似于你想跟踪哪些连接没有命中缓存,可以在vcl_miss函数中这样写:

sub vcl_miss {
     td.log("url miss!!! url=" + req.url);
     return (fetch);
}
  启动varnish 后,通过 varnishlog工具跟踪打印出的日志
varnishlog -I LOG
  Varnish可以挂载多个后端服务器,并进行权重、轮询,将请求转发到后端节点上,以达到避免单点的问题。
  举例如下:
backend web1 {
    .host = "172.16.2.31";
    .port = "80";
    .probe = {
        .url = "/";
        .interval = 10s;
        .timeout = 2s;
        .window = 3;
        .threshold = 3;
    }
}
backend web2 {
    .host = "172.16.2.32";
    .port = "80";
    .probe = {
        .url = "/";
        .interval = 10s;
        .timeout = 2s;
        .window = 3;
        .threshold = 3;
    }
}
# 定义负载均衡
director webgroup random {
    {
       .backend = web1;
       .weight = 1;
    }
    {
       .backend = web2;
       .weight = 1;
    }
}

  其中,在backend 中添加 probe 选项,将可以对后端节点进行健康检查。如果后端节点无法访问,将会自动摘除掉该节点,直到这个节点恢复。 

  需要注意window 和threshold 两个参数。当有后端服务器不可达时,varnish会时不时的报503错误。网上查出的资料都是改线程组什么的,经测试完全无效。后来发现,只要将 window 和threshold 两个参数的值设成一样的,503现象就再没有发生了。 Grace mode   如果后端需要很长时间来生成一个对象,这里有一个线程堆积的风险。为了避免这 种情况,你可以使用 Grace。他可以让 varnish 提供一个存在的版本,然后从后端生成新 的目标版本。当同时有多个请求过来的时候,varnish只发送一个请求到后端服务器,在
  set beresp.grace = 30m; 
  时间内复制旧的请求结果给客户端。
Saint mode   有时候,服务器很古怪,他们发出随机错误,您需要通知 varnish 使用更加优雅的方式处理 它,这种方式叫神圣模式(saint mode)。Saint mode 允许您抛弃一个后端服务器或者另一个尝试的后端服务器或者 cache 中服务陈旧的内容。

例如:

sub vcl_fetch {
    if (beresp.status == 500) {
        set beresp.saintmode = 10s;
        return (restart);
  }
  
    set beresp.grace = 5m;
}
import std;
backend web1 {
    .host = "172.16.2.31";
    .port = "80";
    .probe = {
        .url = "/";
        .interval = 10s;
        .timeout = 2s;
        .window = 3;
        .threshold = 3;
    }
}
backend web2 {
    .host = "172.16.2.32";
    .port = "80";
    .probe = {
        .url = "/";
        .interval = 10s;
        .timeout = 2s;
        .window = 3;
        .threshold = 3;
    }
}
# 定义负载均衡组
director webgroup random {
    {
       .backend = web1;
       .weight = 1;
    }
    {
       .backend = web2;
       .weight = 1;
    }
}
# 允许刷新缓存的ip
acl purgeAllow {
     "localhost";
     "172.16.2.5";
}
sub vcl_recv {
    # 刷新缓存设置
    if (req.request == "PURGE") {
        #判断是否允许ip
        if (!client.ip ~ purgeAllow) {
             error 405 "Not allowed.";
        }
        #去缓存中查找
        return (lookup);
    }
    std.log("LOG_DEBUG: URL=" + req.url);
    set req.backend = webgroup;
    if (req.request != "GET" && req.request != "HEAD" && req.request != "PUT" && req.request != "POST" && req.request != "TRACE" && req.request != "OPTIONS" && req.request != "DELETE") {
         /* Non-RFC2616 or CONNECT which is weird. */
         return (pipe);
    }
    # 只缓存 GET 和 HEAD 请求
    if (req.request != "GET" && req.request != "HEAD") {
        std.log("LOG_DEBUG: req.request not get!  " + req.request );
        return(pass);
    }
    # http 认证的页面也 pass
  if (req.http.Authorization) {
        std.log("LOG_DEBUG: req is authorization !");
        return (pass);
    }
    if (req.http.Cache-Control ~ "no-cache") {
        std.log("LOG_DEBUG: req is no-cache");
        return (pass);
    }
    # 忽略admin、verify、servlet目录,以.jsp和.do结尾以及带有?的URL,直接从后端服务器读取内容
    if (req.url ~ "^/admin" || req.url ~ "^/verify/" || req.url ~ "^/servlet/" || req.url ~ "\.(jsp|php|do)($|\?)") {
        std.log("url is admin or servlet or jsp|php|do, pass.");
        return (pass);
    }
    # 只缓存指定扩展名的请求, 并去除 COOKIE
    if (req.url ~ "^/[^?]+\.(jpeg|jpg|png|gif|bmp|tif|tiff|ico|wmf|js|css|ejs|swf|txt|zip|exe|html|htm)(\?.*|)$") {
        std.log("*** url is jpeg|jpg|png|gif|ico|js|css|txt|zip|exe|html|htm set cached! ***");
        unset req.http.COOKIE;
        # 规范请求头,Accept-Encoding 只保留必要的内容
        if (req.http.Accept-Encoding) {
            if (req.url ~ "\.(jpg|png|gif|jpeg)(\?.*|)$") {
                remove req.http.Accept-Encoding;
            } elsif (req.http.Accept-Encoding ~ "gzip") {
                set req.http.Accept-Encoding = "gzip";
            } elsif (req.http.Accept-Encoding ~ "deflate") {
                set req.http.Accept-Encoding = "deflate";
            } else {
                remove req.http.Accept-Encoding;
            }
        }
        return(lookup);
    } else {
        std.log("url is not cached!");
        return (pass);
    }
}
sub vcl_hit {
    if (req.request == "PURGE") {
        set obj.ttl = 0s;
        error 200 "Purged.";
    }
    return (deliver);
}
sub vcl_miss {
    std.log("################# cache miss ################### url=" + req.url);
    if (req.request == "PURGE") {
        purge;
        error 200 "Purged.";
    }
}
sub vcl_fetch {
    # 如果后端服务器返回错误,则进入 saintmode
    if (beresp.status == 500 || beresp.status == 501 || beresp.status == 502 || beresp.status == 503 || beresp.status == 504) {
        std.log("beresp.status error!!! beresp.status=" + beresp.status);
        set req.http.host = "status";
        set beresp.saintmode = 20s;
        return (restart);
    }
    # 如果后端静止缓存, 则跳过
    if (beresp.http.Pragma ~ "no-cache" || beresp.http.Cache-Control ~ "no-cache" || beresp.http.Cache-Control ~ "private") {
        std.log("not allow cached!   beresp.http.Cache-COntrol=" + beresp.http.Cache-Control);
    return (hit_for_pass);
    }
    if (beresp.ttl <= 0s || beresp.http.Set-COOKIE || beresp.http.Vary == "*") {
        /* Mark as "Hit-For-Pass" for the next 2 minutes */
        set beresp.ttl = 120 s;
        return (hit_for_pass);
    }
    if (req.request == "GET" && req.url ~ "\.(css|js|ejs|html|htm)$") {
        std.log("gzip is enable.");
        set beresp.do_gzip = true;
        set beresp.ttl = 20s;
    }
    if (req.request == "GET" && req.url ~ "^/[^?]+\.(jpeg|jpg|png|gif|bmp|tif|tiff|ico|wmf|js|css|ejs|swf|txt|zip|exe)(\?.*|)$") {
        std.log("url css|js|gif|jpg|jpeg|bmp|png|tiff|tif|ico|swf|exe|zip|bmp|wmf is cache 5m!");
        set beresp.ttl = 5m;
    } elseif (req.request == "GET" && req.url ~ "\.(html|htm)$") {
        set beresp.ttl = 30s;
    } else {
        return (hit_for_pass);
    }
    # 如果后端不健康,则先返回缓存数据1分钟
    if (!req.backend.healthy) {
        std.log("eq.backend not healthy! req.grace = 1m");
        set req.grace = 1m;
    } else {
        set req.grace = 30s;
    }
     return (deliver);
}
# 发送给客户端
sub vcl_deliver {
    if ( obj.hits > 0 ) {
    set resp.http.X-Cache = "has cache";
    } else {
    #set resp.http.X-Cache = "no cache";
    }
    return (deliver);
}
  跟随varnish会一起安装一些方便的调试工具,用好这些工具,对你更好的应用varnish有很大的帮助。
varnishncsa(以 NCSA 的格式显示日志)    通过这个命令,可以像类似于 nginx/apache一样的显示出用户的访问日志来。 varnishlog(varnish详细日志)   如果你想跟踪varnish处理每个请求时的详细处理情况,可以使用此命令。
  直接使用这个命令,显示的内容非常多,通常我们可以通过一些参数,使它只显示我们关心的内容。

-b    \\只显示varnish和backend server之间的日志,当您想要优化命中率的时 候可以使用这个参数。  

-c    \\和-b差不多,不过它代表的是 varnish和 client端的通信。  

-i tag  \\只显示某个 tag,比如“varnishlog ?i SessionOpen”将只显示新会话,注意,这个地方的tag名字是不区分大小写的。  

-I    \\通过正则表达式过滤数据,比如“varnishlog -c -i RxHeader -I COOKIE”将显示所有接到到来自客户端的包含 COOKIE 单词的头信息。  

-o    \\聚合日志请求 ID  

  例如:
  varnishlog -c -o /auth/login  这个命令将告诉您来自客户端(-c)的所有包含”/auth/login” 字段(-o)请求。
  varnishlog -c -o ReqStart 192.168.1.100  只跟踪一个单独的client请求  varnishtop      您可以使用varnishtop 确定哪些URL经常被透传到后端。    
    适当的过滤使用  ?I,-i,-X  和-x 选项,它可以按照您的要求显示请求的内容,客
户端,浏览器等其他日志里的信息。 
    varnishtop -i rxurl    \\您可以看到客户端请求的 url次数。 
    Varnishtop -i txurl    \\您可以看到请求后端服务器的url次数。 
    Varnishtop -i Rxheader -I Accept-Encoding \\可以看见接收到的头信息中有有多少次
包含Accept-Encoding。  varnishstat    显示一个运行varnishd实例的相关统计数据。 
  Varnish 包含很多计数器,请求丢失率,命中率,存储信息,创建线程,删除对象等,几乎所有的操作。通过跟踪这些计数器,可以很好的了解varnish运行状态。   varnishadm    通过命令行,控制varnish服务器。可以动态的删除缓存,重新加载配置文件等。

管理端口有两种链接方式:
  1,telnet方式,可以通过telnet来连接管理端口.如:"telnet localhost 6082"
  2,varnishadm方式,可以通过varnish自带的管理程序传递命令.如: varnishadm -n vcache -T localhost:6082 help

动态清除缓存
  varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082 ban.url /2011111.png
  其中:ban.url 后的路径一定不要带abc.xxx.com域名之类的,否则缓存清除不了。

清除包含某个子目录的URL地址:
  /usr/local/varnish/bin/varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082 url.purge /a/

不重启加载配置文件
  登陆到管理界面
  /usr/local/varnish/bin/varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082 
  加载配置文件
  vcl.load new.vcl /etc/varnish/default.vcl
  编译出错的话会有提示,成功会返回200
  加载新配置文件
  vcl.use new.vcl
  此时新的配置文件已经生效!


推荐阅读
  • 本文将详细介绍如何在ThinkPHP6框架中实现多数据库的部署,包括读写分离的策略,以及如何通过负载均衡和MySQL同步技术优化数据库性能。 ... [详细]
  • 免费获取:全面更新的Linux集群视频教程及配套资源
    本资源包含最新的Linux集群视频教程、详细的教学资料、实用的学习课件、完整的源代码及多种软件开发工具。百度网盘链接:https://pan.baidu.com/s/1roYoSM0jHqa3PrCfaaaqUQ,提取码:41py。关注我们的公众号,获取更多更新的技术教程。 ... [详细]
  • 本文详细介绍了如何检查和配置电脑上的PHP环境,包括位数、运行支持以及文件格式的打开方式。适合初学者了解PHP的基础知识和操作方法。 ... [详细]
  • 本文深入探讨了 Exchange Server 2010 中客户端访问的代理和重定向机制,特别是在跨站点环境中如何配置这些功能以确保用户能够顺利访问邮箱服务。通过详细解析不同场景下的应用,帮助管理员更好地理解和实施相关设置。 ... [详细]
  • 本文介绍了如何通过在数据库表中增加一个字段来记录文章的访问次数,并提供了一个示例方法用于更新该字段值。 ... [详细]
  • Spring Cloud因其强大的功能和灵活性,被誉为开发分布式系统的‘一站式’解决方案。它不仅简化了分布式系统中的常见模式实现,还被广泛应用于企业级生产环境中。本书内容详实,覆盖了从微服务基础到Spring Cloud的高级应用,适合各层次的开发者。 ... [详细]
  • 本文详细介绍了在 Windows 7 系统中配置 Nginx 1.10.3 和 PHP 7.1.1 NTS 的步骤,包括修改 PHP 配置文件、处理依赖项以及创建批处理脚本启动和停止服务。重点解释了如何解决常见的运行时错误。 ... [详细]
  • 本文详细介绍了一种通过MySQL弱口令漏洞在Windows操作系统上获取SYSTEM权限的方法。该方法涉及使用自定义UDF DLL文件来执行任意命令,从而实现对远程服务器的完全控制。 ... [详细]
  • 在尝试使用C# Windows Forms客户端通过SignalR连接到ASP.NET服务器时,遇到了内部服务器错误(500)。本文将详细探讨问题的原因及解决方案。 ... [详细]
  • 深入解析Spring Cloud微服务架构与分布式系统实战
    本文详细介绍了Spring Cloud在微服务架构和分布式系统中的应用,结合实际案例和最新技术,帮助读者全面掌握微服务的实现与优化。 ... [详细]
  • 使用LVS与ldirectord实现高可用负载均衡
    本文介绍了如何通过LVS(Linux Virtual Server)结合ldirectord工具来实现服务器的健康检查及负载均衡功能。环境设置包括一个LVS节点和两个真实服务器节点,通过配置ldirectord进行健康状态监测,确保系统的高可用性。 ... [详细]
  • 全能终端工具推荐:高效、免费、易用
    介绍一款备受好评的全能型终端工具——MobaXterm,它不仅功能强大,而且完全免费,适合各类用户使用。 ... [详细]
  • 探讨GET与POST请求数据传输的最大容量
    在Web开发领域,GET和POST是最常见的两种数据传输方法。本文将深入探讨这两种请求方式在不同环境下的数据传输能力及其限制。 ... [详细]
  • 随着毕业设计的结束,我终于有时间更新我的博客了。这次,我将分享如何在自己的服务器上搭建 Bitwarden,一个广受好评的开源密码管理工具。 ... [详细]
  • 本文探讨了在支付项目开发中使用SS5 Socket Server实现内部网络访问外部网络的技术方案。详细介绍了SS5的安装、配置及性能测试过程,旨在为面临相同需求的技术人员提供参考。 ... [详细]
author-avatar
回忆的沙漏2502890423
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有