代理服务器(Proxy Server)是一种重要的服务器安全功能,它的工作主要在开放系统互联(OSI)模型的会话层,从而起到防火墙的作用。代理服务器大多被用来连 INTERNET(国际互联网)和Local Area Network(局域网)。代理(英语:Proxy),也称网络代理,是一种特殊的网络服务,允许一个网络终端(一般为客户端)通过这个服务与另一个网络终端(一般为服务器)进行非直接的连接。实现一些网关、路由器等网络设备具备网络代理功能。一般认为代理服务有利于保障网络终端的隐私或安全,防止攻击。
(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给 internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。当一个代理服务器能够代理外部网络上的主机,访问内部网络时,这种 代理服务的方式称为反向代理服务。此时代理服务器对外就表现为一个Web服务器,外部网络就可以简单把它当作一个标准的Web服务器而不需要特定的配置。 不同之处在于,这个服务器没有保存任何网页的真实数据,所有的静态网页或者CGI程序,都保存在内部的Web服务器上。因此对反向代理服务器的攻击并不会使得网页信息遭到破坏,这样就增强了Web服务器的安全性。
即内容分发网络。其基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。通过在网络各处放置反向代理节 点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络,CDN系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综 合信息将用户的请求重新导向离用户最近的服务节点上。其目的是使用户可就近取得所需内容,解决 Internet网络拥挤的状况,提高用户访问网站的响应速度。
有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个 实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有 的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也 有就绪、阻塞和运行三种基本状态。就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;运行状态是指线程占有处理机正在运行;阻塞状态是 指线程在等待一个事件(如某个信号量),逻辑上不可执行。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。
web缓存,是一种缓存技术,用于临时存储(缓存)的网页文件,如HTML页面和图像等静态资源 (此处不绝对,也可以缓存动态页面,但是存储到本地后也为静态文件),减少带宽以及后端服务器的压力,通常一个Web Cache也是一个反向代理软件,既 可以通过缓存响应用户的请求,当本地没有缓存时,可以代理用户请求至后端主机
WebCache的由来:
由于程序具有局部性,而局部性分为:时间局部性和空间局部性
(1)时间局部性是指:在单位时间内,大部分用户访问的数据只是热点数据(热点数据指经常被访问的数据)
(2)空间局部性是指:比如,某新闻网站突然出来一个重大新闻,此新闻会被被反复访问
Varnish Configuration Language (VCL)是varnish配置缓存策略的工具,它是一种基于“域”(domain specific)的简单编程语言,它支持有限的算术运算和逻辑运算操作、允许使用正则表达式进行字符串匹配、允许用户使用set自定义变量、支持if判 断语句,也有内置的函数和变量等。使用VCL编写的缓存策略通常保存至.vcl文件中,其需要编译成二进制的格式后才能由varnish调用。事实上,整 个缓存策略就是由几个特定的子例程如vcl_recv、vcl_fetch等组成,它们分别在不同的位置(或时间)执行,如果没有事先为某个位置自定义子 例程,varnish将会执行默认的定义。
VCL 策略在启用前,会由management进程将其转换为C代码,而后再由gcc编译器将C代码编译成二进制程序。编译完成后,management负责将 其连接至varnish实例,即child进程。正是由于编译工作在child进程之外完成,它避免了装载错误格式VCL的风险。因此,varnish修 改配置的开销非常小,其可以同时保有几份尚在引用的旧版本配置,也能够让新的配置即刻生效。编译后的旧版本配置通常在varnish重启时才会被丢弃,如 果需要手动清理,则可以使用varnishadm的vcl.discard命令完成。
VCL的设计参考了C和Perl语言,因此,对有着C或Perl编程经验者来说,其非常易于理解。其基本语法说明如下:
我们一般需要关注的文件类的内核参数有fs.file-max和file-nr。fs.file-max是一个系统能申请到的最大文件句柄数;file-nr是只读的,它与file-max相关,仅用于显示已分配文件句柄的数目、已使用文件句柄的数目、文件句柄的最大数目。
[root@server1 varnish]# sysctl -a |grep file
fs.file-nr = 704 0 98863
fs.file-max = 98863
#查看/proc/sys/fs/file-max文件查看最大打开文件数是否正常设置
[root@server1 varnish]# cat /proc/sys/fs/file-max
98863
#在/etc/security/limits.conf中直接添加如下内容:
[root@server1 varnish]# vim /etc/security/limits.conf
* - nofile 2048
置了每个用户的默认打开文件数为2048。 注意”nofile”项有两个可能的限制措施。就是项下的hard和soft。要使修改过的最大打开文件数生效,必须对这两种限制进行设定。如果使用”-“字符设定, 则hard和soft设定会同时被设定。硬限制表明soft限制中所能设定的最大值。soft限制指的是当前系统生效的设置值。hard限制值可以被普通用户降低。但是不能增加。soft限制不能设置的比hard限制更高。只有root用户才能够增加hard限制值。
注意:这里修改即生效
注意:Varnish依赖“工作区(workspace)”以降低线程在申请或修改内存时出现竞争的可能性。在varnish内部有多种不同的工作区,其中最关键的当属用于管理会话数据的session工作区。
varnish支持多种不同类型的后端存储,这可以在varnishd启动时使用-s选项指定。后端存储的类型包括:
persistent(experimental):与file的功能相同,但可以持久存储数据(即重启varnish数据时不会被清除);仍处于测试期;
Varnish无法追踪某缓存对象 是否存入了缓存文件,从而也就无从得知磁盘上的缓存文件是否可用,因此,file存储方法在varnish停止或重启时会清除数据。而 persistent方法的出现对此有了一个弥补,但persistent仍处于测试阶段,例如目前尚无法有效处理要缓存对象总体大小超出缓存空间的情 况,所以,其仅适用于有着巨大缓存空间的场景。
选择使用合适的存储方式有助于提升系统性,从经验的角度来看,建议在内存空间足以存储所有的缓存对象时使用malloc的方法,反之,file存储将有着更 好的性能的表现。然而,需要注意的是,Varnishd实际上使用的空间比使用-s选项指定的缓存空间更大,一般说来,其需要为每个缓存对象多使用差不多 1K左右的存储空间,这意味着,对于100万个缓存对象的场景来说,其使用的缓存空间将超出指定大小1G左右。另外,为了保存数据结构等,varnish 自身也会占去不小的内存空间。
为varnishd指定使用的缓存类型时,-s选项可接受的参数格式如下:
malloc[,size] 或 file[,path[,size[,granularity]]] 或 persistent,path,size {experimental}
注:file中的granularity用于设定缓存空间分配单位,默认单位是字节,所有其它的大小都会被圆整。
说到varnish的状态引擎,不得不说vcl(Varnish Configuration Language:varnish配置缓存策略的工具)。它是基于域的一种简单的编程语言,支持算数运算、允许使用正则表达式、支持if语句等。使用 vcl语言编写的缓存策略通常保存于.vcl文件中,其需要编译成二进制的格式后才能由varnish调用。
VCL用于让管理员定义缓存策略,而定义好的策略将由varnish的management进程分析、转换成C代码、编译成二进制程序并连接至 child进程。varnish内部有几个所谓的状态(state),在这些状态上可以附加通过VCL定义的策略以完成相应的缓存处理机制,因此VCL也 经常被称作“域专用”语言或状态引擎,“域专用”指的是有些数据仅出现于特定的状态中。
Varnish与一般服务器软件类似,分为master进程和child(worker,主要做cache的工作)进程。master进程读入命令,进行 一些初始化,然后fork并监控child进程。child进程分配若干线程进行工作,主要包括一些管理线程和很多woker线程。针对文件缓存部 分,master读入存储配置,调用合适的存储类型,然后创建/读入相应大小的缓存大文件。接着,master初始化管理该存储空间的结构体。这些变量都 是全局变量,在fork以后会被child进程所继承(包括文件描述符)。在child进程主线程初始化过程中,将前面打开的存储大文件整个mmap到内 存中(如果超出系统的虚拟内存,mmap失败,进程会减少原来的配置mmap大小,然后继续mmap),此时创建并初始化空闲存储结构体,挂到存储管理结 构体,以待分配。接着,真正的工作开始,Varnish的某个负责接受新HTTP连接的线程开始等待用户,如果有新的HTTP连接过来,它总负责接收,然 后叫醒某个等待中的线程,并把具体的处理过程交给它。Worker线程读入HTTP请求的URI,查找已有的object,如果命中则直接返回并回复用 户。如果没有命中,则需要将所请求的内容,从后端服务器中取过来,存到缓存中,然后再回复。分配缓存的过程是这样的:它根据所读到object的大小,创 建相应大小的缓存文件。为了读写方便,程序会把每个object的大小变为最接近其大小的内存页面倍数。然后从现有的空闲存储结构体中查找,找到最合适的 大小的空闲存储块,分配给它。如果空闲块没有用完,就把多余的内存另外组成一个空闲存储块,挂到管理结构体上。如果缓存已满,就根据LRU[4]机制,把 最旧的object释放掉。释放缓存的过程是这样的:有一个超时线程,检测缓存中所有object的生存期,如果超初设定的TTL(Time To Live)没有被访问,就删除之,并且释放相应的结构体及存储内存。注意释放时会检查该存储内存块前面或后面的空闲内存块,如果前面或后面的空闲内存和该 释放内存是连续的,就将它们合并成更大一块内存。整个文件缓存的管理,没有考虑文件与内存的关系,实际上是将所有的object都考虑是在内存中,如果系 统内存不足,系统会自动将其换到swap空间,而不需要varnish程序去控制。
首先用户请求到达后,首先进入vcl_recv,vcl_recv对其做判断,是否命中缓存(vcl_hash),如果不想使用缓存则直接交由vcl_pipe,建立管道并交由后端服务器;如果期望本地缓存处理则自定义检测缓存lookup,很显然,如果要检查缓存是需要根据什么方式做检查,判断缓存中是否存在对象 ,如果命中了yes 于是交予vcl_hit,就算命中了也有两条路可以走:
1) deliver 直接由vcl_deliver在缓存中取出直接返回至用户
2) 如果命中了交予给vcl_pass 通过自行手动控制了到后端缓存中去取的数据,有些时候有独特的控制机制,而vcl_miss也可以交由vcl_pass来处理.
而为什么使用pass,如果我们期望处理缓存的,比如要清理缓存,缓存中的内容找到则清理,如果没有找到则通过pass做一些处理.仅仅是提供用户编辑一些规则的而已,如果未命中,很先让必然要到后端去取vcl_fatc,取完之后是否缓存下来就是在fatch中定义的,如果要缓存就先放着cache中,如果不想缓存则Dont’Cache,最后再响应至客户端
因此用户请求到达varnish之后,varnish大致要经过以上的处理阶段,而每个处理阶段要自定义处理规则对其做出处理,而有些功能只能在后端实现,有些只能在前端,不同的规则要在不同的位置实现的
vcl_recv是在Varnish完成对请求报文的解码为基本数据结构后第一个要执行的子例程,它通常有四个主要用途:
可以使用下面的终止语句,即通过return()向Varnish返回的指示操作:
vcl_recv也可以通过精巧的策略完成一定意义上的安全功能,以将某些特定的攻击扼杀于摇篮中。同时,它也可以检查出一些拼写类的错误并将其进行修正等。
Varnish默认的vcl_recv专门设计用来实现安全的缓存策略,它主要完成两种功能:
Varnish是一个轻量级的Cache和反向代理软件。先进的设计理念和成熟的设计框架是Varnish的主要特点。现在的Varnish总共代码量不大,虽然功能在不断改进,但是还需要继续丰富和加强。下面总结了Varnish的一些特点:
Varnish的优点
Varnish相对于squid的缺点
配置varnish服务端
[root@server1 sysconfig]# vim /etc/sysconfig/varnish
VARNISH_VCL_CONF=/etc/varnish/default.vcl
VARNISH_LISTEN_PORT=80
[root@server1 sysconfig]# vim /etc/varnish/default.vcl
backend default {
.host = "172.25.254.2";
.port = "80";
}
[root@server1 sysconfig]# /etc/init.d/varnish start
配置后端web服务器
[root@server2 ~]# yum install httpd
[root@server2 ~]# echo server2 >> /var/www/html/index.html
[root@server2 ~]# /etc/init.d/httpd start
[root@server3 ~]# curl -I 172.25.254.1
HTTP/1.1 200 OK
Server: Apache/2.2.15 (Red Hat)
Last-Modified: Mon, 18 Dec 2017 16:40:15 GMT
ETag: "60bf3-8-560a0013b7d1b"
Content-Type: text/html; charset=UTF-8
Content-Length: 8
Accept-Ranges: bytes
Date: Mon, 18 Dec 2017 16:57:54 GMT
X-Varnish: 1374727105 1374727102
Age: 63
Via: 1.1 varnish
Connection: keep-alive
[root@server1 sysconfig]# vim /etc/varnish/default.vcl
sub vcl_deliver {
if (obj.hits > 0) {
set resp.http.X-Cache = "HIT from server1 cache";
}
else {
set resp.http.X-Cache = "MISS from server1 cache";
}
return (deliver);
}
[root@server1 sysconfig]# /etc/init.d/varnish restart
backend web1 {
.host = "172.25.254.3";
.port = "80";
}
backend web2 {
.host = "172.25.254.2";
.port = "80";
}
sub vcl_recv {
if (req.http.host ~ "^(www.)?westos.org") {
set req.http.host = "www.westos.org";
set req.backend = web1;
}
elsif (req.http.host ~ "^bbs.westos.org") {
set req.backend = web2;
}
else {
error 404 "westos cache";
}
}
修改本地hosts解析
[root@foundation day01 varniesh]# vim /etc/hosts
172.25.254.1 www.westos.org bbs.westos.org
#把多个后端聚合为一个组,并检测后端健康状况
director lb round-robin {
{ .backend = web1; .weight=1}
{.backend = web2;}
}
#定义健康检查
probe healthcheck {
.url = "/index.html"; # 哪个 url 需要 varnish 请求
.interval = 5s; #检查的间隔时间
.timeout = 1s; #等待多长时间探针超时
.window = 5; #设定在判定后端主机健康状态时基于最近多少次的探测进行,默认是8;
.threshold = 3; #至少有三次 window 是成功的,就宣告 bachend 健康
}
sub vcl_recv {
if (req.http.host ~ "^(www.)?westos.org") {
set req.http.host = "www.westos.org";
set req.:qbackend = lb;
return (pass);
#为了测试方便,不进行缓存,定义了负载均衡。
}
elsif (req.http.host ~ "^bbs.westos.org") {
set req.backend = web2;
}
else {
error 404 "westos cache";
}
}
#注意:这里必须使用域名进行访问,否则访问不到网页
ban()是一种从已缓存对象中过滤(filter)出某此特定的对象并将其移除的缓存内容刷新机制,不过,它并不阻止新的内容进入缓存或响应于请求。在 Varnish中,ban的实现是指将一个ban添加至ban列表(ban-list)中,这可以通过命令行接口或VCL实现,它们的使用语法是相同的。 ban本身就是一个或多个VCL风格的语句,它会在Varnish从缓存哈希(cache hash)中查找某缓存对象时对搜寻的对象进行比较测试,因此,一个ban语句就是类似匹配所有“以/downloads开头的URL”,或“响应首部中 包含nginx的对象”。
定义好的所有ban语句会生成一个ban列表(ban-list),新添加的ban语句会被放置在列表的首部。缓存中的所有对象在响应给客户端之前都会被 ban列表检查至少一次,检查完成后将会为每个缓存创建一个指向与其匹配的ban语句的指针。Varnish在从缓存中获取对象时,总是会检查此缓存对象 的指针是否指向了ban列表的首部。如果没有指向ban列表的首部,其将对使用所有的新添加的ban语句对此缓存对象进行测试,如果没有任何ban语句能够匹配,则更新ban列表。
对ban这种实现方式持反对意见有有之,持赞成意见者亦有之。反对意见主要有两种,一是ban不会释放内存,缓存对象仅在有客户端访问时被测试一次;二是 如果缓存对象曾经被访问到,但却很少被再次访问时ban列表将会变得非常大。赞成的意见则主要集中在ban可以让Varnish在恒定的时间内完成向 ban列表添加ban的操作,例如在有着数百万个缓存对象的场景中,添加一个ban也只需要在恒定的时间内即可完成。其实现方法本处不再详细说明。
[root@server1 html]# yum install php -y
[root@server1 html]# unzip bansys.zip -d /var/www/html/
[root@server1 html]# vim /etc/httpd/conf/httpd.conf
Listen 8080
[root@server1 bansys]# pwd
/var/www/html/bansys
[root@server1 bansys]# vim config.php
$var_group1 = array(
'host' => array('172.25.254.1'),
'port' => '80',
);
$VAR_CLUSTER = array(
'www.westos.org' => $var_group1,
);
[root@server1 bansys]# vim /etc/varnish/default.vcl
sub vcl_recv {acl list {
"127.0.0.1";
"172.25.254.0"/24;
}
#定义批量刷新
if (req.request == "BAN") {
if (!client.ip ~ list) {
error 405 "Not allowed.";
}
ban("req.url ~ " + req.url);
error 200 "ban added";
}
}
#注意:bansys 有两种工作模式,分别是:telnet 和 http 模式。
telnet 模式需要关闭 varnish 服务管理端口的验证,注释掉/etc/sysconfig/varnish 文件中的“-S ${VARNISH_SECRET_FILE}”这行,重启 varnish 服务即可。这里我们使用http模式。