在nginx源码中提供了一个比较重要的hash结构,可以为我们带来高效的kv查找。该hash的实现比较简单,但却非常的高效。该hash结构是只读的,在创建之后,以后只能提供查询功能。
该hash结构体,刚开始理解起来比较费劲,而且在使用时也会有不爽的感觉,需要好几个结构体,以及好几个函数配合才能完成初始化及查找。在本文中,对于通配符的使用,我们先不作介绍。
我们先看看如何使用吧。
创建一个hash结构体的过程是:
1. 构造一个ngx_hash_key_t为成员的数组,然后用我们需要hash的key、value以及计算出来的hash值来初始化该数组的每一个成员。
2. 构建一个 ngx_hash_init_t结构体的变量, 其中包含了ngx_hash_t 的成员, 为hash的结构体, 还包括一些其他初始设置,如bucket的大小,内存池等。该hash结构体会在ngx_hash_init中创建及初始化。
3. 调用 ngx_hash_init 传入 ngx_hash_init_t 结构, ngx_hash_key_t 的数组,和数组的长度, 进行初始化,这样 ngx_hash_init_t的hash成员就是我们要的hash结构体。
再看看查找:
1. 计算出key的hash值。
2. 使用 ngx_hash_find 进行查找,需要同时传入 hash值和key ,返回的就是value的指针。
看起来似乎还比较简单,示例代码来段:
- #include
- #include "ngx_config.h"
- #include "ngx_conf_file.h"
- #include "nginx.h"
- #include "ngx_core.h"
- #include "ngx_string.h"
- #include "ngx_palloc.h"
- #include "ngx_array.h"
- #include "ngx_hash.h"
- volatile ngx_cycle_t *ngx_cycle;
- void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, const char *fmt, ...) { }
- static ngx_str_t names[] = {ngx_string("rainx"),
- ngx_string("xiaozhe"),
- ngx_string("zhoujian")};
- static char* descs[] = {"rainx's id is 1","xiaozhe's id is 2","zhoujian's id is 3"};
-
- int main()
- {
- ngx_uint_t k;
- ngx_pool_t* pool;
- ngx_hash_init_t hash_init;
- ngx_hash_t* hash;
- ngx_array_t* elements;
- ngx_hash_key_t* arr_node;
- char* find;
- int i;
- ngx_cacheline_size = 32;
-
- ngx_str_t str = ngx_string("hello, world");
- k = ngx_hash_key_lc( str.data, str.len);
- pool = ngx_create_pool(1024*10, NULL);
- printf("caculated key is %u /n", k);
-
-
- hash = (ngx_hash_t*) ngx_pcalloc(pool, sizeof(hash));
- hash_init.hash = hash;
- hash_init.key = &ngx_hash_key_lc;
- hash_init.max_size = 1024*10;
- hash_init.bucket_size = 64;
- hash_init.name = "yahoo_guy_hash";
- hash_init.pool = pool;
- hash_init.temp_pool = NULL;
-
- elements = ngx_array_create(pool, 32, sizeof(ngx_hash_key_t));
- for(i = 0; i < 3; i++) {
- arr_node = (ngx_hash_key_t*) ngx_array_push(elements);
- arr_node->key = (names[i]);
- arr_node->key_hash = ngx_hash_key_lc(arr_node->key.data, arr_node->key.len);
- arr_node->value = (void*) descs[i];
-
- printf("key: %s , key_hash: %u/n", arr_node->key.data, arr_node->key_hash);
- }
- if (ngx_hash_init(&hash_init, (ngx_hash_key_t*) elements->elts, elements->nelts)!=NGX_OK){
- return 1;
- }
-
- k = ngx_hash_key_lc(names[0].data, names[0].len);
- printf("%s key is %d/n", names[0].data, k);
- find = (char*)
- ngx_hash_find(hash, k, (u_char*) names[0].data, names[0].len);
- if (find) {
- printf("get desc of rainx: %s/n", (char*) find);
- }
- ngx_array_destroy(elements);
- ngx_destroy_pool(pool);
- return 0;
- }
#include
#include "ngx_config.h"
#include "ngx_conf_file.h"
#include "nginx.h"
#include "ngx_core.h"
#include "ngx_string.h"
#include "ngx_palloc.h"
#include "ngx_array.h"
#include "ngx_hash.h"
volatile ngx_cycle_t *ngx_cycle;
void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, const char *fmt, ...) { }
static ngx_str_t names[] =
{ngx_string("rainx"), ngx_string("xiaozhe"), ngx_string("zhoujian")};
static char* descs[] = {"rainx's id is 1","xiaozhe's id is 2","zhoujian's id is 3"}; // hash table的一些基本操作
int main()
{
ngx_uint_t k; //, p, h;
ngx_pool_t* pool;
ngx_hash_init_t hash_init;
ngx_hash_t* hash;
ngx_array_t* elements;
ngx_hash_key_t* arr_node;
char* find;
int i;
ngx_cacheline_size = 32; // hash key cal start
ngx_str_t str = ngx_string("hello, world");
k = ngx_hash_key_lc( str.data, str.len);
pool = ngx_create_pool(1024*10, NULL);
printf("caculated key is %u /n", k); // hask key cal end //
hash = (ngx_hash_t*) ngx_pcalloc(pool, sizeof(hash));
hash_init.hash = hash; // hash结构
hash_init.key = &ngx_hash_key_lc; // hash算法函数
hash_init.max_size = 1024*10; //
max_size hash_init.bucket_size = 64; //
ngx_align(64, ngx_cacheline_size);
hash_init.name = "yahoo_guy_hash"; // 在log里会用到
hash_init.pool = pool; // 内存池
hash_init.temp_pool = NULL;
// 创建数组 elements = ngx_array_create(pool, 32, sizeof(ngx_hash_key_t));
for(i = 0; i < 3; i++)
{
arr_node = (ngx_hash_key_t*) ngx_array_push(elements);
arr_node->key = (names[i]);
arr_node->key_hash = ngx_hash_key_lc(arr_node->key.data, arr_node->key.len);
arr_node->value = (void*) descs[i]; //
printf("key: %s , key_hash: %u/n", arr_node->key.data, arr_node->key_hash);
}
if (ngx_hash_init(&hash_init, (ngx_hash_key_t*) elements->elts, elements->nelts)!=NGX_OK)
{
return 1;
}
// 查找
k = ngx_hash_key_lc(names[0].data, names[0].len);
printf("%s key is %d/n", names[0].data, k);
find = (char*) ngx_hash_find(hash, k, (u_char*) names[0].data, names[0].len);
if (find)
{
printf("get desc of rainx: %s/n", (char*) find); }
ngx_array_destroy(elements);
ngx_destroy_pool(pool);
return 0;
}
接下来我们来分析下源码,先介绍几个结构体:
- typedef struct {
- void *value;
- u_char len;
- u_char name[1];
- } ngx_hash_elt_t;
-
-
- typedef struct {
- ngx_hash_elt_t **buckets;
- ngx_uint_t size;
- } ngx_hash_t;
-
-
- typedef struct {
- ngx_hash_t hash;
- void *value;
- } ngx_hash_wildcard_t;
-
-
-
- typedef struct {
- ngx_str_t key;
- ngx_uint_t key_hash;
- void *value;
- } ngx_hash_key_t;
-
-
-
- typedef ngx_uint_t (*ngx_hash_key_pt) (u_char *data, size_t len);
- typedef struct {
- ngx_hash_t *hash;
- ngx_hash_key_pt key;
- ngx_uint_t max_size;
- ngx_uint_t bucket_size;
- char *name;
- ngx_pool_t *pool;
- ngx_pool_t *temp_pool;
- } ngx_hash_init_t;
在熟悉了结构体之后,我先给大家看看hash在内存中的存放布局,看图:
接下来,我们来看看复杂的ngx_hash_init函数:
-
-
-
- #define NGX_HASH_ELT_SIZE(name) /
- (sizeof(void *) + ngx_align((name)->key.len + 1, sizeof(void *)))
-
- 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++) {
-
- if (names[n].key.len >= 255) {
- ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0,
- "the /"%V/" value to hash is to long: %uz bytes, "
- "the maximum length can be 255 bytes only",
- &names[n].key, names[n].key.len);
- return NGX_ERROR;
- }
-
- if (hinit->bucket_size < NGX_HASH_ELT_SIZE(&names[n]) + sizeof(void *))
- {
- ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0,
- "could not build 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 = hinit->bucket_size - sizeof(void *);
-
- start = nelts / (bucket_size / (2 * sizeof(void *)));
- start = start ? start : 1;
- if (hinit->max_size > 10000 && hinit->max_size / nelts < 100) {
- start = hinit->max_size - 1000;
- }
-
- for (size = start; size < hinit->max_size; size++) {
- ngx_memzero(test, size * sizeof(u_short));
-
- 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]));
- #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: %i or %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++) {
- 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 = 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;
- 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_char) 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_find提供hash查找:
-
-
- void *
- ngx_hash_find(ngx_hash_t *hash, ngx_uint_t key, u_char *name, size_t len)
- {
- ngx_uint_t i;
- ngx_hash_elt_t *elt;
- #if 0
- ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "hf:/"%*s/"", len, name);
- #endif
-
-
- elt = hash->buckets[key % hash->size];
-
- if (elt == NULL) {
- return NULL;
- }
-
- while (elt->value) {
-
- if (len != (size_t) elt->len) {
- goto next;
- }
-
- for (i = 0; i < len; i++) {
- if (name[i] != elt->name[i]) {
- goto next;
- }
- }
- return elt->value;
- next:
- elt = (ngx_hash_elt_t *) ngx_align_ptr(&elt->name[0] + elt->len,
- sizeof(void *));
- continue;
- }
- return NULL;
- }
最后感谢所提供的帮助及图文!