WEB系统架构及cache基础
WEB系统架构的扩展:一个最简单的WEB系统,就是一台主机承担所有的功能,如以LAMP为例

随着用户访问量的增加,服务器端承压,响应缓慢,此时分析原因,开始扩展:
增加服务器,做负载均衡:

进一步,可能要考虑网站内容分别存放,如图片、.html、.css等静态内容与动态内容分离,数据库独立运行,然后为了加速,会增加缓存服务器,对某些内容进行缓存,数据库服务器会做成主从模式和读写模式,也可增加缓存。

一般的,服务器会先从缓存中请求响应,如果缓存中没有,由缓存服务器向后端服务器发起请求,获得响应内容后缓存,同时反馈给请求前端。
一个系统的性能瓶颈,一般是由于IO系统性能不足造成的,内存的读写速度要远高于磁盘IO的速度,在大规模并发访问的情景中,磁盘IO都会成为性能瓶颈,所以,不管是文件的读写,还是数据库的读写,解决瓶颈的一种方式就是增加中间的缓存服务器。然后是做负载均衡,增加服务器数量。
负载均衡器,可用的有lvs、nginx、HAProxy,缓存服务器,可以使用varnish、squid等,而一般缓存服务器也具有反向代理功能,需要在请求内容未命中时向后端服务器发起请求。
本次学习varnish缓存服务器。
之所以能够做缓存,是因为程序具有局部性: 时间局部性、空间局部性。
缓存一般是按照key-value,即键值对的方式保存的:
key是访问路径,由url进行hash后的值,所以查找速度快;
value是请求的内容值;
缓存的内容有命中率指标,即命中的请求与全部请求的比值:hit/(hit+miss)
缓存对象:具有生命周期
缓存空间耗尽:使用LRU(最近最少使用)原则,清理缓存的内容
不是所有内容都能缓存,对于用户私有数据一般不可缓存,即使要缓存,也要设置访问控制。
缓存的处理步骤(以WEB应用为例):
接收请求 --> 解析请求(提取请求的URL及各种首部) --> 查询缓存 --> 新鲜度检测 --> 创建响应报文 --> 发送响应 --> 记录日志
新鲜度检测机制:
过期日期:
HTTP/1.0:Expires: Thu, 18 Dec 2025 07:11:52 GMT,这是一个绝对时间
HTTP/1.1: Cache-Control:max-age,Cache-Control: no-cache, no-store, max-age=0
有效性再验证:revalidate
如果原始内容未改变,仅相应首部(不附带body部分),响应码304(Not Modified)
如果原始内容发生改变,则正常响应,响应码200;
如果原始内容消失,则响应404,此时缓存中的cache object也应该被删除;
条件式请求首部:
If-Modified-Since:基于请求内容的时间戳做验证;
If-Unmodified-Since:
If-Match:
If-None-Match:使用Etag判断,可能是校验码
Etag:校验码
Cache-Control = "Cache-Control" ":" 1#cache-directive
Cache-Control:各种指令
请求:
no-cache(不要缓存的实体,要求现在从WEB服务器去取)
max-age:(只接受 Age 值小于 max-age 值,并且没有过期的对象)
max-stale:(可以接受过去的对象,但是过期时间必须小于 max-stale 值)
min-fresh:(接受其新鲜生命期大于其当前 Age 跟 min-fresh 值之和的缓存对象)
响应:
public(可以用 Cached 内容回应任何用户)
private(只能用缓存内容回应先前请求该内容的那个用户)
no-cache(可以缓存,但是只有在跟WEB服务器验证了其有效后,才能返回给客户端)
max-age:(本响应包含的对象的过期时间)
ALL: no-store(不允许缓存)
常见的缓存服务解决方案:
varnish,squid
Varnish:
Varnish 是一款高性能且开源的反向代理服务器和 HTTP 加速器,varnish的系统架构:
varnish主要运行两个进程:Management进程和Child进程(也叫Cache进程)。
Varnish Architecture:

管理进程Management:主要负责从对应的配置文件(vcl)中读取配置完成系统配置,实现应用新的配置、编译VCL、监控varnish、初始化varnish以及提供一个命令行接口等。
Child(或叫做cache)进程包含多种类型的线程:
Acceptor线程:接收新的连接请求并响应;
Worker线程:child进程会为每个会话启动一个worker线程,因此,在高并发的场景中可能会出现数百个worker线程甚至更多;
Expiry线程:从缓存中清理过期内容;
Command line:通过命令行接收管理child的命令接口;
Storage/hashing:对缓存内容进行哈希和存储;
Log/stats:日志和状态信息;
Backend communication:与后端服务器通信;
日志:是Shared Memory Log,共享内存日志,大小一般为90MB,空间分文两部分:前一部分为计数器,后一部分为请求的相关数据;
varnish的配置文件vcl(Varnish Configuration Language)比较特殊,是一种DSL格式文件,DSL(Domain Specific Language,领域特定语言),其最终需要使用C编译器编译成二进制格式,是缓存策略配置接口,基于“域”的简单编程语言。
Varnish安装:
Varnish包在epel源中:



一个高并发的服务端程序,需要随时为客户的连接请求创建会话并维持会话,这就需要不断地申请内存空间、释放内存空间,而默认Linux内核是靠内核系统调用malloc()和free()实现的,性能不高。Varnish使用jemalloc(),更加高效。
Varnish的安装后文件:

varnish的启动文件是/usr/lib/systemd/system/varnish.service:


varnish如何存储缓存对象:三种方式,单个文件中,内存中,基于文件的持久存储
file:单个文件,重启后失效,不支持持久机制;所有对象缓存在一个文件中,文件本身有自己的数据结构,形成自己的文件系统,自己识别,独立的自治域,没有key值,所以重启失效。
malloc:内存中,速度快,空间受限,空间碎片化。
persistent:基于文件的持久存储,重启后不会丢失;
配置varnish的三种应用:
1、varnish应用程序的命令行参数,参看上面图片的varnish.service中的ExecStart
监听的socket,使用的存储类型等,在varnish.params中定义;额外的配置参数
-p param=value
-r param,param,...:设定只读参数列表;
2、-p选项指明的参数:是运行时参数
可在运行中,通过CLI进行配置;
3、vcl:配置缓存系统的缓存机制,/etc/varnish/default.vcl
通过vcl配置文件进行配置:需要先编译,后应用,依赖于c编译器。
修改缓存类型为内存:

vcl配置文件:

其中Backend default定义后端服务器,默认指向自身的8080端口,现在修改其他的服务器:
.host = "192.168.147.133";
.port = "80";
注意,因为vcl文件是类似C语言语法的,每条语句后面要加分号。
启动varnish:
systemctl start varnish.service

服务已经启动,测试访问:

访问成功,已经转发到133上,且内容应该已经缓存到varnish中。
Varnish工具:
varnishadm:varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082

不太好的一点是没有提示符。
使用help,查看子命令:

ping命令,探测服务进程状态:


varnishlog:查看共享内存日志
varnishncsa:以另一种格式显示日志
varnishtop:以排序方式显示的信息
varnishstat:状态信息
vcl配置文件:
varnish内部处理,有一个状态引擎处理流程,在不同的状态,进行不同的处理。

客户端请求报文request到达varnish,先由vcl_recv()接收,然后判断请求是否是可缓存的,如果不是可缓存的,那么直接转移到vcl_fetch(),由vcl_fetch()重新封装请求报文到后端服务器获取响应报文,然后转发给vcl_deliver(),由vcl_deliver()封装成新的响应报文Response返回给客户端;如果请求是可缓存的,则转移到vcl_hash(),计算key的哈希值,然后判断是否在缓存中命中,即缓存中是否有此请求所需的结果响应报文,如果没有命中,则转移至vcl_miss(),由vcl_miss()转移至vcl_fetch(),到后端服务器请求内容,然后缓存,然后转移至vcl_deliver(),封装成响应报文,返回给客户端;如果缓存命中,转移至vcl_hit(),在缓存中获取相应内容,然后直接转移至vcl_deliver(),封装为响应报文,反馈给客户端。
各引擎之间存在一定程度上的相关性;前一个engine如果有多种下游engine,则上游engine需要用return指明要转移的下游engine;
varnish还有几种状态引擎:vcl_pipe(),当请求无法被varnish所理解时,直接通过vcl_pipe()到后端服务器;vcl_pass(),当请求不能缓存时,直接通过vcl_pass(),再通过vcl_fetch()直接到后端服务器;如果出现错误,通过vcl_error(),再通过vcl_fetch()直接到后端服务器;
客户端请求到达varnish,会出现三种情况:pipe(不能理解的请求)、pass(不能缓存的请求)、lookup(可以缓存的请求)。

VCL用于定义缓存策略,而定义好的策略将由varnish的management进程分析、转换成C代码、编译成二进制程序并连接至child进程。varnish内部的各状态上可以附加通过VCL定义的策略以完成相应的缓存处理机制,因此VCL也经常被称作“域专用”语言或状态引擎,“域专用”指的是有些数据仅出现于特定的状态中。
VCL配置文件的编程语言语法:
1、注释语法://、#或/* comment */用于注释
2、由subroutine组成,即子例程构成,子例程的定义语法如下:
sub vcl_recv {
}
3、不支持循环
4、有众多内置的变量,变量的可调用位置与state engine有密切相关性;
5、支持终止语句,return(action);没有返回值;
6、“域”专用;
7、操作符:=(赋值)、==(等值比较)、~(模式匹配)、!(取反)、&&(逻辑与)、||(逻辑或)
判断语句:
if (CONDITION) {
} else {
}
变量赋值:
set name=value ; unset name
示例:

内置变量的使用,学过python的web编程,对这里的内置变量及其使用就会感觉很熟悉了。req相当于一个类,类中封装了各种数据,使用时用点号来取数据。req是请求报文相关数据,resp是响应报文相关数据,这两个是客户端的数据,还有一组是bereq和beresp,be是backend的意思。
新版的状态流程图:

vcl_purge()是清理缓存,清理单个目标,vcl_purge()后续是vcl_synth()。有些状态变了,如vcl_fetch()变成了vcl_backend_fetch()、vcl_backend_response()、vcl_backend_error()。
操作vcl配置文件,以/etc/varnish/default.vcl为模版:
拷贝default.vcl为test.vcl,修改:

即如果取得的对象为缓存中的,那么在响应报文首部增加X-Cache,其值为HIT,否则为MISS;
配置文件的加载与使用:


说明第一次缓存没有命中,第二次缓存命中。
Varnish中的内置变量:
变量种类:
client、 server、 req、 resp、 bereq、 beresp、 obj、 storage
client:封装有客户端的信息;
server:封装有varnish服务器本身的信息;
obj:从缓存或从后端服务器中获取的对象,其相关信息被封装于obj中;
storage:varnish缓存存储的相关信息;
测试server:

然后vcl.load test2 test.vcl; vcl.use test2

varnish服务器地址被添加上了。
强制某资源不从缓冲中获取:

url以/test6.html结尾的,不从缓存中获取,测试:

可以看到,多次请求都没有从缓存中获取。示例,对以/login或/admin开头的请求不从缓存中获取,不区分大小写,一般是登录请求:
if (req.url ~ "(?i)^/login" || req.url ~ "(?i)^/admin") {
return(pass); }
对特定类型的资源取消其私有的cookie标识,并强行设定其可以在varnish缓存的时长:
if (beresp.http.cache-control !~ "s-maxage") {
if (bereq.url ~ "(?i)\.jpg$) {
set beresp.ttl = 60s;
unset beresp.http.Set-Cookie;
}
}
backend server的定义:
backend name {
.attribute = "value";
}
.host:be主机的IP
.port:be主机监听的端口
.probe:对be做健康状态检测
.max_connections:并发连接的最大数量
后端主机的健康状态检测方式:
.probe中的探测指令常用的有:
(1) .url:探测后端主机健康状态时请求的URL,默认为“/”;
(2) .request: 探测后端主机健康状态时所请求内容的详细格式,定义后,它会替换.url指定的探测方式;比如:
.request =
"GET /.healthtest.html HTTP/1.1"
"Host: www.test.com"
"Connection: close";
(3) .window:设定在判定后端主机健康状态时基于最近多少次的探测进行,默认是8;
(4) .threshold:在.window中指定的次数中,至少有多少次是成功的才判定后端主机正健康运行;默认是3;
(5) .initial:Varnish启动时对后端主机至少需要多少次的成功探测,默认同.threshold;
(6) .expected_response:期望后端主机响应的状态码,默认为200;
(7) .interval:探测请求的发送周期,默认为5秒;
(8) .timeout:每次探测请求的过期时长,默认为2秒;

定义了两个backend server,同时定义了健康检测,并且实现对gif类资源与其他资源请求的分离。
定义后端服务器集群:

此时访问不同的页面,会轮询的在两个服务器中切换,达到轮询的效果,一旦访问了一个页面,再次访问时,因为缓存,都是同一个服务器的页面。

负载均衡算法:
fallback、random、round_robin、hash
668

被折叠的 条评论
为什么被折叠?



