什么是缓存代理服务器
缓存代理服务器是一种特殊的代理服务器,其主要功能是缓存从目标服务器(通常是Web服务器)获取的数据,并在客户端再次请求相同数据时直接提供缓存的数据。通过缓存代理服务器可以加快访问速度并减轻目标服务器的负载。
缓存代理服务器的工作原理是:
1、当客户端请求访问某个资源时,缓存代理服务器首先检查自己的缓存中是否已经保存了该资源的副本。
2、如果缓存中有该资源的副本,则直接返回给客户端。
3、如果缓存中没有该资源的副本,则缓存代理服务器会向目标服务器请求该资源,并将其保存在缓存中,然后再返回给客户端。
4、下次客户端再次请求相同的资源时,就可以直接从缓存中获取,而不需要再次向目标服务器请求。
当然,缓存代理服务器可以配置需要缓存的数据,比如将获取的网页数据(静态Web元素,如图片、CSS、JavaScript等)保存到缓存中。
使用缓存代理服务器,可以减少向目标服务器发送的请求数量,减轻目标服务器的负载,降低网络带宽的消耗,提供用户的访问速度。同时,还可以隐藏用户的真实IP地址和其他相关信息,增加了一定的匿名性和安全性。
但是在使用缓存代理服务器时,需要注意以下几点:
- 缓存的时效性和更新:由于缓存代理服务器存储的是目标服务器上的数据副本,因此需要定期或根据特定策略更新缓存中的数据,以确保数据的准确性和新鲜度。
- 缓存策略:缓存代理服务器可以根据不同的策略来决定哪些数据应该被缓存、缓存多长时间以及如何更新缓存。这些策略可以根据实际需求进行配置和调整。
常用的缓存代理服务器
目前较常用的缓存代理服务器主要是这几款:
Squid
大概在2010年之前,各大CDN厂商基本都在使用Squid。但因为历史久远,很多近10年左右流行起来的系统特性,Squid本身并不支持,如sendfile、splice和多核等方面的支持不足。Squid具备如下特点:
- 可运行在大多数Unix和OS/2版本的系统之上,包括Windows、Linux等。
- 支持HTTP、HTTPS、FTP等协议。
- 广泛应用于缓存和过滤网络流量场景,具有配置简单、效率高、功能丰富等特点。
- 能基于访问控制列表(ACL)和访问权限列表(ARL)执行内容过滤与权限管理功能,可以保护企业内网的安全。
Varnish
Varnish是一种高性能的HTTP加速和反向代理服务器软件,常用于缓存和加速Web应用。相较于Squid,Varnish更侧重于纯内存缓存方案,但需要注意的是,它不支持HTTPS。
Varnish因为只支持内存缓存,所以使用的场景比较有限。目前使用的都是一些小站,或者说热点很集中、缓存总量不大的业务场景。
Nginx Cache
Nginx是一种轻量级的Web服务器和反向代理服务器软件,也可以作为缓存代理服务器使用。它支持HTTP、HTTPS等协议,并具有负载均衡、反向代理、缓存和高可靠性等特性。Nginx的缓存功能虽然不如Squid和Varnish那样专注,但得益于其强大的性能,仍然是一个不错的选择。
Apache Traffic Server(ATS)
Apache Traffic Server是一个高性能的、模块化的HTTP代理和缓存服务器。Apache Traffic Server最初是Inktomi公司的商业产品,该公司在2003年被Yahoo收购。2009年8月,Yahoo将Apache Traffic Server的源代码贡献给了Apache软件基金会(ASF)。2010年4月,Apache Traffic Server成为了ASF的顶级项目。
ATS和Squid很有渊源,领导开发的Chuck Neerdaels、Leif Hedstrom都曾经是Harvest项目的早期创始人之一,Harvest项目后来发展为Squid项目。所以很多Squid中不完善的功能,在ATS中得到了完善和强化。
ATS具备如下显著特性:
- 高性能:Apache Traffic Server能够在处理TB级数据的真实部署环境中提供强大的处理能力。其最新版本每秒钟已经可以处理200,000多个请求,相比之前版本性能有显著提升。
- 模块化设计:其设计考虑了可扩展性,通过插件接口,开发者能够轻松添加新功能,实现特定业务需求。
- 缓存:通过缓存并重用经常请求的网页、图片和Web Service调用,改进响应时间的同时降低了服务器负载与对带宽的需求。
- 代理:易于添加持续连接、过滤器或异步内容请求,也可以通过添加代理层实现负载平衡。
- 可扩展性:API考虑到了自定义插件,可以修改头与内容,还可以实现新的协议处理器。
- 可靠性:能够完美处理TB级别的数据,包括正向与反向代理。
运行机制
在使用缓存代理服务的场景中,Nginx的使用场景最为广泛,其配置简单、性能优异的特点是首选的考虑要点。ATS以高性能和可靠性著称,普遍用于CDN场景。不同的使用场景是由不同的运行机制决定的,接下来就从运行机制上对比分析下两者。
Nginx缓存服务
从实现角度来讲,Nginx从上游服务器获取到要缓存的文件后,需要在内存中维护一份对应的元数据,并且在磁盘上保存文件完整数据。同时Nginx需要对两者进行有效的管理,比如维护两者的一致性,在一定条件下删除某些文件等。
Nginx的缓存功能主要包括以下四个功能:
1、管理缓存:维护数据的一致性,定期删除过期缓存文件或者强制删除某些缓存文件来释放磁盘空间。
2、加载缓存:在Nginx启动时,如果对应目录中存在缓存文件,则需要生成对应内存中的文件元数据。
3、生成缓存:从上游获取文件时,如果需要缓存则创建内存中的元数据以及对应磁盘中的文件。
4、使用缓存:新的请求到来时,如果有对应的缓存文件可用,则直接使用磁盘中的文件返回。
配置缓存功能后,Nginx就会启动Cache Manager进程和Cache Loader进程分别用来管理缓存数据和初始加载缓存数据。当缓存功能就绪后,缓存的生成和使用会由Worker进程在处理HTTP请求时完成。
Cache Manager是一个常驻进程,它周期性地运行来淘汰过期缓存或者强制删除某些缓存文件释放磁盘空间。
与Cache Manager不同,Cache Loader进程只在启动时运行一次,完成任务后就退出。Nginx 启动1分钟之后,Cache Loader进程生成现有的缓存文件的元数据并且加载到共享内存区域中。
接下来,我们开始分析两个缓存进程的运行逻辑:
Cache Manager进程
Cache Manager进程主要负责清理过期缓存以及强制清理部分缓存来释放磁盘空间。
Cache Manager进程的启动和运行逻辑:
1、配置解析完成后,Nginx的Master进程会触发启动Cache Manager和Cache Loader进程。
2、Cache Manager进程逻辑主体会遍历全局变量ngx_cycle中的paths数组,然后执行数组元素指定的回调函数完成缓存删除工作,并且按照一定算法计算Cache Manager进程下次运行的时间:
(1)、找到缓存对应的共享内存区域后通过lock函数进行同步锁定。
(2)、查找共享内存区域中LRU链表。如果链表为空,则表示当前缓存没有文件进行处理,则直接退出。
(3)、从LRU链表尾部开始遍历,如果有对应的缓存文件已经过期(自上次最近使用时间已经超过inactive时间),并且文件没有被使用,则把缓存文件的元数据信息从共享内存区域删除。然后找到对应的磁盘文件一并删除。
(4)、遍历处理过程中,如果处理的文件数量超过配置的manager_files数量或者本次运行的时间超过配置的manager_threshold,则本次运行结束。
(5)、根据各种情况计算下次Cache Manager运行的时间。比如有文件已经过期但是还在被使用,则下次运行的时间就需要相应变短。
Cache Loader进程
Cache Loader进程只在Nginx启动期间运行一次。它一般是在Nginx启动60秒以后开始运行,遍历缓存目录下存在的文件,然后生成内存中文件对应的元数据。当处理完所有的磁盘的文件后,Cache Loader进程就会退出。
Cache Loaader进程的启动和运行逻辑:
1、配置解析完成后,Nginx的Master进程会触发启动Cache Manager和Cache Loader进程。
2、Cache Loader进程逻辑主体遍历全局变量ngx_cycle中的paths数组,然后执行数组元素指定的loader回调函数:
(1)、进程把磁盘文件的元数据信息加入到缓存目录对应的共享目录内存区域。首先检查缓存文件的有效性(文件名长度是否符合规则、文件大小是否满足最小缓存文 件大小要求等)。
(2)、将缓存文件的元数据信息加入到对应的缓存管理机制中。通过节点的key值检查文件是否已经在共享内存中存在。如果不存在则生成一个新的节点进行保存。如果已经存在就先把节点从LRU链表中移除,再根据inactive参数更新节点的过期时间然后在节点加入到LRU链表的头部。
一个缓存配置示例,供参考:
user nginx; events{ worker_connections 1024; } http { #设置缓存路径和相关参数(必选) proxy_cache_path /tmp/nginx/cache levels=1:2 keys_zone=mycache:10m max_size=10g; server { listen 80; location /cache { proxy_pass http://192.168.1.135:8080;
#引用缓存配置(必选) proxy_cache mycache; #对响应状态码为200 302的响应缓存100s proxy_cache_valid 200 302 100s; #对响应状态码为404的响应缓存200 proxy_cache_valid 404 200s; #请求参数带有nocache或者comment时不使用缓存 proxy_cache_bypass $arg_nocache $arg_comment; #忽略被代理服务器设置的"Cache-Control"头信息 proxy_ignore_headers "Cache-Control"; #对GET HEAD POST方法进行缓存 proxy_cache_methods GET HEAD POST; #当缓存过期时,当构造上游请求时,添加If-Modified-Since和If-None-Match头部,值为过期缓存中的Last-Modified值和Etag值。 proxy_cache_revalidate on; #当被代理服务器返回403时,nginx可以使用历史缓存来响应客户端,该功能在一定程度上能能够为客户端提供不间断访问 proxy_cache_use_stale http_403; #默认开启,开启代理缓冲区(内存) proxy_buffering on; #设置响应头的缓冲区设为8k proxy_buffer_size 8k; #设置网页内容缓冲区个数为8,单个大小为8k proxy_buffers 8 8k; #设置当nginx还在读取被代理服务器的数据响应的同时间一次性向客户端响应的数据的最大为16k proxy_busy_buffers_size 16k; #临时文件最大为1024m proxy_max_temp_file_size 1024m; #设置一次往临时文件的大小最大为16k proxy_temp_file_write_size 16k; #设置临时文件存放目录 proxy_temp_path /tmp/proxy_temp; #设置和被代理服务器连接的超时时间为60s proxy_connect_timeout 60; #设置向被代理服务器发送请求的超时时间为60s proxy_send_timeout 60; #设置从被代理服务器读取响应的超时时间为60s proxy_read_timeout 60; #添加缓存状态参数,方便测试是否命中缓存 add_header cache $upstream_cache_status; } }
}
ATS实现机制
Apache Traffic Server 不同于大部分开源代理服务器,它结合了两种技术来处理高并发场景:
- 异步事件处理(Asynchronous event processing)。
- 多线程(Multi-threading)。
Traffic Server 在多 CPU、多核的硬件上扩展良好,能充分利用所有可用的 CPU 和其他资源。
Apache Traffic Server(ATS或TS)包含如下组件:
Traffic Server缓存
TS 缓存包含一个高速的对象数据库,数据库根据 URL 和相关头部来索引对象。当缓存空间满后,TS 会移除过期的数据。当磁盘出错时,TS 将不再使用该块磁盘,转而使用剩下的磁盘。所有磁盘都出错时,TS 将切换至 proxy-only 模式,即只代理,不缓存。
1、RAM 缓存:内存缓存区储存比较热门的对象,在流量的高峰期时能加快处理速度和降低磁盘负载。
2、主机数据库:储存 DNS 信息,方便主机名到 IP 地址的快速转换。储存每个主机的 HTTP 版本,方便高级协议特性的使用。
3、DNS 解析器:TS 原生实现了 DNS 解析器,不依赖较慢的传统解析库。同时也降低了 DNS 的流量。
Traffic Server 进程
1、traffic_server进程负责接受连接,处理协议请求,然后从缓存或源服务器获取对象并返回。
2、traffic_manager进程是 TS 的命令和控制设施,负责启动、监控和配置 traffic_server 进程,它也负责代理的端口配置、统计信息的接口、集群管理和虚拟 IP 的故障转移。如果 traffic_manager 检测到 traffic_server 进程失效,它立即重启 traffic_server 进程并且维护一个连接队列,保存此时到来的请求,完全重启后这个队列里的连接将按顺序被处理。
3、traffic_cop 进程监视 traffic_server 和 traffic_manager 进程,此进程周期性的查询 traffic_server 和 traffic_manager 进程的健康状况,如果查询在一定间隔时间内未返回或者返回信息不正确,traffic_cop 将重启 traffic_manager 和 traffic_server 进程。
管理工具
1、Traffic Line 是命令行程序,可以用来快速监视 Traffic Server 的性能和网络流量,也能配置 TS。
2、Traffic Shell 也是命令行工具,进入该 shell 后有自己一套语法,可代替 Traffic Line 完成监控、配置任务。
缓存机制
Traffic Server 处理请求的过程为:
1、用户请求一个 Web 对象,TS 收到请求。
2、TS 通过对象的地址,在对象数据库(缓存)中去定位该对象。
(1)、如果对象在缓存中,TS 会检查对象是否新鲜(fresh):
A、如果新鲜,TS 从缓存里返回该对象给用户,此时称为缓存命中(cache hit)。
B、如果不新鲜(stale),TS 会连接源服务器去验证对象是否仍然新鲜,即重新验证(revalidation),如果仍然新鲜,TS 立即将缓存中的副本返回给用户。
(2)、如果对象不在缓存中(缓存未命中,cache miss),或者缓存的副本不再有效,TS 会去源服务器获取对象,然后同时做下面两件事:
A、将对象返回给用户。
B、将对象放到本地缓存中。
Traffic Server 判断 HTTP 对象是否新鲜(fresh)的过程:
1、如果有 Expires 或者 max-age 头部直接定义缓存的过期时间,TS将对比当前时间和过期时间去判断对象是否新鲜。
2、如果没有上述头部,TS 将检查 Last-Modified 和 Date 头部(其中Date是源服务器返回对象的时间,如果没有 Last-Modified 头部,TS 会用对象写入缓存的时间以作代替),然后用以下公式算出新鲜的时间范围(freshness_limit,可理解为保质期):
# 0.1 这个参数可以作调整,并且能限制 freshness_limit 的上下限,默认最小是 1 小时,最大是 1 天
freshness_limit = ( Date - Last-Modified ) x 0.1
3、如果没有 Expires 头部或者没有 Last-Modified、Date 头部,TS 将使用默认的 fressness limit。
4、另外,TS 还会检查 cache.config 配置文件中的 revalidate 规则,该规则可以对特定的 HTTP 对象设置特定的验证时间(特定的域名、IP、一定规则的 URL、特定的客户端等等)。
缓存过期(stale),Traffic Server 去源服务器重新验证对象可能的情况:
1、仍然 fresh,TS 重置 freshness_limit,并返回对象。
2、对象新副本可用,TS 缓存新对象,并同时返回给用户。
3、源服务器上的对象不再存在,TS 也不再返回该副本给用户。
4、源服务器没有响应,TS 返回过期的对象并发出警告。
高可用对比
我们对比下Nginx和ATS的高可用模型。
Nginx 的进程模型采用的是prefork方式,预先分配的worker子进程数量由配置文件指定,默认为1,不超过1024。master主进程创建监听套接口,fork子进程以后,由worker进程监听客户连接,每个worker子进程独自尝试accept已连接套接口,accept是否上锁可以配置,默认会上锁,如果操作系统支持原子整型,才会使用共享内存实现原子上锁,否则使用文件上锁。不使用锁的时候,当多个进程同时accept,当一个连接来的时候多个进程同时被唤起,会导致惊群问题。使用锁的时候,只会有一个worker阻塞在accept上,其他的进程则会不能获取锁而阻塞,这样就解决了惊群的问题。master进程通过socketpair向worker子进程发送命令,终端也可以向master发送各种命令,子进程通过发送信号给master进程的方式与其通信,worker之间通过unix套接口通信。
模型如下图:
Traffic Server采用多线程异步事件处理。traffic_cop和traffic_manager为管理进程,工作进程为traffic_server。traffic_server负责listen和accept,为提高性能,traffic_server使用了异步I/O和多线程技术。Traffic Server并不是为每个连接都建立一个线程,而是事先创建一组数量可配置的工作线程,每一个工作线程上都运行着独立的异步事件处理程序。traffic_server创建若干组Thread,并将Event按类型调度到相应的Thread的Event队列上,Thread通过执行Event对应的Continuation中的回调函数,来完成状态的迁移。从初始态到终止态的迁移代表了整个事件的执行过程,而Thread是永不退出的,等待着下一个事件的到来。模型如下图:
两种服务器都涉及到网络I/O操作,Nginx使用的是linux的epoll模型,Traffic Server使用异步I/O。都使用预分配机制,Nginx预分配worker进程,Traffic Server 预分配工作线程。对比分析:
1、并发度
Nginx使用多进程并发模型,采用多进程监听连接。虽然不需要分发操作,但是引入了锁来保持同步,加入了锁的开销。Nginx通过worker进程自己主动去accept,这也会让各个worker进程的负载量比较均衡。worker进程也可以配置成使用线程进行事件处理工作,但模型上还是属于多进程并发。需要注意的是,worker进程的数量不能太多,太多的worker进程会导致进程之间竞争资源频繁,使系统运行缓慢。
Traffic Server使用多线程异步事件处理模型,将多线程技术与异步事件处理技术结合在一起来提高并发和性能。多线程程序可以充分利用现代处理器多核的处理能力,使一个进程的多个任务可以并行执行,提高程序执行的效率。这种多线程的设计也让Traffic Server的性能与处理器的性能一直成正比,不会像Nginx受到worker进程数目的约束。
2、响应时间
Nginx采用的是worker进程独立accept而直接进行处理的方式处理客户请求,因此Nginx的响应速度应该是很快的。
Traffic Server是server进程接受请求,然后分发到请求队列中,再由处理线程进行处理,所以理论上响应时间没有Nginx快。
3、稳定性
Nginx由于是很多能够独立工作的worker进程在工作,当其中的某个或一些worker进程异常时不影响系统的正常工作。master进程在初始化worker进程以后就在循环体中检测信号,然后处理信号,由于master进程一直会比较稳定,当它发现worker进程异常时,又会去重启该worker进程,而且还会检查其他worker进程。可见Nginx的这种设计容错性是很高的。
Traffic Server中由于server是工作进程,而且只有一个,因此系统要保障它的正常运行,为系统添加了traffic_cop进程与traffic_manager进程来管理server进程,给server进程加上了双重的保障。当server异常时,traffic_manager进程会及时的重启它,而且在重启的过程中还会继续利用traffic_manager进程来接受客户请求,这是个不错的设计。当traffic_manager进程和sever进程同时异常结束的时候,traffic_cop又会重启它们,因此系统的稳定性也是很高的。但由于Traffic Server重启的过程中无法继续服务,因此稳定性上Nginx要胜过Traffic Server。
4、峰值响应
Nginx由于是多个worker进程工作,大量请求来的时候各个worker的负载会比较均匀分布,当峰值过高时,会导致worker进程调度延迟,也就会阻塞客户请求,说明服务器的处理能力会阻塞客户请求,它们之间会存在一个约束关系。另外Nginx采取了分阶段资源分配技术,由Cache Manager进程和Cache Loader 进程处理,这种设计也会使得Nginx在遇到峰值请求的时候不轻易的将内存耗尽,不容易造成系统在过载请求下异常结束。
Traffic Server使用server进程接收到大量的用户请求后会发送到请求队列,然后线程在处理的时候会造成数据量过大,容易内存耗尽,出现异常导致server进程也退出,虽然会很快的重启该进程(官方文档说重启只需要几秒钟),而且重启的过程中还会由manager进程继续接受请求,但是系统却在这段时间内无法处理请求,但是Nginx不会停止处理请求。
分析说明在短时间爆发性访问的时候Nginx的响应能力要优于Traffic Server。
5、安全性
Nginx对于安全性做的工作不多,恶意连接攻击可能会导致worker进程耗尽系统资源而停止响应,虽然master进程会检测并尝试重启,但如果master进程也失去响应,那么系统就需要重启。但是因为Nginx采用分阶段资源分配技术,系统很难会耗尽全部的资源而是去响应,因此一般的DOS攻击对于Nginx是没什么效果的。总体来说安全性一般。
Traffic Server在安全性方面做了很多工作,系统会周期性调用heartbeat_manager函数和heartbeat_server函数来检查manager进程和server进程的健康状况,发现不正常时则会立即重启相应的异常进程。而且一旦server进程异常结束,也会很快被重启。因此当收到攻击的时候,Traffic Server会比较及时的作出反应,系统安全性比较高。
总结一下。个人认为Nginx是多进程并发模型的典范,而Traffic Server是多线程异步事件处理模型的典范,各有其优缺点。Nginx是一款优秀的轻量级Web服务器,适合处理请求不多的情况;Traffic Server则是一款高性能Web服务器,还支持集群,更适合处理大量请求连接的情况。
技术选型
在进行技术选型前,可以对比下这些常用的缓存代理服务器。
项 | Squid | Varnish | Nginx | ATS |
---|---|---|---|---|
特性 | 支持多种加密协议,包括AES、ChaCha20和RC4等。提供访问控制功能,限制或屏蔽特定网站或内容。 | 高性能的HTTP加速器,将频繁访问的静态内容存储在内存中。缓存管理:支持精确的缓存时间设置、缓存刷新机制等。负载均衡:可将流量分散到多个后端服务器。 | 高并发处理:支持数以万计的并发连接,而不会显著增加资源消耗。模块化设计:功能易于扩展,可根据需求添加或移除模块。安全性:提供SSL/TLS支持、防DDoS攻击、IP黑白名单等安全特性 | 高性能的HTTP代理和缓存服务器。模块化设计,易于扩展。支持内容分发网络(CDN)和边缘计算场景。 |
性能 | 安全性高、稳定性好、传输速度快。缓存数据存储在硬盘上,访问速度相对较慢。 | 缓存数据全部存储在内存中,访问速度快。能够支持更多的并发连接,尤其在高并发情况下性能优势更为明显。 | 高效的事件驱动和异步处理机制,性能出色。运行时对内存和CPU的占用非常低,适合在资源受限的环境中运行。 | 能够在处理TB级数据的真实部署环境中提供强大的处理能力。适用于高性能的HTTP代理和缓存需求。 |
优势 | 功能丰富,满足各种代理需求。易于部署和配置。 | 灵活的缓存管理、强大的负载均衡和流量管理。支持多核,并发能力强。内存缓存,速度快。 | 支持多核,性能高效、资源消耗低。配置简单、社区活跃。 | 模块化设计,易于定制。高效的缓存策略和负载均衡能力。支持多核。磁盘/内存缓存,性能强。 |
劣势 | 需要客户端软件支持,可能增加配置复杂性。不支持多核。磁盘缓存容量有优势,但性能中等。 | 缓存容量受限于内存大小。不支持HTTPS代理(需要与其他工具结合使用)。 | 动态内容处理能力有限,通常需要配合应用服务器使用。 | 相比专业的缓存服务器,可能在某些缓存功能上稍显不足。社区支持和文档可能不如Nginx和Squid丰富。 |
典型场景 | 企业、学校或组织内部网络的代理服务器,用于缓存、访问控制和安全防护等。 | 各类规模的网站和服务器环境,用于提升性能,如缓存热门内容以减少数据库查询和服务器计算。 | 网站托管:作为静态和动态内容的Web服务器。CDN:利用反向代理和负载均衡功能,构建高效的CDN。 | CDN:作为高性能的HTTP代理和缓存服务器,加速内容分发。边缘计算:结合QUIC支持,作为边缘计算场景下的高效代理服务器。 |
所以,根据各开源软件的优劣势,建议做如下使用规划:
场景 | 技术选型 |
---|---|
反向代理(路由加速,隐藏主节点) | Nginx > Varnish > ATS > Squid |
缓存加速(静态加速,节省带宽,边缘推送) | ATS > Varnish > Squid > Nginx |
防御功能(快速解析,过滤匹配) | Nginx > ATS > Squid > Varnish |