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教程有兴趣的朋友有所帮助。

热AI工具

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

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

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

要让 Tomcat 服务器对外网访问,需要:修改 Tomcat 配置文件,允许外部连接。添加防火墙规则,允许访问 Tomcat 服务器端口。创建 DNS 记录,将域名指向 Tomcat 服务器公有 IP。可选:使用反向代理提升安全性和性能。可选:设置 HTTPS 以提高安全性。

ThinkPHP Framework 的本地运行步骤:下载并解压 ThinkPHP Framework 到本地目录。创建虚拟主机(可选),指向 ThinkPHP 根目录。配置数据库连接参数。启动 Web 服务器。初始化 ThinkPHP 应用程序。访问 ThinkPHP 应用程序 URL 运行。

要解决 "Welcome to nginx!" 错误,需要检查虚拟主机配置,启用虚拟主机,重新加载 Nginx,如果无法找到虚拟主机配置文件,则创建默认页面并重新加载 Nginx,这样错误消息将消失,网站将正常显示。

要将 HTML 文件转换为网址,需要使用网络服务器,包括以下步骤:获取网络服务器。设置网络服务器。上传 HTML 文件。创建域名。路由请求。

Node.js 项目的服务器部署步骤:准备部署环境:获取服务器访问权限、安装 Node.js、设置 Git 存储库。构建应用程序:使用 npm run build 生成可部署代码和依赖项。上传代码到服务器:通过 Git 或文件传输协议。安装依赖项:SSH 登录服务器并使用 npm install 安装应用程序依赖项。启动应用程序:使用 node index.js 等命令启动应用程序,或使用 pm2 等进程管理器。配置反向代理(可选):使用 Nginx 或 Apache 等反向代理路由流量到应用程

Dockerfile 中最常用的指令有:FROM:创建新镜像或派生新镜像RUN:执行命令(安装软件、配置系统)COPY:复制本地文件到镜像ADD:类似 COPY,可自动解压缩 tar 存档或获取 URL 文件CMD:指定容器启动时的命令EXPOSE:声明容器监听端口(但不公开)ENV:设置环境变量VOLUME:挂载主机目录或匿名卷WORKDIR:设置容器中的工作目录ENTRYPOINT:指定容器启动时要执行的可执行文件(类似 CMD,但不可覆盖)

是的,Node.js 可以外网访问。您可以使用以下方法:使用 Cloud Functions 部署函数并公开访问。使用 Express 框架创建路由并定义端点。使用 Nginx 反向代理请求到 Node.js 应用程序。使用 Docker 容器运行 Node.js 应用程序并通过端口映射公开。

要成功部署和维护PHP网站,需要执行以下步骤:选择Web服务器(如Apache或Nginx)安装PHP创建数据库并连接PHP上传代码到服务器设置域名和DNS监控网站维护步骤包括更新PHP和Web服务器、备份网站、监控错误日志和更新内容。
