squid range请求处理解析

来源:互联网 发布:网站用linux还是win 编辑:程序博客网 时间:2024/06/15 20:17
一、说明
range请求在日常中占比不少,如断点续传,多线程下载(现浏览器访问也开启了多线程),音视频拖拽等。

二、相关命令
1、range_offset_limit 0 KB (默认)所有的Range请求都回源,包括bytes=0-
2、range_offset_limit -1 KB               回源时去掉range请求,下载整个url,如果是个大文件,第一个请求可能等的时间稍长。
3、range_offset_limit N KB   最小的range请求左值小于N时,miss回源且是去掉range回源(可下载整个url),后面只要是相同的url都会命中。
                                             最小的range请求左值大于N时,miss回源且带range头,不被缓存。
                                             当第一个range头左值小于N时,先下载整个url,第二个请求range头左值大于N时也命中。
三、思考
1、 为什么range回源的响应数据不可缓存?抛弃no-cache情况想想,由于多个range请求对应的是一个storeentry,假如果你要缓存,
该怎么根据range请求的A-B值设置memobject的low和high,而且是多个range请求。并不是不想存,是没办法存。
 2、 range请求跟折叠回源有什么关系? 乍看还挺迷惑,折叠回源你可以看成是普通请求的流程,range请求因为在解析hit or miss处做了判断,所以可以看成特殊的请求流程。

四、range流程的处理点
       squid range流程的处理主要是跟range_offset_limit命令相关,根据这个命令盘算是否hit,以及回源时是否需要删除range头。
 
1、 http range请求过来,解析range头放入到request中

clientStoreURLRewriteDone-->clientFinishRewriteStuff-->
clientInterpretRequestHeaders(clientHttpRequest *http){
    if (request->method == METHOD_GET) {
        request->range =httpHeaderGetRange(req_hdr);   解析range头,将A-B,C-D都解析到range->specs 堆栈中,包括0-。
        if (request->range) {
            request->flags.range = 1;
        }
    }
}

2、判断该请求是否hit,此时range_offset_limit作用很大

clientProcessRequest-->clientProcessRequest2--->
                  e = http->entry =storeGetPublicByRequest(r);--->if (NULL == e) { return miss;}      //为空,range的第一个请求肯定是miss  ---->
clientCheckRangeForceMiss(StoreEntry * entry, HttpHdrRange * range)
{
    if (0 ==httpHdrRangeOffsetLimit(range))   
         return 0;    //hit
    if (STORE_OK == entry->store_status)  //此处也是关键点,range_offset_llimit值小于最小左值时,如果改url有cache且数据完整,则hit
          return 0;        /* we have the whole object */
    assert(NULL != entry->mem_obj);
   //此处也是关键点,range_offset_llimit值小于最小左值时,如果改url有cache,解释数据不完整,只要range请求的最小值在cache范围内,则hit
    if (httpHdrRangeFirstOffset(range) <= entry->mem_obj->inmem_hi)  
          return 0;   
    return 1;  //miss
}

httpHdrRangeOffsetLimit(HttpHdrRange * range)
{
    if (NULL == range)
          return 0;
    if (!Config.rangeOffsetLimit)    //为0,走miss
          return 1;
    if (-1 == Config.rangeOffsetLimit)  //为-a,走hit
          return 0;
   //  参数大于最小的range左值时,走hit,此处理解可能有点别扭,流程能走到这说明storeenty已经创建了,大于range左值时等着被hit就行。
    if (Config.rangeOffsetLimit >= httpHdrRangeFirstOffset(range))  
           return 0;
    return 1;
}

3、回源时对range头的处理
httpSendRequest--->httpBuildRequestPrefix--->httpBuildRequestHeader--->
                                //参考上面,rangeoffsetlimit值为0 或range最小左值大于rangeoffsetlimit值时,需Range回源
                                 if (httpHdrRangeOffsetLimit(orig_request->range))   \  
                                            we_do_ranges = 0;
                                  else
                                           we_do_ranges = 1;---->     
                                                                          switch (e->id) {
                                                                                    case HDR_REQUEST_RANGE: 
                                                                                             if( !we_do_ranges ) 
                                                                                                             httpHeaderAddClone(hdr_out, e);  添加Range头
                                                                                                              break;
                                                                           }
4、组织range头回复数据给网民
clientSendHeaders--》clientCloneReply--》clientBuildReplyHeader--》clientBuildRangeHeader--》 if(1个range范围)httpHdrRangeBoundaryStr ; else httpHdrRangeBoundaryStr

5、range头回源时缓冲区问题
range_offset_limit 为0 时,miss且带range头回源。因为不缓存,此时每个请求都需要有一个独立的storeentry来存储源站数据。
流程参考<squid 折叠回源解析> 唯一不同是在第二个以后的请求在httpReadReply->httpProcessReplyHeader -->
                                                                switch (httpCachableReply(httpState)) {
case 1:
             httpMakePublic(entry);
  break;
          case 0:
   httpMakePrivate(entry);}
      httpCachableReply {   //只要是回复状态是206的,就设置私有标签,这样就可以满足每个请求一个storeentry缓冲区使用
             switch (httpState->entry->mem_obj->reply->sline.status) {
                      case HTTP_PARTIAL_CONTENT:  
                           return 0; 
              }   
    }
0 0