Chinaunix首页 | 论坛 | 博客
  • 博客访问: 825297
  • 博文数量: 167
  • 博客积分: 7173
  • 博客等级: 少将
  • 技术积分: 1671
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-04 23:07
文章分类

全部博文(167)

文章存档

2018年(1)

2017年(11)

2012年(2)

2011年(27)

2010年(88)

2009年(38)

分类: 系统运维

2010-09-06 22:58:59

最近在测试nginx proxxy_cache,对其内存的自管理和disk内存怎么使用没有
很清楚的概念,于是就不断的用压力工具压以便能从量上归纳出其中的规律,
不过看来偷懒是不行,乖乖浏览了一下其源代码(网上没相关其内存回收的资料啊),本人能力不够,各位大大
要指点。
其基本管理规律如下:
1.插入,btree插入模式;
2.以最少访问最早删除原则;
3.每次存入数据进内存,disk肯定有一份除非mem
  大于disk(试过这样测试,命中率最高只能去到78%)
4.nginx proxy_cache  disk已经存入,它就很懒了不管。除非发现同键值
  的cache插入,或update操作
5.stop nginx后内存释放,缓存数据不清除,重启nginx再访问时,数据依然是HIT

   网上流传着以下这个配置:
    proxy_cache_path  /data0/proxy_cache_dir  levels=1:2   keys_zone=cache_one:200m inactive=1d max_size=30g;
    内存200m,硬盘30g;我觉得这会造成很大的资源浪费,例如:后端数据3g,根据8:2原则600m常用数据,根据这个配置200m能放到内存,其余的放到disk,随着时间的推移,可以看到浪费的空间就比较多了,清理的时间也比较多;这个想法没经过实际数据。提议要合理分配内存和硬盘空间的数量。
    测试证明。
    网上还存在着一个说法:
    max_size是值最大可缓存的单个文件:经过测试这个值不是这个意思,这个值对proxy_cache_path目录做了大少限制,也是上面
    的说法硬盘空间大少。不过由此可以让我们联想到是否需要做单文件大少的限制,本人觉得有必要,避免造成内存碎片嘛,因为proxy_
    cache申请空间的操作经过了“封装”,不知道器底层是否为c的malloc。
6.其中包含两大数据结构btree、queue(我认为是按需队列,数据结构上有个专业名词
  一时想不起,有兴趣的朋友可以翻翻数据结构look一look)
  插入  查询等操作以btree 
  删除操作以queue管理 
  在update等一些操作中也需要在btree中操作(delete)
  我自己是这样认为:btree管理着disk    queue管理着内存
----------------------------------------------------------------------------------
我是利用这些状态值,来做后面的命中率分析脚本的依据
ngx_str_t  ngx_http_cache_status[] = {
    ngx_string("MISS"),
    ngx_string("EXPIRED"),
    ngx_string("STALE"),
    ngx_string("UPDATING"),
    ngx_string("HIT")
};

##################################################
我的命中率分析代码
#!/bin/bash
#edit by sky
if [[ $# < 1 ]] ; then
   while read INFILE
   do
     echo $INFILE
   done |   awk 'BEGIN{total=0;count=0}$((NF-1))~/HIT|MISS|EXPIRED/{if($((NF-1)) ~ "HIT") {count++}total++}END{print count/total}'
else
   awk 'BEGIN{total=0;count=0}$((NF-1))~/HIT|MISS|EXPIRED/{if($((NF-1)) ~ "HIT") {count++}total++}END{print count/total}' $1
fi
##################################################
----------------------------------------------------------------------------------

内存申请单位
    cache->sh = ngx_slab_alloc(cache->shpool, sizeof(ngx_http_file_cache_sh_t));
    if (cache->sh == NULL) {
        return NGX_ERROR;
    }

从代码中可以了解到:
该proxy_cache一并使用都btree和queue进行缓存(以ngx_http_file_cache_t结构体为单位)
ngx_rbtree_init(&cache->sh->rbtree, &cache->sh->sentinel,
                ngx_http_file_cache_rbtree_insert_value);
ngx_queue_init(&cache->sh->queue);


ngx_http_file_cache_exists(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)
{
   省略代码
    fcn = ngx_http_file_cache_lookup(cache, c->key);先查看是否有该缓存
   省略代码
        fcn->uses++;
        fcn->count++;    有的,进行相关变量自加
  省略代码
    if (fcn) {
        ngx_queue_remove(&fcn->queue);先从删除队列里面将该缓存删除
      省略代码
    }
    找不到就创建该缓存节点,并插入btree中
    if (fcn == NULL) {
        ngx_shmtx_unlock(&cache->shpool->mutex);
        (void) ngx_http_file_cache_forced_expire(cache);
        ngx_shmtx_lock(&cache->shpool->mutex);
        fcn = ngx_slab_alloc_locked(cache->shpool,
                                    sizeof(ngx_http_file_cache_node_t));
        if (fcn == NULL) {
            rc = NGX_ERROR;
            goto failed;
        }
    }
    ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t));
    ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)],
               NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));
    ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node);
 
  
renew:  对相关变量进行初始化
    rc = NGX_DECLINED;
    fcn->uses = 1;
    fcn->count = 1;
    fcn->valid_msec = 0;
    fcn->error = 0;
    fcn->exists = 0;
    fcn->valid_sec = 0;
    fcn->uniq = 0;
    fcn->body_start = 0;
    fcn->length = 0;
done:
    做最后的处理,延长过期的时间点,并把相应的节点加入删除队列里面
    fcn->expire = ngx_time() + cache->inactive;
    ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);
省略代码
}

---------------------------------------------------------------------------
插入方式不是我要了解的重点,在这里简单带过算了;也是btree的一些基本准则
static void
ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
{
   省略代码
    if (node->key < temp->key) {
            p = &temp->left;
        } else if (node->key > temp->key) {
            p = &temp->right;
        } else { /* node->key == temp->key */
            cn = (ngx_http_file_cache_node_t *) node;
            cnt = (ngx_http_file_cache_node_t *) temp;
            p = (ngx_memcmp(cn->key, cnt->key,
                            NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t))
                 < 0)
                    ? &temp->left : &temp->right;
        }
    省略代码
}

------------------------------------------------------

static time_t
ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache)
{
      省略代码
      遍历删除队列
          for (q = ngx_queue_last(&cache->sh->queue);
         q != ngx_queue_sentinel(&cache->sh->queue);
         q = ngx_queue_prev(q))
       {
    
            从下面看出遍历的深度是20
        if (fcn->count) {
            if (tries++ < 20) {
                continue;
            }
            wait = 1;
            break;
        }
       进行删除操作,
      if (!fcn->exists) {
            ngx_queue_remove(q);
            ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node);
            ngx_slab_free_locked(cache->shpool, fcn);
            break;
        }
        ngx_http_file_cache_delete(cache, q, name);
        break;
       省略代码
}

static time_t
ngx_http_file_cache_manager(void *data)
{
      省略代码
        next = ngx_http_file_cache_forced_expire(cache);
        
      省略代码
}

下面可以知道为啥要proxy_cache 的管理进程为啥要sleep
static ngx_int_t
ngx_http_file_cache_manager_sleep(ngx_http_file_cache_t *cache)
{
   省略代码
   
            /*
             * if processing 100 files takes more than 200ms,
             * it seems that many operations require disk i/o,
             * therefore sleep 200ms
             */
   省略代码
}

PS:
1.crc32 全称是循环冗余校验,由于把一段信息隐射成一段固定位数的验证码,常用于验证数据传输的完整性。
学网络的朋友很熟悉这种校验码了吧,很惭愧以前编程过程中没用过这种方式。

2.以上的删除队列,同样也是内存队列。

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