一、说明
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来存储源站数据。
流程参考 唯一不同是在第二个以后的请求在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;
}
}