整个ngx_http_block中的ngx_conf_parse配置解析完成,,后面主要对配置文件进行优化。
优化可以分为以下:
1 配置作用域合并
2location划分
3 http header回调初始化hash
4 初始化http收包回调函数
5 server中的server_name形成hash
http header回调初始化hash
ngx_int_t
ngx_hash_init(ngx_hash_init_t *hinit,ngx_hash_key_t *names, ngx_uint_t nelts)
{
u_char *elts;
size_t len;
u_short *test;
ngx_uint_t i, n, key, size,start, bucket_size;
ngx_hash_elt_t *elt, **buckets;
for(n = 0; n < nelts; n++) {
//判断数组中的节点是否超过bucket_size设置的大小
if (hinit->bucket_size < NGX_HASH_ELT_SIZE(&names[n]) +sizeof(void *))
{
ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0,
"could notbuild the %s, you should "
"increase%s_bucket_size: %i",
hinit->name,hinit->name, hinit->bucket_size);
return NGX_ERROR;
}
}
test = ngx_alloc(hinit->max_size * sizeof(u_short),hinit->pool->log);
if(test == NULL) {
return NGX_ERROR;
}
//bucket_size是一个桶的内存大小,把它减去哨兵void*就是真实hash所占用大小
bucket_size = hinit->bucket_size - sizeof(void *);
/*
typedef struct {
void *value;
u_short len;
u_char name[1];
} ngx_hash_elt_t;
* 因为bucket中存放的是一个数组,每个数组元素的结构是上面ngx_hash_elt_t,最小也就是name为0,最小占用空间为2*sizeof(void*)
*bucket_size/(2*sizeof(void*))是表示每一个bucket 能存放的最大个数
*nelts/bucket_size/(2*sizeof(void*))表示最少需要多少个bucket
*/
start = nelts / (bucket_size / (2 * sizeof(void *)));
start = start ? start : 1;
if(hinit->max_size > 10000 && nelts && hinit->max_size /nelts < 100) {
start = hinit->max_size - 1000;
}
for(size = start; size < hinit->max_size; size++) {
//首先清空为0
ngx_memzero(test, size * sizeof(u_short));
for (n = 0; n < nelts; n++) {
if (names[n].key.data == NULL) {
continue;
}
//第一次映射到test[key]时候为0,后面就加上映射到这块内存大小
//test[key]就是记录使用同一个key占用的空间数
key = names[n].key_hash % size;
test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));
#if 0
ngx_log_error(NGX_LOG_ALERT,hinit->pool->log, 0,
"%ui: %ui %ui\"%V\"",
size, key,test[key], &names[n].key);
#endif
if (test[key] > (u_short) bucket_size) {
goto next;
}
}
goto found;
next:
continue;
}
ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0,
"could not build the%s, you should increase "
"either %s_max_size: %ior %s_bucket_size: %i",
hinit->name,hinit->name, hinit->max_size,
hinit->name,hinit->bucket_size);
ngx_free(test);
return NGX_ERROR;
found:
for(i = 0; i < size; i++) {
test[i] = sizeof(void *);
}
for(n = 0; n < nelts; n++) {
if (names[n].key.data == NULL) {
continue;
}
key = names[n].key_hash % size;
test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));
}
len= 0;
for(i = 0; i < size; i++) {
//如果说当前的i没有映射值,那么就不给这块提供内存
if (test[i] == sizeof(void *)) {
continue;
}
test[i] = (u_short) (ngx_align(test[i], ngx_cacheline_size));
//记录需要申请的总长度
len += test[i];
}
if(hinit->hash == NULL) {
hinit->hash = ngx_pcalloc(hinit->pool, sizeof(ngx_hash_wildcard_t)
+size * sizeof(ngx_hash_elt_t *));
if (hinit->hash == NULL) {
ngx_free(test);
return NGX_ERROR;
}
buckets = (ngx_hash_elt_t **)
((u_char *)hinit->hash + sizeof(ngx_hash_wildcard_t));
}else {
//分配buckets
buckets = ngx_pcalloc(hinit->pool, size * sizeof(ngx_hash_elt_t *));
if (buckets == NULL) {
ngx_free(test);
return NGX_ERROR;
}
}
//分配元素存储空间
elts = ngx_palloc(hinit->pool, len + ngx_cacheline_size);
if(elts == NULL) {
ngx_free(test);
return NGX_ERROR;
}
elts = ngx_align_ptr(elts, ngx_cacheline_size);
for(i = 0; i < size; i++) {
if (test[i] == sizeof(void *)) {
continue;
}
buckets[i] = (ngx_hash_elt_t *) elts;
//因为可能有内存对齐操作,所以需要加上该bucket的偏移
elts += test[i];
}
for(i = 0; i < size; i++) {
test[i] = 0;
}
//下面就是赋值操作
for(n = 0; n < nelts; n++) {
if (names[n].key.data == NULL) {
continue;
}
key = names[n].key_hash % size;
elt = (ngx_hash_elt_t *) ((u_char *) buckets[key] + test[key]);
elt->value = names[n].value;
elt->len = (u_short) names[n].key.len;
ngx_strlow(elt->name, names[n].key.data, names[n].key.len);
test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));
}
for(i = 0; i < size; i++) {
if (buckets[i] == NULL) {
continue;
}
elt = (ngx_hash_elt_t *) ((u_char *) buckets[i] + test[i]);
elt->value = NULL;
}
ngx_free(test);
hinit->hash->buckets = buckets;
hinit->hash->size = size;
return NGX_OK;
}
ngx_hash_init函数主要是根据传入的数组ngx_hash_key_t*names和nelts来动态的设置bucket个数,算法是首先获取每一个节点的最小值,然后bucket_size/(2*sizeof(void*))计算出每一个bucket最大存储节点个数,然后nelts/bucket_size/(2*sizeof(void*))s算出来的值,就是最小bucket数,然后从这个为起始值,动态创建bucket个数,一直到hinit->max_size, 满足bucket的情况是传入数组映射到相应的bucket,此bucket大小并不超过bucket_size,此时的bucket就满足要求。然后该函数在计算传入数组每一个元素映射到相应的内存地址保存在test中。然后申请bucket和bucket相应的起始地址,然后对传入数组元素设置相应的hash值
ngx_http_init_headers_in_hash函数就是遍历ngx_http_headers_in数组,生成headers_in数组,代码如下:
for (header = ngx_http_headers_in; header->name.len; header++) {
hk = ngx_array_push(&headers_in);
if (hk == NULL) {
return NGX_ERROR;
}
hk->key = header->name;
hk->key_hash = ngx_hash_key_lc(header->name.data, header->name.len);
hk->value = header;
}
然后调用ngx_hash_init形成hash数组。
整个结构如下:
ngx_http_block对header信息生成hash后,就调用NGX_HTTP_MODULE的回调postconfiguration函数进行模块函数挂在,此回调函数主要完成模块的函数挂载。
4 http收包回调函数进行初始化
函数主要把cmcf->phases二维数组,第一维为11个阶段,第二维为模块注册的回调函数,修改为一维数组,采用next指示,同一个节点的回调可能就执行一个然后就跳到下一个阶段具体函数为ngx_http_init_phase_handlers 函数很简单就不分析了