一、场景
网民与cache回源是多对一的关系。折叠回源时,如果同时有多个网名请求,fwd在读取源站数据后,会循环发给网民。
二、问题
1、fwd一次没有读全源站数据,fwd循环发往网民时用的是写事件,如果还没有触发这些写事件,源站读事件又多次被触发后会是什么情况? 这样会导致缓冲区会越来越大。
此场景一般是上游源站网速大于下游网速时。
2、对于上述问题squid引入了延迟读的操作。详解如下:
三、详解
storeDeferRead 延迟回源读数据 . 将entry 打上标识 ENTRY_DEFER_READ,且吊销读事件
storeResumeRed 恢复回源读数据 . 清除 entry 标识ENTRY_DEFER_READ,且挂起读事件。
read_ahead_gap 这个标签决定了发送到客户端预取缓冲区的大小
延迟读可分三个地方讲,注册、调用、恢复。场景是数据pending,一次读不完,需多次才能读完。如下:
1、注册
a、fwd向源站发送数据,httpSendComplete 完成后,对fwd读事件设置了defer_check
b、httpSendRequest-》comm_write_mbuf -》sendHeaderDone-》httpSendComplete-》commSetDefer-》defer_check (
fwdCheckDeferRead)
c、defer_check作用只是在 fwd读事件处理中做了一个条件判断,并将读事件吊销,本次读操作不受影响。(具体代码看调用)
2、调用
a、fwd对源站的读事件被触发。
b、Main -》omm_select -》do_comm_select-》 comm_call_handlers -》switch(commDeferRead )-》 F->
defer_check(fd, F->defer_data);
c、
fwdCheckDeferRead ---》if (fd >= 0 && mem->inmem_hi - storeLowestMemReaderOffset(e) > Config.readAheadGap) {
storeDeferRead(e, fd); return 1; }
3、恢复
a、fwd向网民发送数据,只要有一次发完且swapout,则恢复读事件。这样能保证上游网速快的情况下,fwd源站读事件多次迅速触发,扩大数据缓冲区(storeentry)。
b、上游网速快场景,用一个网民解释最简单,每次读完源站要确保将数据真实发送给网民再接着读源站。这样保证了低内存即可实现转发。
c、
swapout->
storeSwapOutMaintainMemObject 函数 , 见代码红色部分是恢复延迟回源读数据 .
if (mem->inmem_lo != new_mem_lo) {
mem->inmem_lo = stmemFreeDataUpto(&mem->data_hdr, new_mem_lo);
/* If ENTRY_DEFER_READ is set, then the client side will continue to
* flush until it has less than READ_AHEAD_GAP bytes in memory */
if (EBIT_TEST(e->flags, ENTRY_DEFER_READ)) {
if (mem->inmem_hi - mem->inmem_lo <= Config.readAheadGap) {
storeResumeRead(e);
}
}
}