全部博文(167)
分类: 系统运维
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.以上的删除队列,同样也是内存队列。