搜索
首页后端开发php教程nginx的cache系统设计原理

这里我们nginx的cache系统为线索,来探讨一个缓存服务器的设计和相关细节,我尽量站在设计和框架的角度来分析,限于篇幅这里不再去撸代码了,相关的细节,欢迎大家一起参与讨论。

    一个cache服务器中从后端取得文件之后,要么直接发送给客户端(学名叫透传),要么缓存在本地,后续相同的请求访问到cache服务器时,就可以直接拿本地的拷贝来用了,如果可以用的话。如果本地缓存的文件被后续的请求访问到,在cache中叫做命中(即Hit)。如果本地还没有文件的缓存拷贝,那么cache服务器需要根据配置或者做解析域名,去后端获取文件,这时称为缓存miss,即未命中。关于cache服务器更多的知识,我们在分析nginx的缓存系统时再深入讨论。

    nginx的存储系统分两类,一类是通过proxy_store开启的,存储方式是按照url中的文件路径,存储在本地。比如/file/2013/0001/en/test.html,那么nginx就会在指定的存储目录下依次建立各个目录和文件。另一类是通过proxy_cache开启,这种方式存储的文件不是按照url路径来组织的,而是使用一些特殊方式来管理的(这里称为自定义方式),自定义方式就是我们要重点分析的。那么这两种方式各有什么优势呢?

    按url路径存储文件的方式,程序处理起来比较简单,但是性能不行。首先有的url巨长,我们要在本地文件系统上建立如此深的目录,那么文件的打开和查找都很会很慢(回想kernel中通过路径名查找inode的过程吧)。如果使用自定义方式来处理模式,尽管也离不开文件和路径,但是它不会因url长度而产生复杂性增加和性能的降低。从某种意义上说这是一种用户态文件系统,最典型的应该算是squid中的CFS。nginx使用的方式相对简单,主要依靠url的md5值来管理,后面我们会分析。

    缓存离不开从后端取内容,然后发送给客户端。具体的处理方式大家很容易就会想到,肯定是一边接收一边发送,其他的方式都太低效了,如读完再发等等。这里提一下nginx边收边发,使用的结构是ngx_event_pipe_t,它是沟通后端和客户端的媒介。由于该结构是一个通用组件,所以需要一些特殊的标记来处理涉及存储的相关功能,那么成员cacheable就担当了这份重任。

p->cacheable = u->cacheable || u->store;

即cacheable为1,则需要存储,否则不存储。那么u->cacheable跟u->store代表什么?他们分别代表前面说的两种方式,即proxy_cache和proxy_store。 

(补充一些知识,nginx在取后端数据时,它的行为受proxy_buffering控制,作用是为后端的服务器启用应答缓冲。如果启用缓冲,nginx假设被代理服务器能够非常快的传递应答,并将其放入缓冲区,可以使用proxy_buffer_size和proxy_buffers设置相关参数。如果响应无法全部放入内存,则将其写入硬盘。如果禁用缓冲,从后端传来的应答将立即被传送到客户端。)


    这里都是一些擦边球,我们还没有接触nginx cache功能的核心。从实现上看,在nginx upstream结构中有个成员叫cache,它的类型是ngx_shm_zone_t。如果我们开启cache功能,cache成员用来管理共享内存(为什么用到了共享内存?),而其他方式的存储该成员都为NULL。另外有一点需要说明一下,在cache系统中一个文件通常被称为store object,即缓存对象,所以进行cache之前必然需要先创建一个store object。一个重要的问题就是如何选择创建的时机,这点大家有什么看法?首先我们需要检查一个文件是否是需要缓存,很明显GET方法请求的文件一般需要缓存,所以我们在请求处理的前期,看到了GET方法,就可以先创建一个对象。但是很多时候,即使是一个GET方法请求的文件也不能缓存,那么你过早的创建对象,不仅浪费时间也浪费了空间,到头来还要将它销毁。那么什么会影响GET请求的存储呢?那就是响应头中的Cache-control字段,这个字段就告诉代理或者浏览器,该文件能否被缓存。一般的cache服务器面对响应头中没有Cache-control字段的请求,默认都是要缓存的。

    基于这一点的考虑,我们开发的cache服务器就是在响应头解析完成,拿到可缓存的足够证据之后,才会创建缓存对象。遗憾的是,nginx没有这么去做。

nginx在ngx_http_upstream_init_request函数中完成缓存对象的创建,这个函数处在http处理的什么阶段呢?在跟后端建立连接之前。这个地方,我个人认为不太合适。。。大家认为呢?

    关于创建过程,大家可以去读函数ngx_http_upstream_cache。这里我拿我们的cache跟nginx对比来分析吧。我们的request中使用一个名叫store的成员,来跟缓存对象建立联系。nginx也差不多,它的request结构体中有个cache成员来做同样的事情。区别在于我们的store成员对应的空间在共享内存中,而nginx则是在r->pool里申请的(我们为什么这么做?)。

    下一步,nginx需要根据配置来生成缓存对象的key,此处一般都是用md5来算的。这个key作为一个缓存对象在系统中的唯一标识,很多人可能担心md5碰撞的问题。这个我认为要求如果不是特别苛刻,这里完全可以接受的,而且处理也相对简单。

后面要处理的是,文件到底应该已怎样的形式存储在磁盘?

    我们拿前面用过的一个例子:/file/2013/0001/en/test.html,它对应的md5值是8ef9229f02c5672c747dc7a324d658d0,实际上nginx就用它当做文件名。这样就可以了?如果我们找一个目录来存放文件,里面都是一堆这样的文件,那么会怎样?我们知道,大多数文件系统下,都对单个目录下的文件数量有限制,所以这样简单粗暴的处理是不行的。那怎么办?nginx通过配置可以让你使用多级目录,来解决这个问题。简单来说,nginx通过levels这个指令指定目录层数(冒号分隔)和每个目录名字的字符个数,在我们的例子中,假设配置levels=1:2,意思是说使用两级目录,第一级目录名是一个字符,第二级用两个字符。但是nginx最大支持3级目录,即levels=xxx:xxx:xxx。

    那么构成目录名字的字符哪来的呢?假设我们的存储目录为/cache,levels=1:2,那么对于上面的文件 就是这样存储的:

  /cache/0/8d/8ef9229f02c5672c747dc7a324d658d0

    看到0和8d这两个目录名怎么来的了吧,不用解释了。

    对象创建完成之后,就需要缓存对象管理结构中去了,这个ngx_http_file_cache_exists去处理的。

    如果在创建这个文件时,当前目录及文件已经存在,那如何处理?大家可以去翻翻代码,看nginx怎么处理的。

    讨论先告一个段落,其实现在都是一些准备工作,下次讨论后端内容到来的处理。

    扩展阅读:

    http://www.pagefault.info/?p=123

    http://www.pagefault.info/?p=375

以上就介绍了nginx的cache系统设计原理,包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
您什么时候使用特质与PHP中的抽象类或接口?您什么时候使用特质与PHP中的抽象类或接口?Apr 10, 2025 am 09:39 AM

在PHP中,trait适用于需要方法复用但不适合使用继承的情况。1)trait允许在类中复用方法,避免多重继承复杂性。2)使用trait时需注意方法冲突,可通过insteadof和as关键字解决。3)应避免过度使用trait,保持其单一职责,以优化性能和提高代码可维护性。

什么是依赖性注入容器(DIC),为什么在PHP中使用一个?什么是依赖性注入容器(DIC),为什么在PHP中使用一个?Apr 10, 2025 am 09:38 AM

依赖注入容器(DIC)是一种管理和提供对象依赖关系的工具,用于PHP项目中。DIC的主要好处包括:1.解耦,使组件独立,代码易维护和测试;2.灵活性,易替换或修改依赖关系;3.可测试性,方便注入mock对象进行单元测试。

与常规PHP阵列相比,解释SPL SplfixedArray及其性能特征。与常规PHP阵列相比,解释SPL SplfixedArray及其性能特征。Apr 10, 2025 am 09:37 AM

SplFixedArray在PHP中是一种固定大小的数组,适用于需要高性能和低内存使用量的场景。1)它在创建时需指定大小,避免动态调整带来的开销。2)基于C语言数组,直接操作内存,访问速度快。3)适合大规模数据处理和内存敏感环境,但需谨慎使用,因其大小固定。

PHP如何安全地上载文件?PHP如何安全地上载文件?Apr 10, 2025 am 09:37 AM

PHP通过$\_FILES变量处理文件上传,确保安全性的方法包括:1.检查上传错误,2.验证文件类型和大小,3.防止文件覆盖,4.移动文件到永久存储位置。

什么是无效的合并操作员(??)和无效分配运算符(?? =)?什么是无效的合并操作员(??)和无效分配运算符(?? =)?Apr 10, 2025 am 09:33 AM

JavaScript中处理空值可以使用NullCoalescingOperator(??)和NullCoalescingAssignmentOperator(??=)。1.??返回第一个非null或非undefined的操作数。2.??=将变量赋值为右操作数的值,但前提是该变量为null或undefined。这些操作符简化了代码逻辑,提高了可读性和性能。

什么是内容安全策略(CSP)标头,为什么重要?什么是内容安全策略(CSP)标头,为什么重要?Apr 09, 2025 am 12:10 AM

CSP重要因为它能防范XSS攻击和限制资源加载,提升网站安全性。1.CSP是HTTP响应头的一部分,通过严格策略限制恶意行为。2.基本用法是只允许从同源加载资源。3.高级用法可设置更细粒度的策略,如允许特定域名加载脚本和样式。4.使用Content-Security-Policy-Report-Only头部可调试和优化CSP策略。

什么是HTTP请求方法(获取,发布,放置,删除等),何时应该使用?什么是HTTP请求方法(获取,发布,放置,删除等),何时应该使用?Apr 09, 2025 am 12:09 AM

HTTP请求方法包括GET、POST、PUT和DELETE,分别用于获取、提交、更新和删除资源。1.GET方法用于获取资源,适用于读取操作。2.POST方法用于提交数据,常用于创建新资源。3.PUT方法用于更新资源,适用于完整更新。4.DELETE方法用于删除资源,适用于删除操作。

什么是HTTP,为什么对Web应用程序至关重要?什么是HTTP,为什么对Web应用程序至关重要?Apr 09, 2025 am 12:08 AM

HTTPS是一种在HTTP基础上增加安全层的协议,主要通过加密数据保护用户隐私和数据安全。其工作原理包括TLS握手、证书验证和加密通信。实现HTTPS时需注意证书管理、性能影响和混合内容问题。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
3 周前By尊渡假赌尊渡假赌尊渡假赌

热工具

SecLists

SecLists

SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

Atom编辑器mac版下载

Atom编辑器mac版下载

最流行的的开源编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用