Varnish 源码分析(主流程)
Varnish 源码分析(主流程)
varnish 简介
varnish 是一个 web cache 服务程序,它由 freeBSD 内核开发者 Poul-Henning Kamp 开发,作者的个人主页 http://phk.freebsd.dk/
由于 varnish 设计的领域小语言 VCL(Varnish Configuration Language) 灵活定制和性能好而备受关注。国外 CDN 厂商 fastly 就用了 varnish 作为他们的主要服务软件。
varnish 分为开源版和商业版,分析目标是开源版本,可以在这里找到 https://github.com/varnishcache/varnish-cache ,具体的版本 varnish-6.0.7。
Poul-Henning Kamp 还分享了一些关于 varnish 设计方面的资料,可以在 http://phk.freebsd.dk/pubs/ 页面中查找到。
varnish 架构图
下图摘自 varnish 作者分享的 varnish_tech.pdf
中间框框圈住的是主要的服务进程 varnishd ,它由 manager 和 cacher 两部分组成。其中 manager 是父进程,cacher 是 fork 出来的子进程,主要对外提供服务的是 cacher。
在 varnishd 运行中中会创建出如下表的线程
Thread-name | Amount of threads | Task |
---|---|---|
cache-worker | One per active connection | Handle requests |
cache-main | One | Startup |
ban lurker | One | Clean bans |
acceptor | One | Accept new connections |
epoll/kqueue | Configurable, default: 2 | Manage thread pools |
expire | One | Remove old content |
backend poll | One | per backend poll |
分析源码的过程中,会去除与主线无关的逻辑代码,争取呈现一个清晰的运行逻辑。
varnish 源码目录结构
.
├── bin
│ ├── varnishd # 主要的源码分析对象
│ │ ├── cache
│ │ ├── common
│ │ ├── fuzzers
│ │ ├── hash
│ │ ├── hpack
│ │ ├── http1
│ │ ├── http2
│ │ ├── mgt
│ │ ├── proxy
│ │ ├── storage
│ │ └── waiter
│ ├── varnishadm # 管理员命令行
│ ├── varnishhist # 响应时间的统计柱状图
│ ├── varnishlog # 共享内存的日志读取
│ ├── varnishncsa # 读取共享内存的日志,输出 Apache 日志格式
│ ├── varnishstat # 统计输出
│ ├── varnishtest # 功能测试框架与用例
│ └── varnishtop # 内存日志的实时 top 视图
├── doc
├── etc
├── lib
├── tools
...
源码分析
manager (父进程)
varnishd main 函数入口的实现在 ./bin/varnishd/mgt/mgt_main.c
文件中。mgt 开头的文件名称可以得知它是对应架构图中的 manager 进程(父进程)
int
main(int argc, char * const *argv)
{
// 初始化传输协议接口,目前支持的有 [PROXY,HTTP/1,H2]
XPORT_Init();
// -a 参数解析,未特别指定 transport 的话,默认使用 http transport
MAC_Arg(optarg);
// 有 -f 指定 VCL-file
while (!VTAILQ_EMPTY(&f_args)) {
// 根据 VCL 生成 .h/.c,并编译 .so,vcl 模块名为 boot
mgt_vcl_startup(cli, fa->src,
VTAILQ_EMPTY(&f_args) ? "boot" : NULL, fa->farg, C_flag);
}
if (mgt_has_vcl() && ! d_flag)
// 启动子进程, 也就是上图中对应的 cacher
u = MCH_Start_Child();
else
u = 0;
...
}
// HTTP 的 transport 结构体定义
struct transport HTTP1_transport = {
.name = "HTTP/1",
.proto_ident = "HTTP",
.magic = TRANSPORT_MAGIC,
// 响应客户端
.deliver = V1D_Deliver,
.minimal_response = http1_minimal_response,
// 接受新的连接,并开始处理时调用 new_session
.new_session = http1_new_session,
// 请求有 body 的情况处理
.req_body = http1_req_body,
// 请求出现失败的处理
.req_fail = http1_req_fail,
.req_panic = http1_req_panic,
.sess_panic = http1_sess_panic,
.unwait = http1_unwait,
};
MCH_Start_Child
只是调用 mgt_launch_child(NULL)
的函数包装,直接跳到 mgt_launch_child
函数实现
static void
mgt_launch_child(struct cli *cli)
{
// fork 克隆出子进程
if ((pid = fork()) < 0) {
// fork 失败处理
VJ_master(JAIL_MASTER_LOW);
perror("Could not fork child");
exit(1);
}
// 在子进程里
if (pid == 0) {
// 子进程的主要入口
child_main(mgt_param.sigsegv_handler, mgt_param.wthread_stacksize);
exit(0);
}
// 发送 vcl.load boot.xxxx/vgc.so 和 vcl.use boot 命令给子进程
// vcl.load 是根据 VCL 文件编译好的 so
// vcl.use boot 是通知 子进程 进行加载并初始化 so 初始函数
if (mgt_push_vcls(cli, &u, &p)) {
...
}
// 发送 start 通知子进程可以开始进行服务了
if (mgt_cli_askchild(&u, &p, "start\n")) {
...
}
}
到目前 manager(父进程)的分析完毕,接下来是子进程处理流程。
cacher(子进程)
为了更好的描述,下面将术语的统一,上面提到的子进程统一称为 cacher,方便描述。
child_main
入口函数定义在 ./bin/varnishd/cache/cache_main.c
中
void
child_main(int sigmagic, size_t altstksz)
{
// 内部调用 CLI_AddFuncs(vcl_cmds) 注册 vcl.load 等命令处理接口
VCL_Init();
// 启动 backend-poller 后台线程
VBP_Init();
// 注册 backend 相关命令
VDI_Init();
// 启动放牧线程,并准备用于服务的 worker 线程池
Pool_Init();
// 启动 cache-exp 后台线程
EXP_Init();
// 启动 ban lurker 后台线程
BAN_Init();
// 注册 start 等命令处理接口
VCA_Init();
// 调用 open 接口初始化存储
STV_open();
// 监听 manager 发送的命令
CLI_Run();
}
manager 进程发送的协议定义在 ./include/tbl/cli_cmd.h
中
下面就是 manager 在子进程启动后会发送的 vcl.load 和 start 协议定义
// 通知 cacher 进行加载 vcl.so
CLI_CMD(VCL_LOAD,
"vcl.load",
"vcl.load <configname> <filename> [auto|cold|warm]",
"Compile and load the VCL file under the name provided.",
"",
2, 3
)
// 通知 cacher 可以监听端口并开始服务
CLI_CMD(SERVER_START,
"start",
"start",
"Start the Varnish cache process.",
"",
0, 0
)
循环监听 manager 发送命令过来的函数 CLI_Run
内部调用 VCLS_Poll
int
VCLS_Poll(struct VCLS *cs, const struct cli *cli, int timeout)
{
// 等待命令
j = poll(pfd, 1, timeout);
...
if (pfd[0].revents & POLLHUP)
k = 1;
else {
// 读取 manager 发送过来的命令
i = read(cfd->fdi, buf, sizeof buf);
if (i <= 0)
k = 1;
else
// cls_feed 函数内对 manager 发送过来的命令与注册的协议进行匹配,匹配上则调用应函数接口。
k = cls_feed(cfd, buf, buf + i);
}
}
“vcl.load” 跳过不分析了,主要流程聚焦在 “start” 命令发送过来后,调用对应的调用接口 ccf_start
// start 命令定义的接口在 ./bin/varnishd/cache/cache_acceptor.c
static struct cli_proto vca_cmds[] = {
{ CLICMD_SERVER_START, "", ccf_start },
{ CLICMD_DEBUG_LISTEN_ADDRESS, "d", ccf_listen_address },
{ NULL }
};
static void v_matchproto_(cli_func_t)
ccf_start(struct cli *cli, const char * const *av, void *priv)
{
VTAILQ_FOREACH(ls, &heritage.socks, list) {
// 监听端口
if (listen(ls->sock, cache_param->listen_depth)) {
...
}
...
}
// 启动 socket 设置线程
AZ(pthread_create(&VCA_thread, NULL, vca_acct, NULL));
}
static void * v_matchproto_()
vca_acct(void *arg)
{
// 设置监听完毕标志位
pool_accepting = 1;
...
}
到目前为止,服务端口已经监听完毕,接下来是对外主要服务的线程池部分。
服务线程池
回到 child_main
中初始化放牧线程的流程,放牧线程的职责是启动和回收线程池中的线程
void
Pool_Init(void)
{
// 创建放牧线程
AZ(pthread_create(&thr_pool_herder, NULL, pool_poolherder, NULL));
// 等待线程池部分就绪
while (!VSC_C_main->pools)
(void)usleep(10000);
}
// 放牧线程,用于创建和回收线程池
static void * v_matchproto_()
pool_poolherder(void *priv)
{
nwq = 0;
while (1) {
// 线程不足时增加线程池
if (nwq < cache_param->wthread_pools) {
// 创建线程池
pp = pool_mkpool(nwq);
if (pp != NULL) {
VSC_C_main->pools++;
nwq++;
continue;
}
} else if (nwq > cache_param->wthread_pools && DO_DEBUG(DBG_DROP_POOLS)) {
// 线程池超过了指定数量,就回收一个线程池
...
}
// 回收挂起的线程池
...
}
}
创建线程池的实现
static struct pool *
pool_mkpool(unsigned pool_no)
{
// 线程池
AZ(pthread_create(&pp->herder_thr, NULL, pool_herder, pp));
// 等待线程池就绪
while (VTAILQ_EMPTY(&pp->idle_queue))
(void)usleep(10000);
// 函数内部调用如下代码
// ps->task->func = vca_accept_task;
// 分配一个 vca_accept_task 函数任务给新创建的线程池,作为调用入口
VCA_NewPool(pp);
}
void*
pool_herder(void *priv)
{
while (!pp->die || pp->nthr > 0) {
wthread_min = cache_param->wthread_min;
if (pp->die)
wthread_min = 0;
// 如果需要,生成更多的线程 (cache_worker)
if (pp->nthr < wthread_min ||
(pp->lqueue > 0 && pp->nthr < cache_param->wthread_max)) {
// 创建 cache-worker 线程
pool_breed(pp);
continue;
}
// 其他回收操作
}
}
static void
pool_breed(struct pool *qp)
{
// 创建 cache-worker
if (pthread_create(&tp, &tp_attr, pool_thread, pi)) {
...
}
}
static void *
pool_thread(void *priv)
{
// 任务线程主要执行入口
WRK_Thread(pi->qp, pi->stacksize, cache_param->workspace_thread);
}
static void
WRK_Thread(struct pool *qp, size_t stacksize, unsigned thread_workspace)
{
// 任务主循环
Pool_Work_Thread(qp, w);
}
static void
Pool_Work_Thread(struct pool *pp, struct worker *wrk)
{
while (1) {
// 锁住任务队列
// 获取任务队列的任务对象
do {
// 执行任务对象的函数
// 之后在 worker 线程中回调其他阶段函数,只需要修改该指针即可
tp->func(wrk, tp->priv);
} while (tp->func != NULL);
}
}
工作者线程创建完毕,每个工作线程从 task->func
。
连接处理的地方
上面看到线程池创建时,会投递了一个 vca_accept_task
任务到任务队列中。
该任务处理接收新的客户端连接。
static void v_matchproto_(task_func_t)
vca_accept_task(struct worker *wrk, void *arg)
{
// 等待监听线程端口监听完毕
while (!pool_accepting)
VTIM_sleep(.1);
while (!ps->pool->die) {
wa.acceptlsock = ls;
wa.acceptaddrlen = sizeof wa.acceptaddr;
do {
i = accept(ls->sock, (void*)&wa.acceptaddr, &wa.acceptaddrlen);
} while (i < 0 && errno == EAGAIN && !ps->pool->die);
// 将 vca_make_session 处理函数放入线程任务队列
if (!Pool_Task_Arg(wrk, TASK_QUEUE_VCA, vca_make_session, &wa, sizeof wa)) {
if (!ps->pool->die) {
AZ(Pool_Task(wrk->pool, ps->task, TASK_QUEUE_VCA));
return;
}
}
}
}
static void v_matchproto_(task_func_t)
vca_make_session(struct worker *wrk, void *arg)
{
// Req_New 内部初始化状态机为 R_STP_TRANSPORT
// req->req_step 是状态机结构体(详情见 "varnish 状态机" 段落)
req = Req_New(wrk, sp);
// 传递监听端口时设置的 transport 对象指针
SES_SetTransport(wrk, sp, req, wa->acceptlsock->transport);
}
void
SES_SetTransport(struct worker *wrk, struct sess *sp, struct req *req,
const struct transport *xp)
{
sp->sattr[SA_TRANSPORT] = xp->number;
// 设置当前请求的 transport 对象
req->transport = xp;
// 当前 worker 线程下一个执行函数变更为 transport.new_session
wrk->task->func = xp->new_session;
wrk->task->priv = req;
}
进入 new_session
传输函数调用,默认是 HTTP 的处理回调定义是 http1_new_session
函数
static void v_matchproto_(task_func_t)
http1_new_session(struct worker *wrk, void *arg)
{
http1_setstate(sp, H1NEWREQ);
// worker 的执行函数变更 http1_req
wrk->task->func = http1_req;
wrk->task->priv = req;
}
static void v_matchproto_(task_func_t)
http1_req(struct worker *wrk, void *arg)
{
// 解析 HTTP 请求头
HTTP1_Session(wrk, req);
}
static void
HTTP1_Session(struct worker *wrk, struct req *req)
{
// 确保 transport 为 HTTP1_transport
req->transport = &HTTP1_transport;
// http 头解析阶段
while (1) {
if (st == H1NEWREQ) {
// 内部调用 read 函数阻塞读取请求并进行解析
// 解析完毕,将状态设置为 H1PROC
http1_setstate(sp, H1PROC);
} else if (st == H1PROC) {
// 持久链接,有多个请求的情况
req->task->func = http1_req;
req->task->priv = req;
wrk->stats->client_req++;
// 处理 HTTP 请求,开始执行 varnish 的状态机机制
if (CNT_Request(req) == REQ_FSM_DISEMBARK)
return;
// 处理完成,做清理动作
req->task->func = NULL;
req->task->priv = NULL;
http1_setstate(sp, H1CLEANUP);
} else if (st == H1CLEANUP) {
// 发生错误,清理当前 session 资源
} else {
WRONG("Wrong H1 session state");
}
}
}
调用 CNT_Request
函数,这里就要开始进入 varnish 状态机了
varnish 状态机
状态机函数与实现定义在 ./bin/varnishd/cache/cache_req_fsm.c
中。
如下所示
#define REQ_STEPS \
REQ_STEP(transport, TRANSPORT, ) \
REQ_STEP(restart, RESTART, static) \
REQ_STEP(recv, RECV, ) \
REQ_STEP(pipe, PIPE, static) \
REQ_STEP(pass, PASS, static) \
REQ_STEP(lookup, LOOKUP, static) \
REQ_STEP(purge, PURGE, static) \
REQ_STEP(miss, MISS, static) \
REQ_STEP(fetch, FETCH, static) \
REQ_STEP(deliver, DELIVER, static) \
REQ_STEP(vclfail, VCLFAIL, static) \
REQ_STEP(synth, SYNTH, static) \
REQ_STEP(transmit, TRANSMIT, static)
#define REQ_STEP(l, U, priv) \
static req_state_f cnt_##l; \
priv const struct req_step R_STP_##U[1] = { \
{ .name = "Fetch Step" #l, .func = cnt_##l, } \
};
REQ_STEPS
#undef REQ_STEP
例如 REQ_STEP(transport, TRANSPORT)
宏定义展开为
static req_state_f cnt_transport;
const struct req_step R_STP_TRANSPORT[1] =
{
{
.name = "Fetch Step transport",
// 展开为 cnt_ 开头的函数
.func = cnt_transport
}
};
展开的状态机列表
状态机结构 | 执行入口 |
---|---|
R_STP_TRANSPORT | cnt_transport |
R_STP_RESTART | cnt_restart |
R_STP_RECV | cnt_recv |
R_STP_PIPE | cnt_pipe |
R_STP_PASS | cnt_pass |
R_STP_LOOKUP | cnt_lookup |
R_STP_PURGE | cnt_purge |
R_STP_MISS | cnt_miss |
R_STP_FETCH | cnt_fetch |
R_STP_DELIVER | cnt_deliver |
R_STP_VCLFAIL | cnt_vclfail |
R_STP_SYNTH | cnt_synth |
R_STP_TRANSMIT | cnt_transmit |
状态机的各个状态之间转换的流程图
状态机处理的主入口
enum req_fsm_nxt
CNT_Request(struct req *req)
{
// 状态机入口只允许 R_STP_LOOKUP 与 R_STP_TRANSPORT.
assert(req->req_step == R_STP_LOOKUP ||
req->req_step == R_STP_TRANSPORT);
// 如果状态机执行后,nxt 值为 REQ_FSM_MORE,就继续执行状态机
for (nxt = REQ_FSM_MORE; nxt == REQ_FSM_MORE; ) {
// 调用状态机定义的函数,R_STP_TRANSPORT
nxt = req->req_step->func(wrk, req);
}
}
// R_STP_TRANSPORT 对应的函数
static enum req_fsm_nxt v_matchproto_(req_state_f)
cnt_transport(struct worker *wrk, struct req *req)
{
// 对请求头进行判断处理
...
// 下一个阶段为 R_STP_RECV
req->req_step = R_STP_RECV;
return (REQ_FSM_MORE);
}
// R_STP_RECV 对应的函数
static enum req_fsm_nxt v_matchproto_(req_state_f)
cnt_recv(struct worker *wrk, struct req *req)
{
// 初始化请求,为调用 vcl_recv 做准备
cnt_recv_prep(req, ci);
// 调用 VCL sub vcl_recv
VCL_recv_method(req->vcl, wrk, req, NULL, NULL);
// 如果处理失败,进入 R_STP_VCLFAIL 阶段
if (wrk->handling == VCL_RET_FAIL) {
req->req_step = R_STP_VCLFAIL;
return (REQ_FSM_MORE);
}
recv_handling = wrk->handling;
// 调用 VCL sub vcl_hash
VSHA256_Init(&sha256ctx);
VCL_hash_method(req->vcl, wrk, req, NULL, &sha256ctx);
// 如果计算失败,则根据返回值选择对应的状态机
// 如果计算成功就进入 R_STP_LOOKUP
if (wrk->handling == VCL_RET_FAIL)
recv_handling = wrk->handling;
else
assert(wrk->handling == VCL_RET_LOOKUP);
VSHA256_Final(req->digest, &sha256ctx);
switch (recv_handling) {
// 根据 VCL vcl_recv 返回值,设置下一个状态
}
return (REQ_FSM_MORE);
}
// R_STP_LOOKUP
static enum req_fsm_nxt v_matchproto_(req_state_f)
cnt_lookup(struct worker *wrk, struct req *req)
{
// 预处理 vary
VRY_Prep(req);
// 查找 key
lr = HSH_Lookup(req, &oc, &busy);
if (lr == HSH_MISS || lr == HSH_HITMISS) {
// 进入 miss 状态
req->req_step = R_STP_MISS;
return (REQ_FSM_MORE);
}
if (lr == HSH_HITPASS) {
// 进入 pass 状态
req->req_step = R_STP_PASS;
return (REQ_FSM_MORE);
}
// 调用 VCL sub vcl_hit
VCL_hit_method(req->vcl, wrk, req, NULL, NULL);
switch (wrk->handling) {
// 根据 VCL vcl_hit 返回值,设置下一个状态
}
return (REQ_FSM_MORE);
}
// cache miss
static enum req_fsm_nxt v_matchproto_(req_state_f)
cnt_miss(struct worker *wrk, struct req *req)
{
// 调用 VCL sub vcl_miss
VCL_miss_method(req->vcl, wrk, req, NULL, NULL);
// 根据 VCL vcl_miss 返回值,设置下一个状态
switch (wrk->handling) {
case VCL_RET_FETCH:
// 回源拉取数据,并等待数据
VBF_Fetch(wrk, req, req->objcore, req->stale_oc, VBF_NORMAL);
// 进入 FETCH 阶段
req->req_step = R_STP_FETCH;
return (REQ_FSM_MORE);
// 其他返回值状态
...
}
return (REQ_FSM_MORE);
}
// 回源拉取数据的调用
void
VBF_Fetch(struct worker *wrk, struct req *req, struct objcore *oc,
struct objcore *oldoc, enum vbf_fetch_mode_e mode)
{
// 准备 worker 线程任务
bo->fetch_task->priv = bo;
// vbf_fetch_thread 是 fetch task 状态机的调用入口
bo->fetch_task->func = vbf_fetch_thread;
// 投递 fetch task 到 workers 中
if (Pool_Task(wrk->pool, bo->fetch_task, TASK_QUEUE_BO)) {
...
} else {
// 等待 fetch_task 信号
if (mode == VBF_BACKGROUND) {
ObjWaitState(oc, BOS_REQ_DONE);
} else {
ObjWaitState(oc, BOS_STREAM);
}
}
}
fetch task 的状态机定义在 ./bin/varnishd/cache/cache_fetch.c
中
如下所示
#define FETCH_STEPS \
FETCH_STEP(mkbereq, MKBEREQ) \
FETCH_STEP(retry, RETRY) \
FETCH_STEP(startfetch, STARTFETCH) \
FETCH_STEP(condfetch, CONDFETCH) \
FETCH_STEP(fetch, FETCH) \
FETCH_STEP(fetchbody, FETCHBODY) \
FETCH_STEP(fetchend, FETCHEND) \
FETCH_STEP(error, ERROR) \
FETCH_STEP(fail, FAIL) \
FETCH_STEP(done, DONE)
typedef const struct fetch_step *vbf_state_f(struct worker *, struct busyobj *);
struct fetch_step {
const char *name;
vbf_state_f *func;
};
#define FETCH_STEP(l, U) \
static vbf_state_f vbf_stp_##l; \
static const struct fetch_step F_STP_##U[1] = { \
{ .name = "Fetch Step" #l, .func = vbf_stp_##l, } \
};
FETCH_STEPS
#undef FETCH_STEP
// 宏展开例子
// FETCH_STEP(mkbereq, MKBEREQ)
static vbf_state_f vbf_stp_mkbereq;
static const struct fetch_step F_STP_MKBEREQ[1] =
{
{
.name = "Fetch Step mkbereq",
.func = vbf_stp_mkbereq,
}
};
展开的状态机列表
状态机结构体 | 函数 |
---|---|
F_STP_MKBEREQ | vbf_stp_mkbereq |
F_STP_RETRY | vbf_stp_retry |
F_STP_STARTFETCH | vbf_stp_startfetch |
F_STP_CONDFETCH | vbf_stp_condfetch |
F_STP_FETCH | vbf_stp_fetch |
F_STP_FETCHBODY | vbf_stp_fetchbody |
F_STP_FETCHEND | vbf_stp_fetchend |
F_STP_ERROR | vbf_stp_error |
F_STP_FAIL | vbf_stp_fail |
F_STP_DONE | vbf_stp_done |
Backend Side 各个状态转换流程
fetch task 的任务函数入口 vbf_fetch_thread
static void v_matchproto_(task_func_t)
vbf_fetch_thread(struct worker *wrk, void *priv)
{
// fetch 的最初状态
stp = F_STP_MKBEREQ;
// 执行 fetch task 状态机
while (stp != F_STP_DONE) {
stp = stp->func(wrk, bo);
}
}
fetch 每个状态的逻辑这里就不再展开。当线程拉取到数据后,通知 worker,并进入 R_STP_FETCH 阶段。
static enum req_fsm_nxt v_matchproto_(req_state_f)
cnt_fetch(struct worker *wrk, struct req *req)
{
// 发生错误,进入 R_STP_SYNTH 阶段
if (req->objcore->flags & OC_F_FAILED) {
req->err_code = 503;
req->req_step = R_STP_SYNTH;
return (REQ_FSM_MORE);
}
// 进入 R_STP_DELIVER 阶段
req->req_step = R_STP_DELIVER;
return (REQ_FSM_MORE);
}
static enum req_fsm_nxt v_matchproto_(req_state_f)
cnt_deliver(struct worker *wrk, struct req *req)
{
// 调用 VCL sub vcl_deliver
VCL_deliver_method(req->vcl, wrk, req, NULL, NULL);
// 如果返回值不是 VCL_RET_DELIVER
if (wrk->handling != VCL_RET_DELIVER) {
// 状态机设置为返回的状态
return (REQ_FSM_MORE);
}
// 进入 R_STP_TRANSMIT 状态
assert(wrk->handling == VCL_RET_DELIVER);
req->req_step = R_STP_TRANSMIT;
return (REQ_FSM_MORE);
}
static enum req_fsm_nxt v_matchproto_(req_state_f)
cnt_transmit(struct worker *wrk, struct req *req)
{
// HTTP/1 deliver 的函数是 V1D_Deliver
req->transport->deliver(req, boc, sendbody);
// 状态机完成
return (REQ_FSM_DONE);
}
HTTP/1 的 DELIVER
void v_matchproto_(vtr_deliver_f)
V1D_Deliver(struct req *req, struct boc *boc, int sendbody)
{
// 发送 http 响应头部
hdrbytes = HTTP1_Write(req->wrk, req->resp, HTTP1_Resp);
if (sendbody) {
// 遍历内容,发送给客户端
err = VDP_DeliverObj(req->vdc, req->objcore);
if (!err && chunked)
V1L_EndChunk(req->wrk);
}
}
到这里 varnishd 主流程就分析结束了。
varnish 以线程池来处理请求,read / write 都是阻塞调用,如果上游响应慢,或者客户端接收慢,这种请求并发多的话,worker 线程池显然会不够使用。