织梦CMS - 轻松建站从此开始!

罗索

Squid合并回源技术

落鹤生 发布于 2013-08-25 12:38 点击:次 
对于CDN的cache服务器而言,减少回源,提高命中率是一个重要的功能,尤其是在处理大文件的时候。这次我们就讲一讲squid是怎样让尽可能多的对于相同url的请求共用同一个回源请求的。
TAG:

1. 合并回源的概念

对于CDNcache服务器而言,减少回源,提高命中率是一个重要的功能,尤其是在处理大文件的时候。这次我们就讲一讲squid是怎样让尽可能多的对于相同url的请求共用同一个回源请求的。

当然,如果一个object已经完整地存在了squid的磁盘上,在它过期之前是不会回源的。我们要讨论的是正在从原站下载过程中的object,当另外一个客户端来下载它的时候。其他cache软件可能会忽略当前正在下载的object,而另起一个回源请求去下载。这样的话,在第一个客户端完整地下完这个object的时候,cache会回源多次。而squid则可以将这些请求合并起来,用一个回源请求服务所有的客户端。

2. Squidstore_client

Squid合并回源请求,主要靠的就是store_client层。

Store_client层的工作原理是:当有多个request请求文件的不同部分时,store_client层会对他们采取不同的处理。

1表示一个1Gobject被多个请求同时访问的情形。

request1是第一个访问这个object的请求,由他发起了回源,现在回源已经下载了200M的内容;

request2是一个从头开始的请求,它开始得比较晚,只请求了100M

request3是一个range请求,它请求的是还没有从原站下到的部分;

 

 1

 

注意,虽然是request1发起了回源,但回源请求并不是request1本身,而是由fwdStart函数发起的一个单独的回源请求。即使request1断掉了,回源也是可以继续的,直到下载完整个object为止。

3个请求都要通过store_client层的storeClientCopy函数从store拿数据。

2.1 数据从内存来

request1copy_offset199M,而从150M200M的数据全都在内存里,因此它进入的分支在storeClientCopy3函数里,它是:

  1. if (sc->copy_offset >= mem->inmem_lo&& sc->copy_offset < mem->inmem_hi) 
  2.     /* What the client wants is in memory */ 
  3.     debug (20, 3) ("storeClientCopy3: Copying from memory\n"); 
  4.     sz = stmemCopy (&mem->data_hdr, sc->copy_offset, sc->copy_buf, sc->copy_size); 
  5.     if (EBIT_TEST (e->flags, RELEASE_REQUEST)) 
  6.         storeSwapOutMaintainMemObject (e); 
  7.     storeClientCallback (sc, sz); 
  8.     return

直接就用stmemCopy将内存中的数据copysc->copy_buf中,然后storeClientCallback回调客户端的函数(其实就是clientSendMoreData)。可以看到,根本不需要读磁盘。

2.2      数据从磁盘来

request2copy_offset100M,比inmem_lo要低,它进入的分支很简单,就是storeClientCopy3函数的最后一行:

  1. storeClientFileRead (sc); 

storeClientFileRead顾名思义,就是去读文件了。

storeClientFileRead会调用storeRead,进入多线程的aufs异步io,拿到文件内容之后还是会调用storeClientCallback回调。

2.3      数据还在原站

request3很显然,数据在磁盘和内存都没有。所以它所能做的就是等待了。它的分支是

  1. if (e->store_status == STORE_PENDING&& sc->seen_offset >= mem->inmem_hi) 
  2.     /* client has already seen this, wait for more */ 
  3.     debug (20, 3) ("storeClientCopy3: Waiting for more\n"); 
  4.  
  5.     /* If the read is backed off and all clients have seen all the data in 
  6.     * memory, re-poll the fd */ 
  7.     if ((EBIT_TEST (e->flags, ENTRY_DEFER_READ)) &&    (storeLowestMemReaderOffset (e) >= mem->inmem_hi)) 
  8.     { 
  9.         debug (20,3)("storeClientCopy3: %s - clearing ENTRY_DEFER_READ\n", e->mem_obj->url); 
  10.         /* Clear the flag and re-poll the fd */ 
  11.         storeResumeRead (e); 
  12.     } 
  13.     return

既没有读磁盘,也没有读内存,就return掉了。那么什么时候这个客户端会继续呢?就是当回源链接每从原站读到一块数据,会调用storeAppend,进而调用InvokeHandler,由于request2store_client是当前storeEntryclient之一,invokeHandler会对request2store_client重新调用storeClientCopy2storeClientCopy3,如果到了那个时候它所需要的数据在内存或硬盘了,客户端就会继续收到数据。

 

3.    折叠回源机制collapsed_forwarding

刚才说的是store_client层,它的作用是,管理已经在访问同一个object的所有客户端,让他们取到各自需要的数据。

那么,怎样让所有的客户端访问到同一个object呢?

squid会为每一个object创建一个StoreEntry结构,并放到store_hash这个哈希表中,供后来的request查找。当request1从原站下载到了数据时,它的StoreEntry肯定是已经创建好了。这个阶段要让其他请求找到这个object是很容易的。

但问题就是request1的客户端请求已经发到了squid,而原站数据还没有下载到的这个期间,如果其他请求来了怎么办呢?其实squid为这个问题引入了一个配置项,叫做collapsed_forwarding,可以on或者off。这个配置项的意思就是,所有请求共用一个回源请求。

3.1 collapsed_forwarding on

当配制成on的时候,只要request1的客户端请求发到了,这时候url也知道了,就立即创建它的StoreEntry,并放到hash表中。即使回源的数据还没拿到,其他请求也能找到它的StoreEntry

但是,这时候就有一个问题了,万一request1请求的是动态内容怎么办?难道让其他请求也拿到跟request1一样的内容么?

当然不会!squidclientCacheHit中做了防范。clientCacheHit是当一个回调函数,在请求hit了,并拿到reply头的时候由storeClientCallback回调。

假如request2就是一个这样的请求,它发生在在request1拿到原站的数据之前,并找到了request1创建的StoreEntry,而且“以为”自己hit了。而回源的reply中有Cache-Control:no-cache,那么当request2在进入clientCacheHit时,它找到的StoreEntry中一定会有RELEASE_REQUEST这个标识。这个标识是httpCachableReply函数发现no-cache之后设置的。当发现这个标识的时候,就会从clientCacheHit转入clientProcessMiss,重新回源,不会跟request1取到相同的数据。代码如下

  1. if (r->flags.collapsed && EBIT_TEST (e->flags, RELEASE_REQUEST)) 
  2.     /* collapsed_forwarding, but the joined request is not good 
  3.      * to be cached.. 
  4.      */ 
  5.     clientProcessMiss (http); 
  6.     return

3.2      collapsed_forwarding off

当配制成off的时候,与on相反,当request1发过来的时候,不立刻将 StoreEntry放到hash表中,只有当回源请求拿到了reply头,确认是可缓存的内容后,才将StoreEntry放到hash表中。

虽然回源拿到响应头的时间通常很短,但终究有可能发生“误会”,request1request2都回源了。那么这种情况下会不会两个回源请求产生冲突,比如写同一个cache文件之类的问题呢?

其实也不会。squid对这种情况也做了防范。

在发现一个object可缓存时,会调用httpMakePublic,进而调用StoreSetPublicKeyStoreSetPublicKey主要是负责将一个StoreEntry放入hash表。这时候它会先检查hash表中是否已经有了key相同的object,如果有,要先删除旧的再插入新的。

删除旧的object之后,request1request2的回源都不会停止,以保证客户端下到完整的数据。

那么,它们会不会写到相同的文件里呢?

还是不会。因为squid为新的object分配文件名不是直接将key转换为文件名,而是用一个file bitmap来分配一个文件号,用这个文件号来生成文件名。分配过的文件号是不会被再次分配出去的。

还有最后一个问题,就算文件名不同,request1回源取到的数据还会不会被写到磁盘上呢?

看到这里,你会知道squid是很聪明的,所以答案当然还是不会了。因为旧的entry一旦被打上RELEASE_REQUEST标,同时会清掉ENTRY_CACHABLE标,它的swapout就不会再进行了。这是在storeSwapOutMaintainMemObject里保证的。

  1. swapout_able = storeSwapOutAble (e); 
  2. if (!swapout_able) 
  3.     /* Stop writing to disk */ 
  4.     storeReleaseRequest (e); 
  5.     if (e->mem_obj->swapout.sio != NULL) 
  6.         storeSwapOutFileClose (e); 

其中storeSwapOutAble的定义如下:

  1. static int storeSwapOutAble (const StoreEntry * e) 
  2.     if (e->mem_obj->inmem_hi > Config.Store.maxObjectSize) 
  3.         return 0; 
  4.     if (!EBIT_TEST (e->flags, ENTRY_CACHABLE)) 
  5.         return 0; 
  6.     if (e->mem_obj->swapout.sio != NULL) 
  7.         return 1; 
  8.     if (e->mem_obj->swapout.queue_offset) 
  9.         if (e->mem_obj->swapout.queue_offset == e->mem_obj->inmem_hi) 
  10.          return 1; 
  11.     if (e->mem_obj->inmem_lo > 0) 
  12.         return 0; 
  13.     return 1; 

4. 思考题 

最后,请大家想一想,什么样的业务模式适合collapsed_forwarding on,什么样的适合off呢?

(xiaosi_sw)
本站文章除注明转载外,均为本站原创或编译欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,同学习共成长。转载请注明:文章转载自:罗索实验室 [http://www.rosoo.net/a/201308/16727.html]
本文出处:ChinaUnix 作者:xiaosi_sw 原文
顶一下
(1)
100%
踩一下
(0)
0%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片
栏目列表
将本文分享到微信
织梦二维码生成器
推荐内容