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

varnish architecture

中间框框圈住的是主要的服务进程 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

状态机的各个状态之间转换的流程图

varnish client side

状态机处理的主入口

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 各个状态转换流程

varnish architecture

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 线程池显然会不够使用。

参考资料