`
nanjingjiangbiao_T
  • 浏览: 2739295 次
  • 来自: 深圳
文章分类
社区版块
存档分类
最新评论

缓存服务器设计与实现(二)

 
阅读更多
我们现在讨论算是最简单的情景,即服务器还没有文件缓存,第一个需要缓存的请求的处理过程。当然需要关注的情景有很多,一个一个来吧。

在缓存服务器设计与实现(一)中讨论的都是一些准备工作,我们接下来要关注从后端机器取回数据以后进行缓存的情景。首先来探讨一个问题,以nginx为例,它是在取后端数据之前就创建了缓存对象,那么从整个系统的角度来看,创建缓存对象的过程包括在内存中建立相应的控制结构,并且在磁盘上创建实体(文件的形式)。那么我们需要关注的是这两部分都有些什么成分?先看磁盘上的文件,它应该存什么。存储实际文件内容是必然的,这就够了吗?

我们知道作为一个http缓存系统,首先它是一个完整的http服务器。所以在响应一个客户端的请求时,必须先给出一个http响应头,然后才是内容。当nginx作为一个静态http服务器工作的时候,响应头是nginx自己构造的,想怎么搞都可以。但是当它作为一个缓存服务器使用时,响应头应该尽量跟被代理的后端服务器一致,甚至严格一致。那么此时,响应头神马的就不能自己杜撰了。从这个角度上来讲把响应头头跟文件内容同时缓存起来是合适,也是必要的。你可以尝试打开一个nginx缓存好的文件,就会发现在具体内容之前确实是保存着相应的响应头的。不过在文件的最开始(响应头之前)貌似还有一些东西,关于这些神秘的东西,随着我们的讨论,都会搞清楚的。其实几乎所有的http缓存服务器都是这么做的,即将http响应头和内容都缓存在文件中。

刚才我们关注的是缓存对象在磁盘上的内容组织,下面我们再看一下内存中的控制结构。

在nginx中每个文件都在内存中都有相应的控制结构,称为一个node。这个结构是在共享内存中申请和管理的,为什么用共享内存?nginx作为多进程模型,我们希望在worker A中缓存的文件对象,在worker B中相同请求到来时也能够hit该对象,要不然就太废了。当然互斥也是不可避免的。

在nginx具体实现中,这个node是结构体ngx_http_file_cache_node_t。关于这个结构中各个成员的说明,可以参考http://www.pagefault.info/?p=375,这里就不多说了。当一个node首次创建之后,需要放入到系统的缓存管理体系中,nginx用到的是红黑树,所有的node都被插入到树里面,然后还要放到lru队列中,作用就是在存储空间不够的时候,通过lru来删掉一些对象。我们的cache在lru方面跟nginx是一致的,但是所有node是通过hash表来管理的。

其实细节需要讨论的东西实在是太多。现在还是先转到重点上来,来看一下nginx如何将收到的后端数据写到本地磁盘文件里去。

核心思想是这样的,nginx首先会将数据写到一个临时文件中去,然后内容收完之后,再将这个临时文件rename到实际的目标文件。这是主要框架,至于如何管理临时文件,又该如何去实现,nginx有它的处理,我们自己实现一套也可以,这都是一些无关紧要的细节了。重点的问题其实是要维护我们的缓存系统中有关当前缓存文件的状态,缓存过程是OK的,该怎么处理,出现异常之后,又该如何处理。

先看当这个文件缓存ok的时候,该如何更新缓存信息,来声明文件已经缓存完毕,可供使用了。这里还是以nginx为例:
在文件获取完成之前,缓存对象的node节点结构中exists成员一直为0,当缓存正常结束时exists就会被置1。所以这个成员的含义就很明确了。这里注意的是,缓存对象的node节点信息位于共享内存中红黑树中,是唯一的。而每个request通过一个cache成员,来跟这个node发生联系。

现在有一个相同的请求过来,首先要做的事情就是查找是否有已经缓存的目标文件。如果文件存在,一般需要先将文件开始部分的一些控制和管理信息读取出来,通过分析这些信息来判断该文件是否是可用的,如未过期或者完好等等。如果可用,那么剩下的工作就是发送内容了,不过在实际发送之前,一般需要先将读取出来的响应头做一些header filter的处理等等,后面的文件内容一般直接发送就好了。

先做一个阶段性的总结吧。到目前为止,我们看到的过程包括:第一个请求到来->去后端取数据缓存;同样的请求到来->发现了刚刚创建的文件->文件可用->发送这个文件。从nginx实现上看,它的ngx_http_file_cache_node_t结构中有两个成员需要注意,一个是count,表示当前正在使用这个node的请求数,另一个成员uses记录了到目前为止,这个node被访问的次数,这个值是一直累加的。

如果一个文件过期了,此时当有请求再次访问到这个文件时,该如何处理呢?这里应该提一下跟cache相关的管理进程(后面会拿出篇幅来重点讨论他们),这其中有一个非常重要的后台程序,它跟普通的worker进程一样,是由master进程fork出来的。ps命令看到的进程title一般为:“nginx: cache manager process”,它的一个重要的作用就是监控过期,做lru等工作。我们把这个manager进程发现过期的情景称为主动发现,worker进程在处理请求时也会发现某个对象过期,这个称为被动发现。能做到这些,它的基础就在于管理缓存对象的控制结构及其信息是由master进程通过共享内存创建并初始化的,这样manager和worker在被fork之后,这些信息就是共享的了。manager进程在运作时,从lru队列的尾部开始,检查是否有文件过期。有过期的就删掉。至于为什么从lru队列的尾部检查,是因为在worker处理对象的时候,每次hit一个文件(一个正常的文件必然位于lru队列中),该文件就从队列中删掉,然后插到队列头部去,所以越靠近尾部的对象,越是较长时间没有被访问到的,LRU的思想也在于此。当然一直以来很多人都在批评这种处理,说它不科学不严谨。是的,这点没错。网上有很多更高级更严谨的lru理论及实现,但是在实际应用中我们不能死磕理论,往往需要结合系统复杂度,实现难易等各方面的考虑。我们自己的cache,包括squid,nginx都是用的这种最简单处理方式,而事实也表明:运行表现不错。

貌似扯远了。我们现在重点关注当worker进程发现了一个对象过期时,它会如何去处理。首先一个问题就是进程如何发现一个文件时过期的?其实在一个文件的开头部分,存放了一些有关文件的控制头信息,前面已经提过了。这个头信息中有相关的变量标记这个对象保鲜时间,所以只需要读出这个变量跟当前时间比较一下就可以了。
从系统的控制结构来看,有个名叫updating的变量此时会被置1。后续的处理就很显然,就是去后端取新文件了。注意此时这个文件相关的管理结构还未被从系统里释放,所以后续的请求还是会hit,不过后面的处理还是会发现过期,这样只要一个请求在更新完成之前,对于该文件的请求,都会去后端取文件(即透传)。前面我们分析过了,nginx会先用临时文件来保存数据,完整取完之后会rename到缓存目录下去。那么当文件过期,但此时有又同一并发请求,那么最后谁去rename呢?说得这里就不得不提另外一个rename,那就是针对一个文件的首次并发请求,各个请求都是独立取源,最后也会出现同时rename的情况。呵呵,看一篇文章吧:http://www.ibm.com/developerworks/cn/linux/l-cn-fsmeta/

上面讲到nginx会出现并发取源的情况,很多公司对这块进行了定制。最常提到的是所谓取源合并。顾名思义,就是合并回源的请求。这项功能,nginx在比较新的版本里面已经支持了,使用的指令时proxy_cache_lock。官方wiki给出的说明:

syntax: proxy_cache_lock on | off;
default: proxy_cache_lock off;
context: http, server, location
This directive appeared in version 1.1.12.
When enabled, only one request at a time will be allowed to populate a new cache element identified according to the proxy_cache_key directive by passing a request to a proxied server. Other requests of the same cache element will either wait for a response to appear in the cache, or the cache lock for this element to be released, up to the time set by the proxy_cache_lock_timeout directive.

另外一个地方就是当文件过期时,也会产生大量的并发回源量。这点nginx也做了处理,很多公司也对这块做了自己的定制。nginx通过指令proxy_cache_use_stale来控制在文件过期更新过程的回源请求量,让当一个请求在更新文件时,其他请求则暂时使用过期文件。具体配置为proxy_cache_use_stale updating;关于该指令的具体用法,大家可以去官网查阅。

还有一些机制,后面再接着讨论。
分享到:
评论

相关推荐

    缓存服务器

    缓存服务器是网络架构中的重要组成部分,其主要目的是提高数据访问速度,降低网络延迟,以及减轻后端服务器的负载。...在设计和实现缓存服务器时,需根据业务需求和资源状况,合理选择缓存策略和技术,以达到最佳效果。

    分布式数据库缓存系统设计与实现

    在这个主题中,我们将深入探讨分布式数据库缓存系统的概念、设计原则以及实现技术。 一、分布式数据库缓存系统的基本概念 分布式数据库缓存系统是一种将数据存储在多台服务器上的缓存解决方案,通过网络连接形成一...

    网络代理服务器的设计与实现

    此外,管道流技术也是代理服务器设计中不可或缺的一部分。管道流允许多个请求并发地通过同一连接进行,提高了网络带宽的利用率,尤其在处理大量并发请求时,能显著提升服务器性能。 系统架构基于服务器/客户机模型...

    web代理服务器缓存设计

    #### 三、代理缓存服务器的设计 ##### 3.1 代理缓存服务器的角色划分 代理服务器在系统中扮演着双重角色:对于外部服务器来说,它是客户端;而对于内部客户端来说,它又是服务器。因此,代理服务器的设计可以分为...

    多线程Web服务器的设计与实现

    本实验的主题是“多线程Web服务器的设计与实现”,这涉及到并发处理和网络通信的核心概念。下面将详细讨论相关知识点。 1. **多线程**:多线程是指在一个程序中可以同时执行多个独立的线程。在Web服务器中,多线程...

    基于ZooKeeper的分布式缓存的设计与实现.pdf

    ZooKeeper被设计为一个高性能的分布式协调服务框架,可以管理分布式环境中各个缓存服务器之间的共享状态数据,以支持分布式缓存系统的稳定运行。ZooKeeper本质上是一个分布式的小文件存储系统,可以维护和协调分布式...

    Python-一个采用Python开发的简单缓存服务器

    本项目"Python-一个采用Python开发的简单缓存服务器"很可能就是实现了这样的功能。 在描述中提到的“一个采用Python开发的简单缓存服务器”,我们可以推测这个服务器可能具备基础的缓存功能,如设置、获取和删除...

    AI服务器设计与实现

    《AI服务器设计与实现》 在游戏开发领域,AI服务器扮演着至关重要的角色,它负责处理游戏中各种智能实体的行为逻辑。本文将详细探讨一个能够同时处理4000多个活跃AI对象的AI服务器的设计和实现策略,特别是如何利用...

    基于C++设计与实现HTTP代理服务器【100011743】

    设计并实现一个基本 HTTP 代理服务器。要求在指定端口(例如 8080)接收来自客户的 HTTP 请求并且根据其中的 URL 地址访问该地址所指向的 HTTP 服务器(原服务器),接收 HTTP 服务器的响应报文,并将响应报文转发给...

    分布式缓存服务器memcacaed的源代码

    分布式缓存服务器Memcached是互联网应用中广泛使用的内存对象缓存系统,用于减轻数据库的负载,提高应用程序的性能。Memcached的设计目标是简单且高效,它通过在内存中存储数据来提供快速的数据访问。让我们深入了解...

    缓存服务器memcached下载

    **缓存服务器Memcached详解** Memcached是一款高性能、分布式内存对象缓存系统,它被广泛应用于Web应用中,用于减轻数据库负载,提高页面加载速度,从而显著提升网站的整体性能。Memcached的设计理念是简单而高效,...

    redis做mysql缓存服务器(公司内部培训资料)

    #### 二、使用Redis作为MySQL缓存服务器的原理 在本文档中,我们将介绍如何使用Redis作为MySQL的缓存服务器来实现读写分离。读写分离的基本思路是将数据读取操作与数据写入操作分开处理,通常通过在不同的服务器上...

    redis页面缓存html使用redis实现页面缓存.docx

    本文档主要介绍了使用 Redis 实现页面缓存的方法,包括缓存 key 的设计、缓存实现的思路和代码实现。 页面缓存的目的:页面缓存的主要目的是为了提高网站的访问速度和用户体验。通过将页面缓存到 Redis 中,可以...

    Redis用作二级缓存

    5. 实现缓存拦截器:创建自定义的RedisCacheInterceptor类,继承自Mybatis的CacheInterceptor,并实现其中的方法,如get、put、remove等,以便与Redis交互。 6. 使用缓存:在Mapper的Java接口或XML映射文件中,通过...

    社交游戏服务器端软件的设计与实现大学论文.doc

    在服务器架构设计方面,本文将游戏服务器分为了数据服务器、数据缓存服务器、登陆服务器、大厅服务器、游戏服务器、中央控制服务器、日志服务器等 7 个不同功能的服务器组。在逻辑层次划分方面,参考了 MVC 的分层...

    memcached缓存服务器

    **Memcached缓存服务器详解** Memcached是一款高性能、分布式内存对象缓存系统,它被广泛应用于Web应用中,用于减轻数据库的负载,提高数据访问速度。这个“memcached缓存服务器”压缩包提供的版本是1.2.1,适用于...

    OPC及OPC服务器的设计与实现

    #### 二、OPC服务器设计与实现 ##### 1. OPC服务器的基本功能 - **数据读取与写入**:OPC服务器能够从硬件设备中读取实时数据,并将其提供给客户端应用;同时也能接收客户端的应用命令来修改硬件设备的状态。 - **...

    社交游戏服务器端软件的设计与实现--大学毕业设计论文.doc

    本论文对社交游戏服务器端软件的设计与实现进行了深入研究,涵盖了网络传输、服务集群技术、负载均衡技术、数据缓存技术等多个方面。 一、社交游戏服务器端软件的设计原则 社交游戏服务器端软件的设计需要考虑到多...

Global site tag (gtag.js) - Google Analytics