Chinaunix首页 | 论坛 | 博客
  • 博客访问: 60217
  • 博文数量: 40
  • 博客积分: 1607
  • 博客等级: 上尉
  • 技术积分: 382
  • 用 户 组: 普通用户
  • 注册时间: 2009-12-09 16:35
文章分类

全部博文(40)

文章存档

2011年(1)

2010年(30)

2009年(9)

我的朋友

分类: LINUX

2010-02-28 14:23:39

Varnish负载均衡的分析

 

最近分析了varnis-2.0.4的一部分代码,主要侧重于其在负载均衡部分的实现。通过阅读一些参考文献,以及自己的一些见解我分析,将代码分析文档整理于此。

首先将varnish整体工作流程在此进行介绍。

1.         Varnish 的总体结构

 

Varnish 主要有两个进程:管理进程和 cache 子进程。

1)        管理进程主要就是对于varnish的整个工作状态进行的调整和设置。编译运行之后,它将建立一个守护进程varnishdVarnishd不断folk()cache子进程来处理HTTP请求。它的实现部分在源代码中bin/varnishd/目录中,主要文件有varnishd.c mgt.hmgt_chld.cmgt_param.c mgt_cli.hmgt_pool.c mgt_vcc.cmgt_cli.hmgt_cli.c

 

2)        下面谈谈cache子进程。

Cache子进程包含了实现命令行加载、请求处理、缓存以及负载均衡的所有线程。分别为:命令行接受处理线程(CLI_Run) ,放牧线程(wrk_herder_thread),放牧超时线程(wrk_herdtimer_thread),请求接受线程(vca_acct),数据接受线程(vca_main),很多工作线程(wrk_thread)HTTP对象超时线程(exp_timer),后台服务器连接探测线程(vbp_wrk_poll_backend)

主要涉及到的文件有:cache_lck.c cache_panic.ccache_cli.ccache_fetch.ccache_center.ccache_vcl.ccache_http.ccache_session.ccache_backend_cfg.ccache_backend_pool.ccache_backend.hcache_pool.ccache_expire.ccache_hash.ccache_accptor.c

Cache 子进程处理所有具体工作,各个线程的任务包括:

l  命令行接受处理线程(CLI_Run):接受从管理进程通过管道传过来的命令,做出相应决定。其中初始时由管理进程默认产生,三个命令(vcl.loadvcl.usestart)来启动后台服务器连接探测线程 和两个接受线程。

l  放牧线程(wrk_herder_thread):用于产生工作线程池。线程不足时会增加线程池。

l  放牧超时检查线程(wrk_herdtimer_thread清理一些工作超时的工作线程。

l  请求接受线程(vca_acct接受 HTTP 初次请求,并叫醒某个工作线程,处理请求。

l  数据接受线程(vca_main):在发送数据以后,继续可能的再次请求,并把请求交给工作线程。

l  工作线程(wrk_thread):不断处理请求,进入状态机。如果缓存没有命中,还需要从后台服务取过数据,存入缓存并回复。然后把该连接通过管道转给数据接受线程并睡去。

l  HTTP 对象超时检查线程(exp_timer):检查二叉堆中 HTTP 超时对象,删除之。

l  后台服务器连接探测线程(vbp_wrk_poll_backend):针对不同的后台服务器组进行轮询,检查存活与否。

各线程的工作流程大致如图一所示

图一:cache子进程各个线程流程图

 

2.         负载均衡实现的分析

 

就目前分析来看,Cache子进程的代码实现部分主要由cache_main.c这个文件为主要脉络的。Cache_main.c中将cache子进程的各个线程一一初始化。

 

目前我所关注的重点在于wrk_thread部分,它是实现varnish负载均衡的主要内容。

 

1)         wrk_thread的作用:不断处理请求,进入状态机。如果缓存没有命中,还需要从后台服务取过数据,存入缓存并回复。然后把该连接通过管道转给数据接受线程并sleep

2)         wrk_thread的工作流程:

图二:cache子进程中,wrk_thread线程工作流程

3)        Wrk_thread的代码实现分析:wrk_thread线程在cache_main.c文件中初始化(代码 WRK_Init() cache_main.cline 121),具体实现在cache_pool.c文件中出现。

Cache_pool.c文件中主要函数有:

static void

wrk_addpools(const unsigned pools):增添work线程池

static void *

wrk_herder_thread(void *priv) :放牧进程,用于产生工作线程池。线程不足时会增加线程;

static void *

wrk_herdtimer_thread(void *priv):放牧超时检查线程,清理一些工作超时的工作线程。

static void

wrk_breed_flock(struct wq *qp):在需要并且空间允许的情况下,产生新的线程

static void

wrk_decimate_flock(struct wq *qp, double t_idle, struct varnish_stats *vs):检查空闲或者已经执行完的线程,从线程池中清除。

static void *

wrk_thread(void *priv):实际的工作线程,实现主要功能。

针对此函数,进行具体分析:

//摘自cache_pool.c//


//////////////////////////////////////////////////////////////////


static void *

wrk_thread(void *priv)

{

       struct worker *w, ww;

       struct wq *qp;

       unsigned char wlog[params->shm_workspace];

       struct SHA256Context sha256;

 

       THR_SetName("cache-worker");

       w = &ww;

       CAST_OBJ_NOTNULL(qp, priv, WQ_MAGIC);

       memset(w, 0, sizeof *w);

       w->magic = WORKER_MAGIC;

       w->lastused = NAN;

       w->wlb = w->wlp = wlog;

       w->wle = wlog + sizeof wlog;

       w->sha256ctx = &sha256;

       AZ(pthread_cond_init(&w->cond, NULL));

 

       VSL(SLT_WorkThread, 0, "%p start", w);

 

       Lck_Lock(&qp->mtx);

       qp->nthr++;

       while (1) {

              CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);

 

              /* Process overflow requests, if any */

              w->wrq = VTAILQ_FIRST(&qp->overflow);

              if (w->wrq != NULL) {

                     VTAILQ_REMOVE(&qp->overflow, w->wrq, list);

                     qp->nqueue--;

              } else {

                     if (isnan(w->lastused))

                            w->lastused = TIM_real();

                     VTAILQ_INSERT_HEAD(&qp->idle, w, list);

                     Lck_CondWait(&w->cond, &qp->mtx);

              }

              if (w->wrq == NULL)

                     break;

              Lck_Unlock(&qp->mtx);

              AN(w->wrq);

              AN(w->wrq->func);

              w->lastused = NAN;

              w->wrq->func(w, w->wrq->priv);

              AZ(w->wfd);

              assert(w->wlp == w->wlb);

              w->wrq = NULL;

              Lck_Lock(&qp->mtx);

       }

       qp->nthr--;

       Lck_Unlock(&qp->mtx);

 

       VSL(SLT_WorkThread, 0, "%p end", w);

       if (w->vcl != NULL)

              VCL_Rel(&w->vcl);

       AZ(pthread_cond_destroy(&w->cond));

       if (w->srcaddr != NULL)

              free(w->srcaddr);

       if (w->nobjhead != NULL) {

              Lck_Delete(&w->nobjhead->mtx);

              FREE_OBJ(w->nobjhead);

       }

       if (w-> NULL)

              STV_free(w->nobj->objstore);

       return (NULL);

}

 

 

3.         参考文献

[1]        

[2]      

[3]      http://yaoweibin2008.blog.163.com/blog/static/11031392008102163141550/

[4]      

[5]      Varnish应用技术指南V2.1》,Freeke 整理于2008-12-15

 

以上为目前分析varnish所得,发布在此欢迎探讨,以后会持续更新。

阅读(826) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~