国外设计欣赏网站 - DOOOOR.com

 找回密码
 注册

QQ登录

只需一步,快速开始

扫一扫,微信登陆

搜索

[Drupal优化/SEO/环境] Drupal高级性能优化:高负载高性能网站之Varnish 全篇详解

[复制链接]
发表于 4-20-2012 21:22 | 显示全部楼层 |阅读模式

1.

Varnish作为一款优秀的反向代理服务器以及缓存服务器,已经越来越流行,本文就Varnish的基本使用以及如何与Drupal合作使用,做一个简要清单。

安装
推荐使用系统自带的源安装,比如apt或者yum。

结构
/etc/varnish/ 存放varnish VCL配置文件
/etc/sysconfig/varnish 【CentOS】 存放varnish服务器运行的参数
/etc/default/varnish 【Ubuntu】 存放varnish服务器运行的参数
/usr/sbin/varnishd varnish服务器执行文件
/etc/init.d/varnish 运行程序

相关命令
这里列举一下比较有用的几个varnish管理命令

varnishadm 管理Varnish后端的工具 telnet也可以(下面详细介绍)
varnishhist 查看Varnish命中的工具 运行可以看到一张柱状描绘图,|表示缓存命中,#表示未命中,横向代表时间。 【非常有用】
varnishlog 实时显示varnish的请求日志
varnishncsa 以Apache标准的格式combined输出日志
varnishstat 查看状态、参数等,具体查阅百度。【非常有用】
varnishtop 类似top工具,查看varnish相关进程的资源、运行等状况。

varnishncsa 将Varnish的log以Apache的格式输出,varnishlog以原始方式显示Varnish的日志。我们知道varnish默认会把日志存放在内存中,如果我们要把日志存放起来,就需要你启动一个守护进程,把内存中的日志存放到文件中。

Varnish — (http)—> Backend servers
|
|–> (Daemon) varnishncsa/varnishlog === (write) ==> Log Files

管理varnish以及清除内存等操作虽然可以使用varnishadm,但是这里推荐使用telnet,一个交互的管理界面。

比如: telnet 127.0.0.1 6082
之后,输入help会显示所有可用命令。

help [command]
% ]$ _) R. `% P) P% I6 Jping [timestamp]
& `6 v7 Q2 t- g# V, jstatus, Q" m& b. i9 p9 Q
start3 J3 }* R$ I* w$ I
stop' h! t7 @" M. M: C
stats
- W# {1 M  i( @2 g) D& L6 pvcl.load
" S4 P: r2 E" ^9 S' ovcl.inline) p+ U7 i5 q& @- l) N8 U* F
vcl.use; C9 W! w$ k# l0 I. E0 y
vcl.discard- e, ~2 O( U% z2 Y' c& n  Q! g7 E2 r
vcl.list  {8 T$ Q! z. u
vcl.show# d4 o+ i' S4 m8 `5 T5 F
param.show [-l] []
* r8 i* b) S* \! r8 Iparam.set
! U: y4 }# Y9 z8 u. yquit; s* @) U3 X( T4 F
purge.url
6 F/ F( D& _3 t/ k( o/ x% G3 ]purge.hash7 S% M* ]* q. _( z6 z+ C
purge    [&&   ]...
# x+ {! p6 x% Z7 gpurge.list

重新加载Varnish配置文件

telnet 127.0.0.1 6082
1 S7 u( |$ o) c( e1 zvcl.load newconfig /data/app/varnish/etc/varnish/default.vcl) M8 a1 \& a' o! {, B0 o
vcl.use newconfig

注意:varnish 的CLI可能需要认证,最简单的办法就是在varnish启动的时候取掉相应的参数。

-S /etc/varnish/secret \

参考 https://www.varnish-cache.org/docs/trunk/reference/varnish-cli.html

 

Varnish的缓存方式
Malloc (malloc) 通过 malloc 获取内存,简单,速度快
Mmap file (file) 创建文件缓存
这个是varnish缓存的两种方式,可以在启动的时候通过参数指定。

Varnish处理流程

首次请求时过程如下:
recv->hash->miss->fetch->deliver
缓存后再次请求:
recv->hash->hit->deliver(fetch的过程没了,这就是我们要做的,把要缓存的页面保存下来)
直接交给后端pass的情况:
recv->hash->pass->fetch->deliver(直接从后端获取数据后发送给客户端,此时Varnish相当于一个中转站,只负责转发)

VCL以及基本对象

request 从客户端进来
responses 从后端服务器过来
object 存储在cache中

VCL语言
req 请求目标,当varnish接收到一个请求,这时req object就被创建了,你在vcl_recv中的大部分工作,都是在req object上展开的。
beresp 后端服务器返回的目标,它包含返回的头信息,你在vcl_fetch中的大部分工作都是在beresp object上开展的。
obj 被cache的目标,只读的目标被保存于内存中,obj.ttl的值可修改,其他的只能读。

VCL支持一下运算符
= 赋值运算符
== 对比
~ 匹配,在ACL中和正则表达式中都可以用
! 否定
&& 逻辑与
|| 逻辑或

Grace mode

如果后端需要很长时间来生成一个对象,这里有一个线程堆积的风险。为了避免这 种情况,你可以使用 Grace。他可以让 varnish 提供一个存在的版本,然后从后端生成新 的目标版本。
当同时有多个请求过来的时候,varnish只发送一个请求到后端服务器,在“set beresp.grace = 30m; ”时间内复制旧的请求结果给客户端。

Saint mode
有时候,服务器很古怪,他们发出随机错误,您需要通知 varnish 使用更加优雅的方式处理 它,这种方式叫神圣模式(saint mode)。Saint mode 允许您抛弃一个后端服务器或者另一 个尝试的后端服务器或者 cache 中服务陈旧的内容。

sub vcl_fetch {
) j# ?4 l4 d/ [+ b if (beresp.status == 500) {
/ i2 d* K/ e2 L9 g& Z  set beresp.saintmode = 10s;
9 x# _. q+ a" ^7 k  P  restart;! v5 y, f. P" s+ b( f
 }& k/ |1 `  W$ j& u  t5 b. g
 set beresp.grace = 5m;
, A" O9 p  g$ a$ G6 c" P) E. [}

Varnish代理

########
1 w2 J  |$ ~6 {/ z, z5 v& v% Fbackend default {
$ u! H% Y, }) g! R" }4 J5 x3 g% z$ I  .host = "192.168.0.12";$ }3 k$ }6 e0 D+ {5 J
  .port = "8080";  j! s; B- I, V8 r; }
}
1 K. ?% h0 w5 X#现在添加一个新的backend服务器, h$ N4 M$ R- w' p% @2 E
backend test {
2 D1 c6 m. b- [7 V  .host = "192.168.0.12";9 M/ Y8 }) t3 w' J
  .port = "8000";
  f" U8 Q4 v( A2 m# `4 [}
8 c+ T; \2 Z9 U. g* ]: y2 I#要定义特殊的url被发送到哪里( M" T( Z0 `7 t6 R1 E' T- p: y
sub vcl_recv {
& N  t2 V! `* u  w* j7 @. A0 i  if (req.url ~ "^/abcd/") {6 f9 |+ U8 n! J" ~
    set req.backend = test;
( _( s# d/ |" w" x  } else {
! c6 n+ J: V" m7 ~    set req.backend = default;
; k; g- _$ e) m' m4 U# f6 L7 [  }
7 L, J2 I( O' Q& e( [$ ?9 v' U}

Varnish负载均衡
可以把多台 backends 聚合成一个组,这些组被叫做directors。这样可以增强性能和弹力。您可以定义多个backends和多个group在同一个directors。

backend server1 {
" S  G# R0 J% h& w0 v+ D4 {: q+ N    .host = "192.168.0.1" ;
% l+ a. \& m* u6 L, F9 G- z2 B- k    .port = "8080" ;
4 B1 J8 W1 B3 H$ ?. |}
6 R. o; w! j' l8 {4 bbackend server2 {# C1 x1 P, r+ H+ ^
    .host = "192.168.0.2" ;1 B7 o  N& A' @; R# _4 y$ K
    .port = "8080" ;
, I5 A! B. X3 }' g}
6 f& F, E+ w& F  [+ X9 |5 wdirector drupal001 round-robin {- [/ A1 l6 c- h( v' S
    { .backend = server1; }( V- ]" q7 q- P9 Y& V3 o  D6 @
    { .backend = server2; }% ~$ k1 D) W3 @# V
}
* T1 A1 g+ q  t/ y, psub vcl_recv {
' w/ b; K3 O- E( p' U& s1 c   if (req.http.host !~ "www\.drupal001\.com$"){
3 |6 N& j4 A/ q( q! b2 N      error 404 "Unknown HostName!";
2 k# g. M) Y" Y8 e/ E  d9 _    }
& |* j% |. |6 K7 S2 q8 t" q4 }    set req.backend = drupal001;+ u; O  y" c5 Q: @  R! v
 }

健康检查
在之前的两个后端服务器上加上健康检查。

backend server1 {
# B: P- K. l$ y1 R    .host = "192.168.0.1" ;
5 {7 c. |, ~: x4 z7 x; w    .port = "8000" ;
* u, ^* s4 ]: ^, {# `( v% b    .probe = {  }# c+ o& f' R. c- k( _
      .url = "/";       #哪个 url需要varnish请求。0 ^$ _) T+ b3 c8 D6 I. N
      .interval = 5s;   #检查的间隔时间。& P* ]+ U; l( c/ _/ s' R
      .timeout = 1 s;   #等待多长时间探针超时。
4 {, M3 D1 a  s; R' U. }0 {/ r      .window = 5;      #维持5个sliding window的结果。
- I4 \* J, o8 T" w% X7 w3 P" b      .threshold = 3;   #至少有三次window是成功的,就宣告bachend健康。
2 z3 ?- d- b* X. u, F  [/ k' \     }
& q/ l, a4 S$ d1 N4 t}

注意事项:
1. Varnish的不同版本的配置不同,必须查阅官方文档。
比如,在2.x 直接用ESI表示启用ESI,在3.x就不是了。
2. Varnish 2.x有Cookie的问题,当Cookie过大,会产生503错误,升级varnish或者减少set_cookie的操作。
3. 启动varnishd的时候如果选用-s file方式,每次会重新建立缓存文件,而原来的文件不会删除,因此注意清除这些遗留文件。

注释完整的一段VCL文件参考

#设置后端服务器地址
9 ^- u/ V- Y! v" nbackend default {
- W4 T) G# p' m6 q     .host = "127.0.0.1";
/ O( C5 d  g; Q: j     .port = "80";' |: d! c* k6 b$ g8 i! p
}
% l+ ^% i0 E/ Q( S% |  k; y) { 2 f  c. B9 I3 G% K1 b2 |) u  ^
#允许刷新缓存的ip
3 t, F3 l$ ?" A( z# h- q* Macl purgeAllow {
6 {! n- a9 L; Y! B     "localhost";: H1 _* @4 J' D6 j
     "192.168.56.1";; ~: \1 L5 E' M9 G+ X
}2 N# v  j% R/ h/ r8 f; Y
 
; y, P3 S( P5 Ysub vcl_recv {7 ^  [( m% o7 ^) t' @* C' l
     #刷新缓存设置* [3 D0 ?, y( U' N. y# ]4 g. ^
     if (req.request == "PURGE") {
" ?4 _" _# d; F- t! O          #判断是否允许ip  `7 ~- w5 I7 m$ H  Y: T
          if (!client.ip ~ purgeAllow) {! z4 s9 a7 M9 N
               error 405 "Not allowed.";
0 G8 @2 r* D& |* {1 @  R; l          }
( P! R  v7 r" s& `/ U, F4 O          #去缓存中查找1 s  B& a  |' t8 \' E3 X
          return (lookup);; [, ?. K$ I0 x
     }
7 B. l/ D- K% ~$ s+ D- w. Z/ h : s8 M, M( |6 R7 f3 q0 u% p) r& ?
     #首次访问增加X-Forwarded-For头信息,方便后端程序
4 C- i* L5 p" U" L1 {     #获取客户端ip
6 d7 n0 C! H& n. X3 `     if (req.restarts == 0) {- W. p# e5 L$ s8 R7 V, ]$ n
          #如果设置过此header则要再次附加上,用,隔开,如果
* }! c) F/ p2 c' i# V          #只有一层代理的话,就无需设置了
. u8 K- t. U% w9 h  h, Y3 C          if (req.http.x-forwarded-for) {% x2 b. h1 R7 C
               set req.http.X-Forwarded-For =* X" N$ m9 h$ C
                    req.http.X-Forwarded-For ", " client.ip;
* d5 E9 ]+ \+ T- t          }  H- i. G  z  q& t7 L3 e
          #没有则要加上9 y+ s0 W$ }1 E7 g7 i( v+ Z
          else {
1 T* X" X5 E2 `               set req.http.X-Forwarded-For = client.ip;
; h7 P( w/ K4 o2 {" Z  C          }# T1 `( {+ H( i+ ?8 q4 m
     }( @6 q2 s+ ?# ^
 
6 r* l# m! N# F( I5 W) G     #修正客户端的Accept-Encoding头信息,默认选用gzip方式: o' t& j( q. I7 e
     #防止个别浏览器发送类似 deflate, gzip
; @( _2 P* I2 R) {  h3 M2 ^     #当然后端如果只支持gzip的话.可以只设置gizp
& h! p3 y, ^+ e- l, o     if (req.http.Accept-Encoding) {1 z6 U: V9 ^$ R. l' ]& Q) E
          if (req.http.Accept-Encoding ~ "gzip") {
; j0 i! l6 p  M. @, w: P! O, W9 F5 [" F               set req.http.Accept-Encoding = "gzip";* u' _0 \/ g2 u+ S4 q/ ?
          }: b- S  F8 G+ D
          #如果后端支持deflate方式,建议去掉,否则刷新缓存时
2 A* \4 j; M: A) k          #需特殊处理
- s! V2 m2 g; D: D$ t: A$ e$ n. F          elsif (req.http.Accept-Encoding ~ "deflate") {
- _  Q% q$ |0 d. N6 A               set req.http.Accept-Encoding = "deflate";; ^9 c/ a" t$ y3 r
          }
/ i( v6 `2 y. w, d! @+ D          #其他的压缩方式忽略
/ W: N$ g5 l) S+ [: S          else {# Q  ^4 h! U3 e7 W1 U, C
               remove req.http.Accept-Encoding;
) K% C( y  A& }: }) m          }
) J! r! \+ ]9 s1 y1 c  N$ m     }
* U' ]& t  m% H6 v ' p/ F+ K9 r' ^4 s
     #静态文件和明确以.php结尾的url无需缓存, 建议把静态文件分离2 O0 Y+ N+ @$ w! S- E* b0 @
     #至单独服务器下,减少动态应用服务器压力,同时降低arnishd的压力( P5 y# e4 I- L' d
     if(req.url ~ "\.(png|gif|jpg|css|js|php)$"){
+ g( M2 N$ e0 u" L- \# X          return (pass);$ O4 s1 _# T- m7 q, U( N3 Z
     }; x! Q( T+ B( _' b
 
% z, G0 r& s: t, D% [6 \! j4 a     #非正规的请求直接转发给后端服务器& l: m) C- @6 E9 X( o* Z! U. @
     if (req.request != "GET" &&% U) E% Z! X, p
          req.request != "HEAD" &&
" C3 _/ i, p. w6 W2 E# Z0 a          req.request != "PUT" &&( t: `7 O( M" n* w
          req.request != "POST" &&' g4 |% P4 I1 w  u$ E/ W
          req.request != "TRACE" &&
* R* T9 O" M* e          req.request != "OPTIONS" &&7 n8 c2 [5 n! c( I8 \5 H
          req.request != "DELETE") {0 r% T+ Z3 g- @2 s7 L
          /* Non-RFC2616 or CONNECT which is weird. */
" w  n5 ]. p8 u8 g" c2 s- Z          return (pipe);
5 C( a- }2 S; Y# d* d     }
( c8 T, ^5 @( M8 G/ `3 a1 e 
$ v; T1 K+ v8 V$ q     #只处理GET和HEAD请求,如果你不想缓存POST的页面话& w6 w1 U2 e9 U- Z' [+ g/ n
     if (req.request != "GET" && req.request != "HEAD") {! n" m; T( W5 g1 r* r5 F0 ?3 c# o1 ]; Q9 b
          return (pass);
; M7 S' ~' k3 g+ ?! o7 ]     }
" K: U2 Z4 b+ L4 `( u     #http认证的页面也pass
/ A9 V5 o2 Y: ~) @  l     if (req.http.Authorization) {; z' \! [. n3 H* N
          return (pass);
/ H) B3 W; v* b6 ^1 F) w     }7 M4 E7 M4 j, y  t2 \6 b0 B7 O1 W
 
2 h; u, \! r6 l$ D. Y6 \     return (lookup);
% r9 v, ~  k0 P}
  c7 [' R' d: f  ? 
7 s) u  U* S* D0 `6 c& e#管道?按官方文档说的是此模式下请求会原封不动的转交给后端知道连接关闭% i1 l6 u+ p  @' ^8 J
sub vcl_pipe {
8 q7 z6 j; U3 y     return (pipe);8 u" f1 G, Y* a5 E7 ^
}
: F  L* Q, M0 {6 w! C 1 r$ x6 W8 ~* z- U9 V3 P
#交给后端服务器
; h3 u- m' k) `, N1 ?3 T$ f2 psub vcl_pass {
& `/ I1 a+ M7 d/ Y    return (pass);$ j, X+ t% k8 i, E& T
}2 W; [4 S+ `4 z( v  v
 
3 S  _* \# w" H, `2 }#缓存文件名的哈希处理
2 ~$ w$ l) }8 Y3 [" G# Gsub vcl_hash {( v0 R! C" o3 S* b8 J
     #以url为hash$ {2 s/ e) O/ L* U: i
     set req.hash += req.url;
& V7 f+ ?2 f% f. P     #加上host& S- i; d, b6 E: P3 ]
     if (req.http.host) {* f. m. E3 P) c! R6 q% h. R! v; H
          set req.hash += req.http.host;) }8 G) M8 `' }+ v4 ~4 [' L
     }- m  h: |. j( K. c* q+ j- _' g3 m
     #没有则取ip
1 Q$ `$ a: ]9 S% Y! ]3 T     else {0 B! e* D$ ^6 S$ V
          set req.hash += server.ip;' W8 L* U& ~6 j
     }& k; \0 ?( C& N0 A( l$ C7 O
     #支持压缩的要增加,防止发送给不支持压缩的浏览器压缩的内容
! J4 r! ^6 ^4 }* j" ~     if(req.http.Accept-Encoding){
  `' w& Q; ~: j! P3 X! V! Y          set req.hash += req.http.Accept-Encoding;; }7 H" s- O- `3 D3 K
     }
7 b! n+ i/ Y6 C6 u: C3 O$ T4 E9 x     return (hash);
/ V9 z# V" f5 a  Z}
) f) }! T$ e# l( m2 R) @( D6 s - M) N; j% D5 A" h
#缓存命中后的处理
9 @7 G! Z, c! Z0 \  {' lsub vcl_hit {
  X3 \; S5 e: B$ A1 l% h     #如果不可缓存,直接交给后端6 L9 v3 B$ N8 [* D
     if (!obj.cacheable) {2 S9 o. X) C! U2 P" I! U0 M, a9 o
          return (pass);. ?! u/ I& Q; l1 D! p
     }
7 ~5 {- Z& ^) j3 L) h2 P# T     #如果为更新缓存的请求,则设置ttl为0,并告知客户端处理结果4 r# w/ V4 E2 Q/ G9 W" X
     if (req.request == "PURGE") {" M5 Z! H' H$ o' o* K
          set obj.ttl = 0s;2 X- }% t  `! z7 F0 ?8 t
          error 200 "Purged.";
7 |) n9 N7 s1 ]& \& Z" s- h     }4 z& ]' Q, t4 u0 a( A
 . M% D2 j4 G4 r: t$ ]
     return (deliver);# P) y0 m/ D3 n8 i( R7 j- W
}& h: ?% h8 Y/ n. |
 % ?/ d) B9 }1 I* U. d5 h- a% v% l
#缓存未命中
; ]# P' B' [9 U( Qsub vcl_miss {
: A; a  w1 B6 Y6 ]" e1 Z     #更新缓存的请求,提示缓存不存在Not in cache
% Q$ S7 L( p1 C, D  ?     if (req.request == "PURGE") {$ h$ A1 ^  V0 h$ I5 n, E8 _
          error 404 "Not in cache.";. K- h; }/ Z2 }. }: O, m3 I4 C, t" r$ ]
     }4 {. B# s, x: C4 X2 {6 H6 T) y/ l
     #既然缓存不存在了,就去后端取吧# Y+ O( G/ X" e  {1 r* [
     return (fetch);
( Z; l3 e9 S" X. Y  V# B}- v% b/ ]  v5 v4 A
#
/ Y+ o  d- g7 X$ P1 A4 ksub vcl_fetch {
/ C0 U* x# k! B. }& N) m 
" c% Q2 V- s9 Y. L     #是否开启ESI功能(仅做演示用,单独给/esi-test.html开启esi)$ d! h: z% g6 ~( M
     if (req.url == "/esi-test.html") {. J$ l) Q) G. n6 r' p% Q3 I
          esi;$ u8 W9 ~& n  \' o: V" u# J5 Z: _- s
     }1 x: X! r4 R1 x/ u
 : l4 K: P. G( N* A4 w
     #如果后端返回不可缓存,直接pass
% {  S$ D* t" t& f$ l     if (!beresp.cacheable) {
4 \3 c6 P0 L; B) t8 \: s% g) k          return (pass);
9 ~8 ], e9 c7 @     }" N& E/ i9 {' |! _! [
 
8 D. {  E1 E1 m1 U% m: ?     #/article/下的url缓存30分钟(演示用)" X4 S) d5 a0 g* O: N; E- c/ F
     if (req.url ~ "^/article/\d+\.html$"){$ `! }. Y7 k2 N0 E
          set beresp.ttl = 30m;
7 f; K2 }3 s, q: q; f! j     }
. u% T( x  u1 L& k# d, X' C 
2 P* o) r' S4 N- j: W# O: O- y     #/category/下的url缓存20分钟(演示用)
$ s. p* S; `& G     if (req.url ~ "^/category/$"){
5 N, |3 f4 {* L9 a  M2 O( l          set beresp.ttl = 20m;
& Y4 Y( @" t3 W     }  [" n( q6 D8 ?* E) n8 k
 6 {+ f3 S- z  ]9 v  h% S# ]
     #如果后端发送了set-cookie头信息(如session会话开启),则不缓存
0 z' C/ V, f3 b5 X$ Y% I" i     if (beresp.http.Set-Cookie) {
6 o4 X: N# i+ i  m- m- D          return (pass);( w7 m' F5 K& C& @1 c1 ?
     }
5 a# j& _' A% Q' b; A% } 
! ?) j: a" A' E7 N     return (deliver);
; q  i/ [8 |2 t, p/ p7 y}
7 K3 n4 t8 R, t( @  F/ ^" n 
! m+ k7 {7 m$ Y" `#发送给客户端3 ?4 \1 ^- t8 l9 f% Q
sub vcl_deliver {  Q5 f, U: N5 @4 v5 I& L
     #去掉varnish中的一些头信息(如果你不想保留的话,建议保留Age,方便查看)9 m% i( i2 s  n8 h, }! A( [
     #remove resp.http.X-Varnish;
7 E- P( v  F  N  o) e' l, ?     #remove resp.http.Via;' U& A# T- J( d3 R8 i/ w
     return (deliver);0 t) z3 ~, ~, l+ S3 N4 W8 u
}
" M' `3 {& ]. O1 R' a% I 
" J, m& ?$ C, S! L- [  W, b#定义错误页面的信息
. m7 X9 n' e! e* |. Y" D1 X% z8 qsub vcl_error {" g, c+ ?# V2 [9 M/ R
     #设置返回页面的类型及编码' O1 [2 Z! [* q" A
     set obj.http.Content-Type = "text/html; charset=utf-8";
- ~( s- R& G3 W9 [/ h& T& |8 _     #具体内容,可以更加自己的需要修改错误提示信息
  }1 G% M7 k0 W9 F" t: m' ?* _1 l     synthetic {"
! i) t8 g7 X# L) S       HTTP ERROR
8 C" m1 Q- S3 u  L- Y- x    "};
! Z: w% H% C, `, @" Z# I     return (deliver);9 M+ \$ v& R% M" m3 V
}

官方文档
非常重要,必须参考。
https://www.varnish-cache.org/docs

官方示例 Example
https://www.varnish-cache.org/trac/wiki/VCLExamples

VCL2.x升级到3.0
https://www.varnish-cache.org/docs/3.0/installation/upgrade.html 

WIKI
https://www.varnish-cache.org/trac/ 

Drupal on Varnish
https://www.varnish-cache.org/trac/wiki/VarnishAndDrupal 

至此,基本的Varnish了解完毕,后面继续讨论ESI的使用,以及如何配合Drupal模块构建高性能网站
待续…



--------------------------------------------------------------------------------------------------------------------------------------------------------


2.

本篇我们来深入讨论Varnish的优化,缓存页面,ESI,Cookie过滤,登录用户的缓存以及与Drupal的配合使用等相关话题。

首先我们熟悉一下两个VCL的函数

regsub(string, pattern, replacement); //正则替换" V# L. u% z- s. d4 R/ i% Q
regsuball(string, pattern, replacement); //同上,只是替换所有遇到的问题


开启VCL Debug信息
再优化之前,我们来开启VCL的调试功能。在当前的active.acl里面,加入如下函数,

sub vcl_deliver {) d' m9 @4 R; ~4 Y; p6 b" \4 Q
    if (obj.hits > 0) {
# j$ `7 T/ ~: K6 p& v2 B9 z2 U       set resp.http.X-Cache = "HIT";, q: {' c( e( @& j; B
    } else {; e  B4 b6 u' f4 I
       set resp.http.X-Cache = "MISS";5 Q! O" s! b' `7 _
    }  m2 y7 {/ g- q! C: P! J
}
: |- i9 v  A% J1 z: C# g8 n " w$ \: }6 O* e7 d$ B& m# F
#如果需要详细的了解cookie值,可以在vcl_fetch里面加入如下代码. u. i1 p# j: M' T8 H
sub vcl_fetch {! ]0 o; L% D4 W
  # Debug the req cookie; z7 Y; @4 ], u2 ^5 m
  set obj.http.X-Cookie-Debug = "Request cookie: " req.http.Cookie;  s, p6 b8 F6 C( D- ^3 _
}

这样在HTTP Header里面我们添加了调试信息,如果缓存命中,则是HIT反之为MISS。

默认情况下,varnish提供一个http头,X-Varnish,它包含两个数字,一个是当前请求的ID,另一个是缓存中的ID,这表明,如果X-Varnish含有两个值,则缓存命中,反之没有命中。

基本优化
默认情况下,Varnish不缓存任何有Cookie的请求,带有Cookie的请求,Varnish认为是私有的HTTP会话,会直接pass到backend。所以我们首先要删除Cookie。一般情况下,对于图片、附件、css、js等文件,是不需要cookie会话的,所以我们需要对其缓存。
在vcl_recv里面添加如下代码

  # cache these file types3 W' X1 G& w* ]6 Y' F
  if (req.url ~ "\.(jpg|png|css|js|ico|gz|tgz|bz2|tbz|gif)$") {
1 I, m- J* T' G: D- s    remove req.http.cookie;+ ~$ |2 H- P0 V1 J7 ~; O4 w6 Z
  }

Drupal动态内容的缓存
默认的Drupal会开启session,页面会带有session生成的cookie值和value,这样Varnish就无法缓存内容页面。

方法A,在Drupal添加代码,给每个Role设置一个cookie,然后在VCL中检查检查,如果是匿名用户就删除所有cookie。
方法B,使用Drupal的PressFlow版本,PressFlow会默认清理掉匿名用户的Cookie。

ESI之动态内容
对于随时当前用户变动的信息,需要即时刷新,和SSI类似,我们可以用类似部分包含的方法,通过ESI,我们可以调用一些动态内容。详细的了解请参阅官方网站。
Drupal已经有一个模块,Edge Side Includes integration,可以将Drupal的Block转换成ESI include的形式。
正常登陆和非登陆情况下测试成功,但问题在于这两种情况下,页面的HTML并没有缓存成功。
所以关于登陆用户的ESI缓存,比较困难。

有解决方案,就是把Cookie的值通过ESI的URL来传递,但是目前的ESI也不支持。
https://www.varnish-cache.org/trac/wiki/VCLExampleCachingLoggedInUsers

PS:如果使用Varnish3.0,请下载新的配置文件 http://drupal.org/node/1388950

Varnish和Boost搭配使用
前文Drupal性能优化之-将Boost模块用到极致,详细讲解了使用CSI(ajax)对于登录用户的处理。
因为Varnish对页面缓存的实效性、已经不完整性,用Boost可以做有效的补充。通常情况下,Varnish做一般的缓存代理,后端用Boost生成静态页面,把Varnish的逻辑减少,服务器结构之间相对简单一些,以便于维护。

这里有一篇文章,讲解了如何使用Boost和Varnish模块,详情请参阅:Boosted Varnish – High Performance Caching made easy

结论
Varnish的ESI对登录用户的支持有限,所以一般情况下,我们只作为前段缓存和代理来使用,如果要缓存登录用户,即使我们使用Cookie的Hash,使用大量的内存提高缓存,缓存的使用率也是非常低的。因此我们还是使用Boost的CSI(ajax)来实现,调试方式也比Varnish的ESI简单。

附录A Varnish缓存清理PHP接口

Varnish官方站给出了PHP的接口文件,但是该文件有很多bug以及Varnish3.0不能使用等原因,经过修改和调试,分享供大家使用。
注意:如果只用一个vpurge.php通过GET方式获得URL,那么该vpurge.php也会被varnish缓存,所以使用html form提交POST的方式清除缓存是比较好的选择。

1. vpurge.php 文件

  $ip = '127.0.0.1';//
( ^2 V6 p. l( f9 Z6 L% X  $port = '6082'; //$ ^4 k5 ?" M0 b9 C
 & |* N  S! v5 Z3 @2 M
  $timeout = 1;* W6 V# A" Q- a! ~$ T7 S. q8 l
  $verbose = 1;0 m$ ~( k( o2 f! i
  # inits
6 T3 Y* C6 ?1 |) i  $sock = fsockopen ($ip,$port,$errno, $errstr,$timeout);
3 L7 z4 @- s  L' h) C# f( }  if (!$sock) { echo "connections failed $errno $errstr"; exit; }+ w" c  S; R" S3 t" z
 + {, k* `; J# P# }% g( S
  # get param and strip invalid stuff0 E& W( H1 U/ a! F
  $url  = $_POST['url'];
6 P3 q6 u9 y5 L; C/ Z8 N / {' z' t( S. w
  if (!$url) { echo "No params"; exit; }- l# U3 y! \/ |8 S' Z
 
% n% L  k. p6 {) y& \  stream_set_timeout($sock,$timeout);
; c$ O! g% s2 w  put ("ban.url ". $url .'$'); #Varnish 3.0需要修改成 purge.url
9 `# g9 v9 P0 T6 f  put ("quit");
! U8 U, [3 m. h, K8 Q 3 }8 q( h: l  ]$ E+ q! _4 X  y- [( G
  fclose ($sock);
7 t, R3 ~* Y' I0 l+ O4 \/ q 
( ~9 P5 q, T+ b( s  _2 [  function readit() {
. T. v& q) J2 ]+ b# u7 t! @    global $sock,$verbose;
7 N  N; N8 U% H    if (!$verbose) { return; }
) \- g( r% F9 {% a    while ($sockstr = fgets($sock,1024)) {
4 o$ }7 ?4 v6 R      $str .= "rcv: " . $sockstr . ") n+ X+ V, n% h8 {
";
6 ?7 L* @7 g5 i4 c    }
* K3 Z% ~" q& `$ _5 L( S$ G    if ($verbose) { echo "$str\n"; }5 ]( _, ~; q  M6 w0 C8 y
  }0 ?* F. m& G% A1 q; {. L4 W
 8 y0 a2 R& u" p& K& U: A
  function put($str) {5 Y$ P' x' _" @3 ]" ]' j5 P
    global $sock,$verbose;. N; V3 B4 Z" y6 a' y2 t
    fwrite ($sock, $str . "\r\n");$ a% |( Z, `. k$ M
    if ($verbose) { echo "send: $str  r: M  T3 I' e) M$ H
\n"; }0 X8 C4 ^, q5 m' Y$ |/ W- @
    readit();
  H( N) K- W" K* D, j' Q' }' o7 m  }

2. purge.html

  <h1>Makes Varnish purge the supplied URL from its cache</h1>
# f6 Z" n5 h; f  <form action="vpurge.php" method="post">/ L9 s* e! u$ X1 Z0 @) Y: J( N
        <p><label>URL</label> <input type="text" name="url"></p>
; i- B! R" a% U! }9 V$ B  g8 G9 W3 I! i        <p class="submit"><input value="Submit" type="submit"></p>$ r2 k$ s( z: C5 ^
  </form>

附录B Varnish的缓存以及VCL的流程图

相关文章:
高负载网站之Varnish与Drupal – 基本篇
Drupal性能优化之-将Boost模块用到极致





声明: 本站所有文章欢迎转载,所有文章未说明,均属于原创,转载均请注明出处。 
本文有效链接: http://www.drupal001.com/2011/12/varnish-drupal-advanced/ 
版权所有: Drupal与高性能网站架构 http://www.drupal001.com

|2011-2026-版权声明|平台(网站)公约|手机版|手机版|DOOOOR 设计网 ( 吉ICP备2022003869号 )

GMT+8, 12-18-2025 17:07 , Processed in 0.367070 second(s), 22 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表