第五周负载及缓存
1、运用haproxy实现nginx服务负载均衡
http://cbonte.github.io/haproxy-dconv/1.5/configuration.html #github之上的文档手持地址
docker pull nginx:alpine #使用docker拉取nginx镜像
[root@centos7 ~]# docker run --name web1 -d --network bridge nginx:alpine #启动容器
WARNING: IPv4 forwarding is disabled. Networking will not work.
4055e0d579ff7f0ef999322ab6d698ce7f7d15f1e049231b848e9704a390545a
[root@centos7 ~]# docker run --name web2 -d --network bridge nginx:alpine#启动容器
exec -it web1 /bin/sh #连接容器
/ # cd /usr/share/nginx/html
/usr/share/nginx/html # echo hello world 1 > /usr/share/nginx/index.html #修改一下测试页
[root@centos7 ~]# docker exec -it web2 /bin/sh
/ # echo hello world 2 > /usr/share/nginx/html/index.html
vim /etc/haproxy/haproxy.cfg
修改配置文件在frontend 字段中指定前段调度监听的端口为80,调度到后端websrvs组。定义后端backend组 组名为websrvs ,组内服务器172.17.0.2|0.3这两台主机。
frontend myweb *:80
default_backend websrvs
backend websrvs
server web1 172.17.0.2:80 check
server web1 172.17.0.3:80 check
listen http_proxy #也可以使用listen将前端和后端定义在一起,定义多端口,
bind :80,:8080 #也可以使用bind绑定端口
# default_backend websrvs
#backend websrvs
server web1 172.17.0.2:80 check
server web2 172.17.0.3:80 check
balance:后端服务器组内的服务器调度算法只能定义在backend和listen中
balance <algorithm> [ <arguments> ]
balance url_param <param> [check_post]
算法:
roundrobin:Each server is used in turns, according to their weights.
server options: weight #
动态算法:支持权重的运行时调整,支持慢启动;每个后端中最多支持4095个server;
static-rr:
静态算法:不支持权重的运行时调整及慢启动;后端主机数量无上限;
leastconn:
推荐使用在具有较长会话的场景中,例如MySQL、LDAP等;
first:
根据服务器在列表中的位置,自上而下进行调度;前面服务器的连接数达到上限,新请求才会分配给下一台服务;
source:源地址hash;
除权取余法:
一致性哈希:
uri:
对URI的左半部分做hash计算,并由服务器总权重相除以后派发至某挑出的服务器;
<scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag>
左半部分:/<path>;<params>
整个uri:/<path>;<params>?<query>#<frag>
username=jerry
url_param:对用户请求的uri的<params>部分中的参数的值作hash计算,并由服务器总权重相除以后派发至某挑出的服务器;常用于追踪用户,以确保来自同一个用户的请求始终发往同一个Backend Server;
username=tom
hdr(<name>):对于每个http请求,此处由<name>指定的http首部将会被取出做hash计算; 并由服务器总权重相除以后派发至某挑出的服务器;没有有效值的会被轮询调度;
hdr(Cookie)
rdp-cookie
rdp-cookie(<name>)
hash-type:哈希算法
hash-type <method> <function> <modifier>
map-based:除权取余法,哈希数据结构是静态的数组;
consistent:一致性哈希,哈希数据结构是一个树;
<function> is the hash function to be used : 哈希函数
sdbm
djb2
wt6
示例
balance roundrobin
balance url_param userid
balance url_param session_id check_post 64
balance hdr(User-Agent)
balance hdr(host)
balance hdr(Host) use_domain_only
server <name> <address>[:[port]] [param*]
定义后端主机的各服务器及其选项;
server <name> <address>[:port] [settings ...]
default-server [settings ...]
<name>:服务器在haproxy上的内部名称;出现在日志及警告信息中;
<address>:服务器地址,支持使用主机名;
[:[port]]:端口映射;省略时,表示同bind中绑定的端口;
[param*]:参数
maxconn <maxconn>:当前server的最大并发连接数;
backlog <backlog>:当前server的连接数达到上限后的后援队列长度;
backup:设定当前server为备用服务器;
check:对当前server做健康状态检测;
addr :检测时使用的IP地址;
port :针对此端口进行检测;
inter <delay>:连续两次检测之间的时间间隔,默认为2000ms;
rise <count>:连续多少次检测结果为“成功”才标记服务器为可用;默认为2;
fall <count>:连续多少次检测结果为“失败”才标记服务器为不可用;默认为3;
注意:option httpchk,"smtpchk", "mysql-check", "pgsql-check" and "ssl-hello-chk" 用于定义应用层检测方法;
cookie <value>:为当前server指定其cookie值,用于实现基于cookie的会话黏性;
disabled:标记为不可用;
on-error <mode>:后端服务故障时的行动策略;
- fastinter: force fastinter
- fail-check: simulate a failed check, also forces fastinter (default)
- sudden-death: simulate a pre-fatal failed health check, one more failed
check will mark a server down, forces fastinter
- mark-down: mark the server immediately down and force fastinter
redir <prefix>:将发往此server的所有GET和HEAD类的请求重定向至指定的URL;
server web2 172.17.0.3:80 redir http://www.baidu.com #示例
weight <weight>:权重,默认为1;
OK --> PROBLEM
OK --> PROBLEM --> PROBLEM --> PROBLEM
PROBLEM --> OK
统计接口启用相关的参数:
stats enable
启用统计页;基于默认的参数启用stats page;
- stats uri : /haproxy?stats
- stats realm : "HAProxy Statistics"
- stats auth : no authentication
- stats scope : no restriction
stats auth <user>:<passwd>
认证时的账号和密码,可使用多次;
stats realm <realm>
认证时的realm;
stats uri <prefix>
自定义stats page uri
stats refresh <delay>
设定自动刷新时间间隔;
stats admin { if | unless } <cond>
启用stats page中的管理功能
配置示例:
listen stats
bind :9099
stats enable
stats realm HAPorxy\ Stats\ Page #登录时验证提示信息
stats auth admin:admin
stats admin if TRUE
#############在frontend 加入stats enable 即可访问
frontend myweb
bind :80,:8080
default_backend websrvs
backend websrvs
server web1 172.17.0.2:80 check
server web2 172.17.0.3:80 check
listen stats :9090
stats enable
stats uri /admin ?stats #自定义修改页面路径
mode { tcp|http|health }
定义haproxy的工作模式;
tcp:基于layer4实现代理;可代理mysql, pgsql, ssh, ssl等协议;
http:仅当代理的协议为http时使用;
health:工作为健康状态检查的响应模式,当连接请求到达时回应“OK”后即断开连接;
cookie <name> [ rewrite | insert | prefix ] [ indirect ] [ nocache ] [ postonly ] [ preserve ] [ httponly ] [ secure ] [ domain <domain> ]* [ maxidle <idle> ] [ maxlife <life> ]
<name>:is the name of the cookie which will be monitored, modified or inserted in order to bring persistence.
rewirte:重写;
insert:插入;
prefix:前缀;
基于cookie的session sticky的实现:
backend websrvs
cookie WEBSRV insert nocache indirect #定义cookie的名称和插入cookie报文的信息,这里的名称并不重要
server srv1 172.16.100.6:80 weight 2 check rise 1 fall 2 maxconn 3000 cookie srv1 #定义cookie名
server srv2 172.16.100.7:80 weight 1 check rise 1 fall 2 maxconn 3000 cookie srv2
option forwardfor [ except <network> ] [ header <name> ] [ if-none ]
Enable insertion of the X-Forwarded-For header to requests sent to servers
在由haproxy发往后端主机的请求报文中添加“X-Forwarded-For”首部,其值前端客户端的地址;用于向后端主发送真实的客户端IP;
reqadd <string> [{if | unless} <cond>]
Add a header at the end of the HTTP request
rspadd <string> [{if | unless} <cond>]
Add a header at the end of the HTTP response
rspadd X-Via:\ HAPorxy
reqdel <search> [{if | unless} <cond>]
reqidel <search> [{if | unless} <cond>] (ignore case)
Delete all headers matching a regular expression in an HTTP request
rspdel <search> [{if | unless} <cond>]
rspidel <search> [{if | unless} <cond>] (ignore case)
Delete all headers matching a regular expression in an HTTP response
rspidel Server.*
日志系统:
log:
log global
log <address> [len <length>] <facility> [<level> [<minlevel>]]
no log
注意:
默认发往本机的日志服务器;
(1) local2.* /var/log/local2.log
(2) $ModLoad imudp
$UDPServerRun 514
log-format <string>:
对后端服务器做http协议的健康状态检测:
option httpchk
option httpchk <uri>
option httpchk <method> <uri>
option httpchk <method> <uri> <version>
定义基于http协议的7层健康状态检测机制;
http-check expect [!] <match> <pattern>
Make HTTP health checks consider response contents or specific status codes.
配置HAProxy支持https协议:
1 支持ssl会话;
bind *:443 ssl crt /PATH/TO/SOME_PEM_FILE
crt后的证书文件要求PEM格式,且同时包含证书和与之匹配的所有私钥;
cat demo.crt demo.key > demo.pem
2 把80端口的请求重向定443;
bind *:80
redirect scheme https if !{ ssl_fc }
另一种配置:对非ssl的任何url的访问统统定向至https主机的主页;
redirect location https://172.16.0.67/ if !{ ssl_fc }
3 如何向后端传递用户请求的协议和端口
http_request set-header X-Forwarded-Port %[dst_port]
http_request add-header X-Forwared-Proto https if { ssl_fc }
配置时常用的功能:
http --> https
mode http
压缩、条件式转发、算法、stats page、自定义错误页、访问控制、日志功能
最大并发连接;
global, defaults, frontend, listen, server
基于cookie的session粘滞
后端主机的健康状态检测
请求和响应报文首部的操纵
acl:
访问控制列表(ACL)的使用提供了一种灵活的解决方案来执行内容切换,并且通常基于从请求,响应或任何环境状态中提取的内容来做出决策
acl <aclname> <criterion> [flags] [operator] [<value>] ...
<aclname>:ACL names must be formed from upper and lower case letters, digits, '-' (dash), '_' (underscore) , '.' (dot) and ':' (colon).ACL names are case-sensitive.
<value>的类型:
- boolean
- integer or integer range
- IP address / network
- string (exact, substring, suffix, prefix, subdir, domain)
- regular expression
- hex block
匹配字符串:
- exact match (-m str) : the extracted string must exactly match the patterns ;
- substring match (-m sub) : the patterns are looked up inside the extracted string, and the ACL matches if any of them is found inside ;
- prefix match (-m beg) : the patterns are compared with the beginning of the extracted string, and the ACL matches if any of them matches.
- suffix match (-m end) : the patterns are compared with the end of the extracted string, and the ACL matches if any of them matches.
- subdir match (-m dir) : the patterns are looked up inside the extracted string, delimited with slashes ("/"), and the ACL matches if any of them matches.
- domain match (-m dom) : the patterns are looked up inside the extracted string, delimited with dots ("."), and the ACL matches if any of them matches.
acl作为条件时的逻辑关系:
- AND (implicit)
- OR (explicit with the "or" keyword or the "||" operator)
- Negation with the exclamation mark ("!")
if invalid_src invalid_port #与
if invalid_src || invalid_port #或
if ! invalid_src invalid_port#非
基于ACL的动静分离示例:
frontend web *:80
acl url_static path_beg -i /static /images /javascript /stylesheets
acl url_static path_end -i .jpg .gif .png .css .js .html .txt .htm
use_backend staticsrvs if url_static
default_backend appsrvs
backend staticsrvs
balance roundrobin
server stcsrv1 172.16.100.6:80 check
backend appsrvs
balance roundrobin
server app1 172.16.100.7:80 check
server app1 172.16.100.7:8080 check
listen stats
bind :9091
stats enable
stats auth admin:admin
stats admin if TRUE
global配置参数:
进程及安全管理:chroot, daemon,user, group, uid, gid
log:定义全局的syslog服务器;最多可以定义两个;
log <address> [len <length>] <facility> [max level [min level]]
nbproc <number>:要启动的haproxy的进程数量;
ulimit-n <number>:每个haproxy进程可打开的最大文件数;
性能调整:
maxconn <number>:设定每个haproxy进程所能接受的最大并发连接数;Sets the maximum per-process number of concurrent connections to <number>.
总体的并发连接数:nbproc * maxconn
maxconnrate <number>:Sets the maximum per-process number of connections per second to <number>. 每个进程每秒种所能创建的最大连接数量;
maxsessrate <number>:设定会话常见速率
maxsslconn <number>: Sets the maximum per-process number of concurrent SSL connections to <number>.
设定每个haproxy进程所能接受的ssl的最大并发连接数;
spread-checks <0..50, in percent>
2、搭建haproxy实现mysql负载均衡
docker pull mysql:5.7 #拉取镜像
[root@centos7 haproxy]# docker run --name db1 -d e "MYSQL_ROOT_PASSWORD=123456" mysql:5.7 #启动容器1
[root@centos7 haproxy]# docker run --name db2 -d e "MYSQL_ROOT_PASSWORD=123456" mysql:5.7 #启动容器2
docker exec -it db1 /bin/sh #连接容器创建账号
mysql> GRANT ALL ON *.* to 'myuser'@'%' identified by '123456';
docker exec -it db1 /bin/sh
mysql> GRANT ALL ON *.* to 'myuser'@'%' identified by '123456';
yum install haproxy
修改配置
vi/etc/haproxy/haproxy.cfg
配置如下
global
daemon
nbproc 1
pidfile /var/run/haproxy.pid
defaults
mode tcp #默认的模式mode { tcp|http|health },tcp是4层,http是7层,health只会返回OK
retries 3 #两次连接失败就认为是服务器不可用,也可以通过后面设置
option redispatch #当serverId对应的服务器挂掉后,强制定向到其他健康的服务器
option abortonclose #当服务器负载很高的时候,自动结束掉当前队列处理比较久的链接
maxconn 4096 #默认的最大连接数
timeout connect 5000ms #连接超时
timeout client 30000ms #客户端超时
timeout server 30000ms #服务器超时
timeout check 2000 #=心跳检测超时
log 127.0.0.1 local0 err #[err warning info debug]
listen configMysql
bind :3306
mode tcp
maxconn 4086
server s1 172.17.0.2:3306
server s2 172.17.0.3:3306
启动
在解压目录下执行haproxy -f /etc/haproxy/haproxy.cfg
或者使用
3、搭建tomcat服务器, 并通过nginx反向代理访问
Tomcat安装
tomcat下载
进入官网,在左侧download中选择对应tomcat主版本,然后点击右侧的Archives,找到对应的具体版本后进入到bin目录下载tar.gz包,点击Which version查看Tomcat版本对应的JDK版本要求。
这里我们下载的版本是7.0.73
安装步骤
首先确保已经安装好了jdk,并且jdk版本能够满足当前Tomcat的版本要求。
解压缩:tar -zxvf apache-tomcat-7.0.73.tar.gz
将tomcat移到安装软件位置:mv apache-tomcat-7.0.73 /usr/local/
环境变量配置
编辑环境变量配置文件:vim /etc/profile
在文件末尾位置添加如下内容(CATALINA_HOME为安装tomcat的路径)
export CATALINA_HOME=/usr/local/apache-tomcat-7.0.73
通过vim的 ":wq" 命令进行保存退出
使配置生效:source /etc/profile
字符集配置
进入tomcat安装目录的conf目录,编辑server.xml文件
cd /usr/local/apache-tomcat-7.0.73/
vim server.xml
找到配置8080端口的位置,在节点末尾添加URIEncoding="UTF-8"
tomcat验证
进入tomcat安装目录的bin目录,执行./startup.sh,看到如图提示代表启动成功。
可以通过主机的ip地址+8080端口访问tomcat主页,比如:http://192.168.0.110:8080/
需要注意检查防火墙是否关闭,如果未关闭需要配置iptables规则开放8080端口。
关闭iptables规则:iptables -F & iptables -t nat -F
Tomcat启动与关闭
Tomcat启动:${CATALINA_HOME}/bin/startup.sh
Tomcat关闭:${CATALINA_HOME}/bin/shutdown.sh
${CATALINA_HOME}代表tomcat的安装路径
这里将nginx和tomcat都安装在/usr/local目录下,tomcat目录分别命名为tomcat1和tomcat2。
配置环境变量
vim /etc/profile# 在文件末尾添加如下内容
export CATALINA_BASE=/usr/local/tomcat1export CATALINA_HOME=/usr/local/tomcat1export
TOMCAT_HOME=/usr/local/tomcat1export
CATALINA_2_BASE=/usr/local/tomcat2export
CATALINA_2_HOME=/usr/local/tomcat2export
TOMCAT_2_HOME=/usr/local/tomcat2# 通过vim的 ":wq" 命令进行保存退出# 使配置生效source /etc/profile
修改两个tomcat的编码
vim ${tomcat}/conf/server.xml
进入tomcat安装目录,编辑conf目录下的server.xml文件,找到如下节点添加编码配置:URIEncoding="UTF-8"
<Connectorport="8080"protocol="HTTP/1.1"connectionTimeout="20000"redirectPort="8443"URIEncoding="UTF-8"/>
修改第二个tomcat的配置
修改第二个tomcat的bin目录下的catalina.sh文件,找到# OS注释内容,在其下面新增配置
vim/usr/local/tomcat2/bin/catalina.sh# 新增如下配置# OS specific support. $var _must_ be set to either true or false.export CATALINA_BASE=$CATALINA_2_BASEexport CATALINA_HOME=$CATALINA_2_HOME
修改第二个tomcat的conf目录下的server.xml文件,,修改3个端口配置
vim /usr/local/tomcat2/conf/server.xml# 修改以下3个端口,修改为9005,9080,9009<Serverport="9005"shutdown="SHUTDOWN"><ListenerclassName="org.apache.catalina.startup.VersionLoggerListener"/><Connectorport="9080"protocol="HTTP/1.1"connectionTimeout="20000"redirectPort="8443"URIEncoding="UTF-8"/><Connectorport="9009"protocol="AJP/1.3"redirectPort="8443"/>
为了方便对两个tomcat进行区分,我们修改第二个tomcat的主页logo图片,即替换/usr/local/tomcat2/webapps/ROOT/tomcat.png,图片名保持一致。
防火墙配置
如果开启了防火墙,需要配置防火墙规则,先将两个tomcat的端口进行开放,主要是为了测试,在配置了Nginx负载均衡后就可以关闭端口了。
vim /etc/sysconfig/iptables# 添加如下规则-A INPUT -p tcp -m tcp --dport 8080 -j ACCEPT-A INPUT -p tcp -m tcp --dport 9080 -j ACCEPT# 重启iptablesservice iptables restart
tomcat验证
分别启动两个tomcat,即执行命令:${tomcat}/bin/startup.sh
注意检查两个tomcat启动使用的环境变量是否正确。
分别访问两个tomcat,这里是在windows下访问虚拟机的IP + 端口。
修改nginx主配置文件nginx.conf
vim /usr/local/nginx/conf/nginx.conf# 在注释内容上面添加如下内容include vhost/*.conf;# another virtual host
在Nginx安装目录的conf目录下新建一个vhost目录,然后在vhost目录下新建配置文件,文件名需要以.conf结尾
cd /usr/local/nginx/conf/mkdir vhostcd vhost/vim www.silly.com.conf
配置文件添加如下内容,这里server_name配置的是主机对应的域名,proxy_pass是反向代理配置,upstream是负载均衡配置。
upstream www.silly.com{
server 127.0.0.1:8080 weight=1;
server 127.0.0.1:9080 weight=2;
}
server {
listen 80;
autoindex on;
server_name silly.com www.silly.com;
access_log /usr/local/nginx/logs/access.log combined;
index index.html index.htm index.jsp index.php;
location / {
proxy_pass http://www.silly.com;
add_header Access-Control-Allow-Origin *;
}
}
4、采用varnish为nginx实现缓存加速
使用epel中仓库自带varnish安装包
yum install epel-release varnish
varnish中配置文件
/etc/varnish/default.vcl #用来定义缓存策略的配置文件
/etc/varnish/varnish.params #用来定义进程状态端口等,本身没有配置参数,这个配置文件会把自身定义的变量传递给unitfile文件,启动时通过配置文件定义的参数来启动
[root@centos7 ~]# cat /etc/varnish/varnish.params
cat /usr/lib/systemd/system/varnish.service#服务单元启动时都调用了param文件中的变量
修改varnish.params配置文件
RELOAD_VCL=1
VARNISH_VCL_CONF=/etc/varnish/default.vcl #加载默认vcl配置文件,可修改
VARNISH_LISTEN_PORT=80 #监听的端口默认为6081,客户端监听端口
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1 #监听在本机地址
VARNISH_ADMIN_LISTEN_PORT=6082#管理端口
VARNISH_SECRET_FILE=/etc/varnish/secret #秘钥文件
VARNISH_STORAGE="file,/var/cache/varnish/,2G" #缓存的类型,大小,可用内存缓存,文件缓存等。。malloc基于内存缓存。file表示为磁盘缓存,需要varnish用户对此目录拥有权限
VARNISH_USER=varnish #运行varnish进程的用户
VARNISH_GROUP=varnish
mkdir /var/cache/varnish/ #创建缓存目录
chown -R varnish.varnish /var/cache/varnish/ #修改缓存目录所属用户及组
systemctl start varnish 启动varnish
docker pull nginx:1.14-alpine #使用容器拉取镜像,使用varnish作为前端
docker run --name web1 -d nginx:1.14-alpine #启动容器
查看容器地址
[root@centos7 varnish]# varnish_reload_vcl #重新加载新vcl文件
Loading vcl from /etc/varnish/default.vcl 加载配置文件
Current running config name is
Using new config name reload_2019-09-16T04:42:42
VCL compiled. 编译
VCL 'reload_2019-09-16T04:42:42' now active
available 0 boot #随系统启动的vcl文件。未启动
active 0 reload_2019-09-16T04:42:42#当前正在使用的vcl版本即我们修改后的版本
Done
命令端接口varnishadm管理varnish
[root@centos7 varnish]# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082 #-S指定秘钥文件 -T 指定主机(本机)及端口
200
-----------------------------
Varnish Cache CLI 1.0
-----------------------------
Linux,3.10.0-693.el7.x86_64,x86_64,-sfile,-smalloc,-hcritbit
varnish-4.0.5 revision 07eff4c29
Type 'help' for command list.
Type 'quit' to close CLI session.
help [<command>]
ping [<timestamp>]
auth <response> #认证
quit
banner #欢迎信息
status#状态信息
start#开启varnish进程
stop#关闭
vcl.load <configname> <filename> #与vcl相关,加载vcl文件进来
vcl.inline <configname> <quoted_VCLstring>
vcl.use <configname>#切换vcl文件
vcl.discard <configname>
vcl.list #显示vcl文件
param.show [-l] [<param>] #定义进程工作特性,即使生效,重启失效
param.set <param> <value>
panic.show
panic.clear
storage.list#存储相关
vcl.show [-v] <configname>
backend.list [<backend_expression>] #后端服务器相关
backend.set_health <backend_expression> <state>
ban <field> <operator> <arg> [&& <field> <oper> <arg>]...
ban.list #缓存清理相关
vcl.show -v boot #查看默认的vcl规则
varnish的vcl文件常用变量
变量类型:
内建变量:
req.*:request,表示由客户端发来的请求报文相关;
req.http.*
req.http.User-Agent, req.http.Referer, ...
bereq.*:由varnish发往BE主机的httpd请求相关;Backend 后端主机
bereq.http.*
beresp.*:由BE主机响应给varnish的响应报文相关;
beresp.http.*
resp.*:由varnish响应给client相关;
obj.*:存储在缓存空间中的缓存对象的属性;只读;
举例:obj.hits是内建变量,用于保存某缓存项的从缓存中命中的次数;
vim /etc/varnish/default.vcl
sub vcl_deliver{ #定义在deliver阶段,不能定义在recv(接受客户端请求阶段)和backend_response(后端服务器响应阶段)
if (obj.hits>0) {
set resp.http.X-Cache = "HIT via" + " " + server.ip;
} else {
set resp.http.X-Cache = "MISS from " + server.ip;
}
}
varnish> vcl.load test1 default.vcl 加载配置文件进来,并且改名为test1
200
VCL compiled.
vcl.use test1 切换到test1 vcl文件
200
VCL 'test1' now active
vcl.list
200
available 0 boot
available 0 reload_2019-09-16T04:42:42
active 0 test1
varnish_reload_vcl #也可以使用此命令重新加载配置文件
curl -I http://172.17.0.1 访问本机请求的头部值,查看,第一次为miss第二次为hit
VCL有多个状态引擎,状态之间存在相关性,但状态引擎彼此间互相隔离;每个状态引擎可使用return(action)指明关联至哪个下一级引擎;每个状态引擎对应于vcl文件中的一个配置段,即为subroutine
vcl_init
vcl_recv,
vcl_synth
vcl_pipe
vcl_pass
vcl_hash
vcl_hit
vcl_deliver
vcl_miss
vcl_pass
vcl_backend_fetch
vcl_backend_error
vcl_backend_response
vcl_deliver
vcl的语法格式:
(1) VCL files start with “vcl 4.0;”
(2) //, # and /* foo */ for comments; #注释
(3) Subroutines are declared with the sub keyword; 例如sub vcl_recv { ...};#每一个子例程都要用sub 加上关键词之内,
(4) No loops, state-limited variables(受限于引擎的内建变量);#不支持循环
(5) Terminating statements with a keyword for next action as argument of the return() function, i.e.: return(action);用于实现状态引擎转换;#指定吓一跳是谁
(6) Domain-specific;
流程示意图
定义哪些能缓存,哪些不能缓存的项
sub vcl_recv { #在recv阶段调整访问login界面时不能被缓存,直接发往后端。
if(req.url ~ "^/login") {
return(pass);
}
docker exec -it web1 /bin/sh #进入容器创建页面
echo hello index nginx >> /usr/share/nginx/html/login
####访问login界面便不会再名字缓存,访问普通首页即会缓存
vcl变量
变量类型:
内建变量:
req.*:request,表示由客户端发来的请求报文相关;
req.http.*
req.http.User-Agent, req.http.Referer, ...
bereq.*:由varnish发往BE主机的httpd请求相关;
bereq.http.*
beresp.*:由BE主机响应给varnish的响应报文相关;
beresp.http.*
resp.*:由varnish响应给client相关;
obj.*:存储在缓存空间中的缓存对象的属性;只读;
常用变量:
bereq.*, req.*:
bereq.http.HEADERS
bereq.request, req.request:请求方法;
bereq.url, req.url:请求的url;
bereq.proto,req.proto:请求的协议版本;
bereq.backend:指明要调用的后端主机;
req.http.Cookie:客户端的请求报文中Cookie首部的值;
req.http.User-Agent ~ "chrome"
beresp.*, resp.*:
beresp.http.HEADERS
beresp.status, resp.status:响应的状态码;
reresp.proto, resp.proto:协议版本;
beresp.backend.name:BE主机的主机名;
beresp.ttl:BE主机响应的内容的余下的可缓存时长;
obj.*
obj.hits:此对象从缓存中命中的次数;
obj.ttl:对象的ttl值
server.*
server.ip:varnish主机的IP;
server.hostname:varnish主机的Hostname;
client.*
client.ip:发请求至varnish主机的客户端IP;
缓存对象的修剪:purge, ban
配置purge操作:
(1) 能执行purge操作
sub vcl_purge { #定义purge方法。
return (synth(200,"Purged")); #返回200 清理成功
}
(2) 何时执行purge操作
sub vcl_recv { #这个配置项是接收请求时,发现是purge则会掉上面定义好的purge方法清理缓存
if (req.method == "PURGE") {
return(purge);
}
...
}
curl -X PURGE http://172.17.0.1 #模拟purge方法清理缓存
添加此类请求的访问控制法则:
acl purgers {
"127.0.0.0"/8;
"10.1.0.0"/16;
}
sub vcl_recv {
if (req.method == "PURGE") {
if (!client.ip ~ purgers) {
return(synth(405,"Purging not allowed for " + client.ip));
}
return(purge);
}
...
}
Banning: #使用ban命令清理指定页面的缓存
(1) varnishadm:
ban <field> <operator> <arg> #在命令行中使用ban命令清理指定缓存
示例:
ban req.url ~ (?i)^/javascripts
(2) 在配置文件中定义,使用ban()函数;
示例:
if (req.method == "BAN") {
ban("req.http.host == " + req.http.host + " && req.url == " + req.url);
# Throw a synthetic page so the request won't go to the backend.
return(synth(200, "Ban added"));
}
curl -X BAN http://www.ilinux.io/test1.html
ban req.http.host==www.ilinux.io && req.url==/test1.html
如何设定使用多个后端主机:
backend default {
.host = "172.16.100.6";
.port = "80";
}
backend appsrv {
.host = "172.16.100.7";
.port = "80";
}
sub vcl_recv {
if (req.url ~ "(?i)\.php$") {
set req.backend_hint = appsrv;
} else {
set req.backend_hint = default;
}
...
}
nginx: proxy_pass
haproxy: use_backend
BE Health Check:#健康状态检测
backend BE_NAME {
.host =
.port =
.probe = {
.url=
.timeout=
.interval=
.window=
.threshold=
}
}
.probe:定义健康状态检测方法;
.url:检测时要请求的URL,默认为”/";
.request:发出的具体请求;
.request =
"GET /.healthtest.html HTTP/1.1"
"Host: www.magedu.com"
"Connection: close"
.window:基于最近的多少次检查来判断其健康状态;
.threshold:最近.window中定义的这么次检查中至有.threshhold定义的次数是成功的;成功阈值;
.interval:检测频度;
.timeout:超时时长;
.expected_response:期望的响应码,默认为200;
健康状态检测的配置方式:
probe check {
.url = "/.healthcheck.html";
.window = 5;
.threshold = 4;
.interval = 2s;
.timeout = 1s;
}
backend default {
.host = "10.1.0.68";
.port = "80";
.probe = check;
}
backend appsrv {
.host = "10.1.0.69";
.port = "80";
.probe = check;
}
手动设定BE主机的状态:
sick:管理down;
healthy:管理up;
auto:probe auto;
varnish做负载均衡配置示例
Director:#需要导入此模块来做负载均衡
varnish module;
使用前需要导入:
import directors;
示例:
import directors; # load the directors
示例: 1定义后端机器。2将后端服务器分组加入GROUP中,并指定其调度算法
backend imgsrv1 {
.host = "192.168.10.11";
.port = "80";
}
backend imgsrv2 {
.host = "192.168.10.12";
.port = "80";
}
backend appsrv1 {
.host = "192.168.10.21";
.port = "80";
}
backend appsrv2 {
.host = "192.168.10.22";
.port = "80";
}
sub vcl_init {
new imgsrvs = directors.random();
imgsrvs.add_backend(imgsrv1,10);
imgsrvs.add_backend(imgsrv2,20);
new staticsrvs = directors.round_robin();
appsrvs.add_backend(appsrv1);
appsrvs.add_backend(appsrv2);
new appsrvs = directors.hash();
appsrvs.add_backend(appsrv1,1);
appsrvs.add_backend(appsrv2,1);
}
sub vcl_recv {
if (req.url ~ "(?i)\.(css|js)$" {
set req.backend_hint = staticsrvs.backend();
}
if (req.url ~ "(?i)\.(jpg|jpeg|png|gif)$" {
set req.backend_hint = imgsrvs.backend();
} else {
set req.backend_hint = appsrvs.backend(req.http.cookie);
}
}
基于cookie的session sticky:
sub vcl_init {
new h = directors.hash();
h.add_backend(one, 1); // backend 'one' with weight '1'
h.add_backend(two, 1); // backend 'two' with weight '1'
}
sub vcl_recv {
// pick a backend based on the cookie header of the client
set req.backend_hint = h.backend(req.http.cookie);
}
设置后端的主机属性:
backend BE_NAME {
...
.connect_timeout = 0.5s; #超时时长
.first_byte_timeout = 20s; #发送报文时第一字节传输时长
.between_bytes_timeout = 5s;#从后端服务器接收报文超时时长,字节和字节之间传输间隔时间
.max_connections = 50; #与后端服务器最大并非连接
}
varnish的运行时参数:
线程模型:
cache-worker
cache-main
ban lurker
acceptor:
epoll/kqueue:
...
线程相关的参数:使用线程池机制管理线程;
在线程池内部,其每一个请求由一个线程来处理; 其worker线程的最大数决定了varnish的并发响应能力;
thread_pools:Number of worker thread pools. 最好小于或等于CPU核心数量;
thread_pool_max:The maximum number of worker threads in each pool. 每线程池的最大线程数;
thread_pool_min:The minimum number of worker threads in each pool. 额外意义为“最大空闲线程数”;
最大并发连接数 = thread_pools * thread_pool_max
thread_pool_timeout:Thread idle threshold. Threads in excess of thread_pool_min, which have been idle for at least this long, will be destroyed.
thread_pool_add_delay:Wait at least this long after creating a thread.
thread_pool_destroy_delay:Wait this long after destroying a thread.
Timer相关的参数:
send_timeout:Send timeout for client connections. If the HTTP response hasn't been transmitted in this many seconds the session is closed.
timeout_idle:Idle timeout for client connections.
timeout_req: Max time to receive clients request headers, measured from first non-white-space character to double CRNL.
cli_timeout:Timeout for the childs replies to CLI requests from the mgt_param.
设置方式:
vcl.param
param.set
永久有效的方法:
varnish.params
DEAMON_OPTS="-p PARAM1=VALUE -p PARAM2=VALUE"
varnish日志区域:
shared memory log
计数器
日志信息
1、varnishstat - Varnish Cache statistics
-1
-1 -f FILED_NAME
-l:可用于-f选项指定的字段名称列表;
MAIN.cache_hit
MAIN.cache_miss
# varnishstat -1 -f MAIN.cache_hit -f MAIN.cache_miss
显示指定参数的当前统计数据;
# varnishstat -l -f MAIN -f MEMPOOL
列出指定配置段的每个参数的意义;
2、varnishtop - Varnish log entry ranking
-1 Instead of a continously updated display, print the statistics once and exit.
-i taglist,可以同时使用多个-i选项,也可以一个选项跟上多个标签;
-I <[taglist:]regex>:对指定的标签的值基于regex进行过滤;
-x taglist:排除列表
-X <[taglist:]regex>:对指定的标签的值基于regex进行过滤,符合条件的予以排除;
3、varnishlog - Display Varnish logs
4、 varnishncsa - Display Varnish logs in Apache / NCSA combined log format #以ncsa(类似http协议日志)方式显示-D 以守护进程运行
varnishncsa -D -w /var/log/varnish_access.log #以守护进程方式启动varnish日志
systemctl start varnishncsa #以守护进程启动且剥离终端也不会被停止
内建函数:
hash_data():指明哈希计算的数据;减少差异,以提升命中率;
regsub(str,regex,sub):把str中被regex第一次匹配到字符串替换为sub;主要用于URL Rewrite
regsuball(str,regex,sub):把str中被regex每一次匹配到字符串均替换为sub;
return():
ban(expression)
ban_url(regex):Bans所有的其URL可以被此处的regex匹配到的缓存对象;
synth(status,"STRING"):生成响应报文;
5、LNMP结合varnish实现动静分离
docker pull duckll/lnmp #我这里拿一个做好的镜像提供lnmp架构
docker run -idt --name lnmp -p {port}:80 -v {diretory}:/home/wwwroot/default/{something} duckll/lnmp #启动容器,并指定端口及存储卷
vim /etc/varnish/default.vcl #修改配置文件添加后端主机并指定访问缓存规则
backend web {
.host = "172.17.0.2";
.port = "80";
}
backend lnmp {
.host = "172.17.0.5";
.port = "80";#
}
sub vcl_recv { #在recv 字段添加规则,将以php结尾请求的报文发送至web服务器,也就是上面我们定义好的后端服务器名称
if(req.url ~ "(?i)\.php$") {
set req.backend_hint = web; #设置backend_hint(后端服务器提示符)为backend名称即为web
else{
set req.backend_hint = default;
}
6、实现varnish对静态资源的缓存
使用容器模拟动态和静态服务器
静态 nginx 172.17.0.2
动态 php+apache 172.17.0.3
docker pull nginx
docker run --name web1 -d nginx
docker pull php:7.3-rc-apache
docker run --name web2 -d php:7.3-rc-apache #启动容器
docker exec -it web2 /bin/sh #连接容器
cd /var/www/html #进入目录
echo "<?php phpinfo(); ?>" >index.php
vim /etc/varnish/default.vcl #修改配置文件添加后端主机并指定访问缓存规则
backend web {
.host = "172.17.0.3";
.port = "80";
}
sub vcl_recv { #在recv 字段添加规则,将以php结尾请求的报文发送至web服务器,也就是上面我们定义好的后端服务器名称
if(req.url ~ "(?i)\.php$") {
set req.backend_hint = web; #设置backend_hint(后端服务器提示符)为backend名称即为web
else{
set req.backend_hint = default;
}
vcl.load test3 default.vcl 在命令行中装载此配置文件命名为test3
vcl.use test3 #切换到test3配置