Chinaunix首页 | 论坛 | 博客
  • 博客访问: 143582
  • 博文数量: 7
  • 博客积分: 67
  • 博客等级: 民兵
  • 技术积分: 184
  • 用 户 组: 普通用户
  • 注册时间: 2010-12-04 05:16
文章分类

全部博文(7)

文章存档

2013年(7)

我的朋友

分类: LINUX

2013-08-01 20:53:30

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



直接就用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. {
  3.     /* client has already seen this, wait for more */
  4.     debug (20, 3) ("storeClientCopy3: Waiting for more\n");

  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;
  14. }


既没有读磁盘,也没有读内存,就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. {
  3.     /* collapsed_forwarding, but the joined request is not good
  4.      * to be cached..
  5.      */
  6.     clientProcessMiss (http);
  7.     return;
  8. }


 

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. {
  4.     /* Stop writing to disk */
  5.     storeReleaseRequest (e);
  6.     if (e->mem_obj->swapout.sio != NULL)
  7.         storeSwapOutFileClose (e);
  8. }


其中storeSwapOutAble的定义如下:

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



4.
思考题 

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

阅读(9298) | 评论(1) | 转发(1) |
0

上一篇:nginx rtmp代码架构1 hook点总结

下一篇:没有了

给主人留下些什么吧!~~

zjturn2013-12-18 15:30:31

每一篇都好好好拜读下啊 谢谢哥们在QQ上耐心回答我的问题