varnish缓存代理配置

一、varnish 原理:

1)Varnish 简介:

varnish 缓存是 web 应用加速器,同时也作为 http 反向缓存代理。你可以安装 varnish 在任何 http 的前端,同时配置它缓存内容。与传统的 squid 相比,varnish 具有性能更高、速度更 快、管理更加方便等诸多优点。有一部分企业已经在生产环境中使用其作为旧版本的 squid 的替代方案,以在相同的服务器成本下提供更好的缓存效果,Varnish 更是作为 CDN 缓存服 务器的可选服务之一。

根据官网的介绍,Varnish 的主要特性如下:https://www.varnish-cache.org/

1.缓存位置:可以使用内存也可以使用磁盘。如果要使用磁盘的话推荐 SSD 做 RAID1

2.日志存储:日志也存储在内存中。存储策略:固定大小,循环使用

3.支持虚拟内存的使用。

4.有精确的时间管理机制,即缓存的时间属性控制。

5.状态引擎架构:在不同的引擎上完成对不同的缓存和代理数据进行处理。可以通过特定的 配置语言设计不同的控制语句,以决定数据在不同位置以不同方式缓存,在特定的地方对经 过的报文进行特定规则的处理。

6.缓存管理:以二叉堆格式管理缓存数据,做到数据的及时清理。

2)Varnish 与 Squid 的对比

相同点:

都是一个反向代理服务器;

都是开源软件;

Varnish 的优势:

1、Varnish 的稳定性很高,两者在完成相同负荷的工作时,Squid 服务器发生故障的几率要 高于 Varnish,因为使用 Squid 要经常重启;

2、Varnish 访问速度更快,因为采用了“Visual Page Cache”技术,所有缓存数据都直接从内存 读取,而 squid 是从硬盘读取,因而 Varnish 在访问速度方面会更快;

3、Varnish 可以支持更多的并发连接,因为 Varnish 的 TCP 连接释放要比 Squid 快,因而在 高并发连接情况下可以支持更多 TCP 连接;

4、Varnish 可以通过管理端口,使用正则表达式批量的清除部分缓存,而 Squid 是做不到的; squid 属于是单进程使用单核 CPU,但 Varnish 是通过 fork 形式打开多进程来做处理,所以可 以合理的使用所有核来处理相应的请求;

Varnish 的劣势:

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

Varnish 劣势的解决方案:

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

针对劣势二:可以在负载均衡上做 url 哈希,让单个 url 请求固定请求到一台 varnish 服务器 上;

3)使用 varnish 作为 web 代理缓存的原理 :

varnish 是一个 http 反向代理的缓存。它从客户端接收请求然后尝试从缓存中获取数据来响 应客户端的请求,如果 varnish 不能从缓存中获得数据来响应客户端,它将转发请求到后端 (backend servers),获取响应同时存储,最后交付给客户端。 如果 varnish 已经缓存了某个响应,它比你传统的后端服务器的响应要快很多,所以你需要 尽可能是更多的请求直接从 varnish 的缓存中获取响应。 varnish 决定是缓存内容或者是从后端服务器获取响应。后端服务器能通过 http 响应头中的 Cache-Control 来同步 varnish 缓存内容。在某些条件下 varnish 将不缓存内容,最常见的是使 用 cookie。当一个被标记有 cookie 的客户端 web 请求,varnish 默认是不缓存。这些众多的 varnish 功能特点都是可以通过写 vcl 来改变的。

5)简单架构:

Varnish 分为 management 进程和 child 进程;

Management 进程:对子进程进行管理,同时对 VCL 配置进行编译,并应用到不同的状态引 擎。

Child 进程:生成线程池,负责对用户请求进行处理,并通过 hash 查找返回用户结果。

6)varnish 主要配置部分:

varnish 配置主要分为:后端配置,ACL 配置,probes 配置,directors 配置,核心子程序配置 几大块。

其中后端配置是必要的,在多台服务器中还会用到 directors 配置,核心子程序配 置。

后端配置:即给 varnish 添加反代服务器节点,最少配置一个。

ACL 配置:即给 varnish 添加访问控制列表,可以指定这些列表访问或禁止访问。

probes 配置:即给 varnish 添加探测后端服务器是否正常的规则,方便切换或禁止对应后端 服务器。

directors 配置:即给 varnish 添加负载均衡模式管理多个后端服务器。

核心子程序配置:即给 varnish 添加后端服务器切换,请求缓存,访问控制,错误处理等规 则。

7)VCL 中内置预设变量:变量(也叫 object):

req:The request object,请求到达时可用的变量(客户端发送的请求对象)

bereq:The backend request object,向后端主机请求时可用的变量

beresp:The backend response object,从后端主机获取内容时可用的变量(后端响应请求对象)

resp:The HTTP response object,对客户端响应时可用的变量(返回给客户端的响应对象) obj:存储在内存中时对象属性相关的可用的变量(高速缓存对象,缓存后端响应请求内容)

预设变量是系统固定的,请求进入对应的 vcl 子程序后便生成,这些变量可以方便子程序提 取,当然也可以自定义一些全局变量。

当前时间:

now :作用:返回当前时间戳。

服务器:(服务器基本信息)

注:原 server.port 已经弃用,如果要取服务器端口号使用 std.port(server.ip),需要 import std; 才可以使用 std

server.hostname:服务器主机名。

server.identity:服务器身份标识。

server.ip:返回服务器端 IP 地址。

varnish 子程序调用流程图,通过大部分子程序的 return 返回值进入下一步行动:

11)优雅模式(Garce mode)

Varnish 中的请求合并 当几个客户端请求同一个页面的时候,varnish 只发送一个请求到后端服务器,然后让其他 几个请求挂起并等待返回结果;获得结果后,其它请求再复制后端的结果发送给客户端; 但如果同时有数以千计的请求,那么这个等待队列将变得庞大,这将导致 2 类潜在问题: 惊群问题(thundering herd problem),即突然释放大量的线程去复制后端返回的结果,将导致 负载急速上升;没有用户喜欢等待; 故为了解决这类问题,可以配置 varnish 在缓存对象因超时失效后再保留一段时间,以给那 些等待的请求返回过去的文件内容(stale content),配置案例如下:

二、安装 varnish

1、安装依赖关系的软件包(注:使用 centos 在线 yum 源)

[root@varnish ~]# yum -y install autoconf automake libedit-devel libtool ncurses-devel pcre-devel pkgconfig python-docutils python-sphinx

2、安装 varnish Varnish 的官方网址为 http://varnish-cache.org,可以在这里下载最新版本的软件。

下载地址:https://www.varnish-cache.org/content/varnish-cache-403

解压,进入解压目录编译安装:

[root@varnish ~]# tar zxf varnish-4.0.3.tar.gz

[root@varnish ~]# cd varnish-4.0.3/

[root@varnish varnish-4.0.3]# ./configure

:不指定安装路径,默认是安装在/usr/local 目录下

编译、安装

[root@varnish varnish-4.0.3]# make && make install

复制 vcl 文件(在编译安装目录下),如果安装目录里没有 default.vcl 文件。

复制到安装目录的/usr/local/var/varnish/目录下(当然并无必需要求在哪个目录,因为正式 启动时还得指定这个文件的目录)

[root@varnish varnish-4.0.3]# cp etc/example.vcl /usr/local/var/varnish/default.vcl

编写配置文件 把原来的配置cp下来

#使用 varnish 版本 4 的格式.
vcl 4.0;
#加载后端负载均衡模块
import directors;
#加载 std 模块
import std;
#创建名为 backend_healthcheck 的健康检查策略
probe backend_healthcheck {
  .url="/";
  .interval = 5s;
  .timeout = 1s;
  .window = 5;
  .threshold = 3;
}
#定义后端服务器
backend web_app_01 {
  .host = "192.168.239.134";   (定义第一台webip)
  .port = "80";
  .first_byte_timeout = 9s;
  .connect_timeout = 3s;
  .between_bytes_timeout = 1s;
  .probe = backend_healthcheck;
}
backend web_app_02 {
  .host = "192.168.239.141";    (定义第二台webip)
  .port = "80";
  .first_byte_timeout = 9s;
  .connect_timeout = 3s;
  .between_bytes_timeout = 1s;
  .probe = backend_healthcheck;
}
#定义允许清理缓存的 IP
acl purgers {
  "127.0.0.1";
  "localhost";
  "192.168.239.0/24"; (本网段)
}
#vcl_init 初始化子程序创建后端主机组
sub vcl_init {
  new web = directors.round_robin();
  web.add_backend(web_app_01);
  web.add_backend(web_app_02);
}
#请求入口,用于接收和处理请求。这里一般用作路由处理,判断是否读取缓存和指定该请
求使用哪个后端
sub vcl_recv {
  #将请求指定使用 web 后端集群 .在集群名后加上 .backend()
  set req.backend_hint = web.backend();
  # 匹配清理缓存的请求
  if (req.method == "PURGE") {
    if (!client.ip ~ purgers) {
      return (synth(405, "Not Allowed."));
    }
    # 是的话就执行清理
    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);
}
# 如果不是 GET 和 HEAD 就跳到 pass
if (req.method != "GET" && req.method != "HEAD") {
  return (pass);
}
#如果匹配动态内容访问请求就跳到 pass
if (req.url ~ ".(php|asp|aspx|jsp|do|ashx|shtml)($|?)") {
  return (pass);
}
#具有身份验证的请求跳到 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.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);
}
# 把真实客户端 IP 传递给后端服务器 后端服务器日志使用 X-Forwarded-For 来接收
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;
  }
}
  return (hash);
}
# hash 事件(缓存事件)
sub vcl_hash {
  hash_data(req.url);
  if (req.http.host) {
    hash_data(req.http.host);
  } else {
    hash_data(server.ip);
  }
  return (lookup);
}
# 缓存命中事件
sub vcl_hit {
  if (req.method == "PURGE") {
    return (synth(200, "Purged."));
  }
  return (deliver);
}
# 缓存不命中事件
sub vcl_miss {
  if (req.method == "PURGE") {
    return (synth(404, "Purged."));
  }
  return (fetch);
}
# 返回给用户的前一个事件 通常用于添加或删除 header 头
sub vcl_deliver {
  if (obj.hits > 0) {
    set resp.http.X-Cache = "HIT";
    set resp.http.X-Cache-Hits = obj.hits;
  } else {
    set resp.http.X-Cache = "MISS";
  }
  #取消显示 php 框架版本的 header 头
  unset resp.http.X-Powered-By;
  #取消显示 web 软件版本、Via(来自 varnish)等 header 头 为了安全
  unset resp.http.Server;
  unset resp.http.X-Drupal-Cache;
  unset resp.http.Via;
  unset resp.http.Link;
  unset resp.http.X-Varnish;
  #显示请求经历 restarts 事件的次数
  set resp.http.xx_restarts_count = req.restarts;
  #显示该资源缓存的时间单位秒
  set resp.http.xx_Age = resp.http.Age;
  #显示该资源命中的次数
  set resp.http.hit_count = obj.hits;
  #取消显示 Age 为了不和 CDN 冲突
  unset resp.http.Age;
  #返回给用户
  return (deliver);
}
# pass 事件
sub vcl_pass {
  return (fetch);
}
#处理对后端返回结果的事件(设置缓存、移除 cookie 信息、设置 header 头等) 在 fetch 事件后自动调用
sub vcl_backend_response {
  #开启 grace 模式 表示当后端全挂掉后 即使缓存资源已过期(超过缓存时间) 也会把该
资源返回给用户 资源最大有效时间为 5 分钟
  set beresp.grace = 5m;
  #后端返回如下错误状态码 则不缓存
  if (beresp.status == 499 || beresp.status == 404 || beresp.status == 502) {
  set beresp.uncacheable = true;
  }
  #如请求 php 或 jsp 则不缓存
  if (bereq.url ~ ".(php|jsp)(?|$)") {
    set beresp.uncacheable = true;
  } else { //自定义缓存文件的缓存时长,即 TTL 值
    if (bereq.url ~ ".(css|js|html|htm|bmp|png|gif|jpg|jpeg|ico)($|?)") {
      set beresp.ttl = 15m;
      unset beresp.http.Set-Cookie;
    } elseif (bereq.url ~ ".(gz|tgz|bz2|tbz|zip|rar|mp3|mp4|ogg|swf|flv)($|?)") {
      set beresp.ttl = 30m;
      unset beresp.http.Set-Cookie;
    } else {
      set beresp.ttl = 10m;
      unset beresp.http.Set-Cookie;
    }
  }
#返回给用户
  return (deliver);
}
sub vcl_purge {
  return (synth(200,"success"));
}
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);
}

其他两台web安装nginx:

启动命令

会报错往里追加uuid就行

原文地址:https://www.cnblogs.com/ljl1366136/p/9547023.html