nginx實現了自己的hash數據結構,正如數據結構中講述的那樣,nginx用開放鏈表法解決沖突,不過不同的是一旦一個hash表被初始化后就不會被修改,即插入和刪除,只進行查詢操作,所以nginx通過計算初始化時key的個數來確定hash表中桶的個數和每個桶的容量,這樣能最大限度的利用內存資源。雖然用開放鏈表法,實際上每個桶都是一塊連續的內存空間。nginx實現了兩類hash結構,一類是key中包含通配符的ngx_hash_wildcard_t,另一類則是key中不包含通配符的ngx_hash_t,這里的通配符指*號。之所以會有這兩類hash,是因為nginx主要用hash來存放url與ip的對應關系,目的是為了通過url來查找ip,而url是可以用通配符表示的,如*.baidu.com。接下來先通過深入到nginx源碼中看看hash中定義的數據結構,然后看看兩類hash是怎樣實現的,最后通過一個例子來使用這兩類hash。
1 相關的數據結構
1.1 ngx_hash_t結構
這類hash的數據結構定義如下:
1 typedef struct { 2 void *value; //ip,也就是<key,value>中的key 3 u_short len; //url長度 4 u_char name[1]; //url,也就是<key,value>中的value 5 } ngx_hash_elt_t; 6 7 8 typedef struct { 9 ngx_hash_elt_t **buckets; //每個桶的起始地址 10 ngx_uint_t size; //桶的個數 11 } ngx_hash_t;
ngx_hash_t結構管理這個hash,ngx_hash_elt_t是hash桶中一個元素的表示。ngx_hash_elt_t字段中的value既可以指向實際的ip,也可以指向另一個hash表(這種情況只能出現在包含通配符的hash中),name字段既可以指向完整的url,也可以指向url的一部分(這種情況也只能出現在包含通配符的hash中)。
1.2 ngx_hash_wildcard_t
這個結構主要用於包含通配符的hash的,具體如下:
1 typedef struct { 2 ngx_hash_t hash; 3 void *value; 4 } ngx_hash_wildcard_t;
這個結構相比ngx_hash_t結構就是多了一個value指針,value這個字段是用來存放某個已經達到末尾的通配符url對應的value值,如果通配符url沒有達到末尾,這個字段為NULL。
1.3 ngx_hash_init_t
ngx_hash_init_t結構主要用於提供創建一個hash所需要的一些信息,定義如下:
1 typedef struct { 2 ngx_hash_t *hash; //指向待新建的hash表 3 ngx_hash_key_pt key; //hash函數指針 4 5 ngx_uint_t max_size; //hash表中桶的最大值,實際桶的個數存放在前面ngx_hash_t中的size字段中 6 ngx_uint_t bucket_size; //每個桶的最大尺寸 7 8 char *name; //hash表的名字,其實沒啥用(在log中用) 9 ngx_pool_t *pool; //構建hash所用的內存池 10 ngx_pool_t *temp_pool; //構建hash所用的臨時內存池 11 } ngx_hash_init_t;
hash字段如果為NULL的話,會動態創建管理hash的結構ngx_hash_t,這種情況通常在包含通配符的情況下使用;如果不為NULL,則使用這個字段指向的ngx_hash_t結構,這種情況通常在不包含通配符的情況下使用。
1.4 ngx_hash_key_t
ngx_hash_key_t結構主要用於初始化hash表的,正如前面說的,nginx只能一次性的初始化,初始化之后就不能插入和修改了,所以用一個ngx_table_elt_t結構的數組來存放要插入到hash中的所有<key,value>對,所有初始化hash的函數都需要根據這樣一個數組來初始化hash,這個在后面初始化hash函數的時候可以看到。
1 typedef struct { 2 ngx_str_t key; //<key,value>中的key 3 ngx_uint_t key_hash; //key通過hash函數算出的hash值 4 void *value; //<key,value>中的value 5 } ngx_hash_key_t;
1.5 ngx_hash_combined_t
1 typedef struct { 2 ngx_hash_t hash; 3 ngx_hash_wildcard_t *wc_head; 4 ngx_hash_wildcard_t *wc_tail; 5 } ngx_hash_combined_t;
這個結構包含了三類hash,hash字段表示不保護通配符的hash,wc_head字段表示包含前綴通配符的hash,wc_tail表示包含后綴通配符的hash。由於用戶配置的url通常會是是不包含通配符的url,包含前綴通配符的url和包含后綴通配符的rul中的一種或多種,所以一般使用這個結構來完全表示用戶配置的url。
1.6 ngx_hash_keys_arrays_t
1 typedef struct { 2 ngx_uint_t hsize; 3 ngx_pool_t *pool; 4 ngx_pool_t *temp_pool; 5 6 ngx_array_t keys; //存放不包含通配符的<key,value>鍵值對 7 ngx_array_t *keys_hash; //用來檢測沖突的 8 9 ngx_array_t dns_wc_head; //存放包含前綴通配符的<key,value>鍵值對 10 ngx_array_t *dns_wc_head_hash; //用來檢測沖突的 11 12 ngx_array_t dns_wc_tail; //存放包含后綴通配符的<key,value>鍵值對 13 ngx_array_t *dns_wc_tail_hash; //用來檢測沖突的 14 } ngx_hash_keys_arrays_t;
這個結構存放了初始化hash需要的所有鍵值對,keys數組用來初始化不包含通配符的hash,dns_wc_head數組用來初始化包含前綴通配符的hash,dns_wc_tail數組用來初始化包含后綴通配符的hash,並且dns_wc_head和dns_wc_tail數組包含的<key,value>鍵值對中的key都是已經去掉通配符的key,具體情況在后面ngx_hash_add_key函數的介紹中會詳細說明。
2 相關函數
2.1 ngx_hash_init
這個函數用來初始化不包含通配符的hash,函數原型如下:
1 ngx_int_t 2 ngx_hash_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, ngx_uint_t nelts)
hinit參數是初始化hash的相關信息,names存放了所有需要插入到hash中的<key,value>對,nelts是<key,value>對的個數。這個函數主要做了4部分的工作:
(1)檢查bucket_size是否合法,也就是它的值必須保證一個桶至少能存放一個<key,value>鍵值對,具體如下:
1 #define NGX_HASH_ELT_SIZE(name) \ 2 (sizeof(void *) + ngx_align((name)->key.len + 2, sizeof(void *))) 3 4 for (n = 0; n < nelts; n++) { 5 if (hinit->bucket_size < NGX_HASH_ELT_SIZE(&names[n]) + sizeof(void *)) 6 { 7 ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0, 8 "could not build the %s, you should " 9 "increase %s_bucket_size: %i", 10 hinit->name, hinit->name, hinit->bucket_size); 11 return NGX_ERROR; 12 } 13 }
上面的for循環保證hash的桶至少能裝一個<key,value>鍵值對,宏NGX_HASH_ELT_SIZE用來計算一個ngx_hash_key_t表示一個實際的<key,value>鍵值對占用內存的大小,之所以NGX_HASH_ELT_SIZE(&names[n]) 后面需要加上sizeof(void *),主要是每個桶都用一個值位NULL的void*指針來標記結束。
(2)計算hash中桶的個數
1 bucket_size = hinit->bucket_size - sizeof(void *); //除去桶標記后桶的大小 2 start = nelts / (bucket_size / (2 * sizeof(void *))); //桶的最小個數 3 start = start ? start : 1; 4 if (hinit->max_size > 10000 && nelts && hinit->max_size / nelts < 100) { 5 start = hinit->max_size - 1000; 6 } 7 //更新size來滿足bucket_size 8 for (size = start; size < hinit->max_size; size++) { 9 ngx_memzero(test, size * sizeof(u_short)); 10 for (n = 0; n < nelts; n++) { 11 if (names[n].key.data == NULL) { 12 continue; 13 } 14 key = names[n].key_hash % size; //計算當前<key,value>在哪個桶 15 test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n])); //計算當前<key,value>插入到桶之后桶的大小 16 17 if (test[key] > (u_short) bucket_size) { //檢查桶是否溢出了 18 goto next; 19 } 20 } 21 goto found; 22 next: 23 continue; 24
從最小桶的個數開始遞增,直到所有的<key,value>鍵值對都能存放在對應的桶中不溢出,那當前的桶個數就是需要的桶個數。
(3)計算新創建的hash所占用的空間,並調用內存分配函數分配這些空間
1 for (i = 0; i < size; i++) { 2 test[i] = sizeof(void *); 3 } 4 //計算每個桶的實際大小 5 for (n = 0; n < nelts; n++) { 6 if (names[n].key.data == NULL) { 7 continue; 8 } 9 10 key = names[n].key_hash % size; 11 test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n])); 12 } 13 //計算所有桶的大小 14 len = 0; 15 16 for (i = 0; i < size; i++) { 17 if (test[i] == sizeof(void *)) { 18 continue; 19 } 20 21 test[i] = (u_short) (ngx_align(test[i], ngx_cacheline_size)); //每個桶大小滿足cache行對齊 22 23 len += test[i]; 24 }
上面根據實際的<key,value>鍵值對來實際計算每個桶的大小,而不是所有桶的大小的設置成一樣的,這樣能很有效的節約內存空間,當然由於每個桶的大小是不固定的,所有每個桶的末尾需要一個額外空間(大小為sizeof(void*))來標記桶的結束。並且每個桶大小滿足cache行對齊,這樣能加快訪問速度,從這里也可以看出nginx無處不在優化程序的性能和資源的使用效率。
1 if (hinit->hash == NULL) { //hash為NULL,則動態生成管理hash的結構 2 //calloc會把獲取的內存初始化為0 3 hinit->hash = ngx_pcalloc(hinit->pool, sizeof(ngx_hash_wildcard_t) 4 + size * sizeof(ngx_hash_elt_t *)); 5 if (hinit->hash == NULL) { 6 ngx_free(test); 7 return NGX_ERROR; 8 } 9 10 buckets = (ngx_hash_elt_t **) 11 ((u_char *) hinit->hash + sizeof(ngx_hash_wildcard_t)); 12 13 } else { 14 buckets = ngx_pcalloc(hinit->pool, size * sizeof(ngx_hash_elt_t *)); 15 if (buckets == NULL) { 16 ngx_free(test); 17 return NGX_ERROR; 18 } 19 } 20 21 elts = ngx_palloc(hinit->pool, len + ngx_cacheline_size); //將所有桶占用的空間分配在連續的內存空間中 22 if (elts == NULL) { 23 ngx_free(test); 24 return NGX_ERROR; 25 } 26 27 elts = ngx_align_ptr(elts, ngx_cacheline_size); 28 29 for (i = 0; i < size; i++) { 30 if (test[i] == sizeof(void *)) { 31 continue; 32 } 33 34 buckets[i] = (ngx_hash_elt_t *) elts; //初始化每個桶的起始位置 35 elts += test[i]; 36 37 }
上面代碼先動態分配每個桶的起始指針,然后動態分配所有桶的空間,然后根據每個桶的大小,將每個桶的起始指針初始化指向對應桶的起始地址。
(4)將每個<key,value>鍵值對復制到所在的桶中
1 for (i = 0; i < size; i++) { 2 test[i] = 0; 3 } 4 5 for (n = 0; n < nelts; n++) { 6 if (names[n].key.data == NULL) { 7 continue; 8 } 9 10 key = names[n].key_hash % size; //計算當前<key,value>所在的桶 11 elt = (ngx_hash_elt_t *) ((u_char *) buckets[key] + test[key]); //計算當前<key,value>所在桶中的位置 12 //將當前<key,value>的值復制到桶中 13 elt->value = names[n].value; 14 elt->len = (u_short) names[n].key.len; 15 16 ngx_strlow(elt->name, names[n].key.data, names[n].key.len); 17 test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n])); //更新當前桶的大小 18 } 19 //為了標記每個桶的結束 20 for (i = 0; i < size; i++) { 21 if (buckets[i] == NULL) { 22 continue; 23 } 24 25 elt = (ngx_hash_elt_t *) ((u_char *) buckets[i] + test[i]); 26 27 elt->value = NULL; //前面每個test加上sizeof(void *)就是為了這個value指針 28 }
通過調用ngx_hash_init函數,一個hash就建立起來了,該hash大概的情況如下圖所示:
2.2 ngx_hash_add_key
1 ngx_int_t 2 ngx_hash_add_key(ngx_hash_keys_arrays_t *ha, ngx_str_t *key, void *value, 3 ngx_uint_t flags)
ha中包含了三類ngx_hash_key_t類型的數組,分別用來初始化三類hash(介紹ngx_hash_keys_arrays_t時已說明),那么新的<key,value>應該加入到哪個數組中就是ngx_hash_add_key這個函數的主要任務。參數中的flags用來標記是否key中可能包含通配符,一般這個參數設置為NGX_HASH_WILDCARD_KEY,即可能包含通配符。需要注意的是這個函數會改變包含通配符的key,將通配符去掉,如*.baidu.com會改變為com.baidu.,.baidu.com會改變為com.baidu,www.baidu.*會改變為www.baidu,www.baidu.這種通配是不允許出現的。
2.3 ngx_hash_find
1 void * 2 ngx_hash_find(ngx_hash_t *hash, ngx_uint_t key, u_char *name, size_t len)
這個函數通過給定的key和name在hash表中查找對應的<name,value>鍵值對,並將查找到的value值返回,參數中的key是name通過hash計算出來的。這個函數的實現很簡單,就是通過key找到要查找的鍵值對在哪個桶中,然后遍歷這個桶中的每個元素找key等於name的元素。
2.4 ngx_hash_wildcard_init
1 ngx_int_t 2 ngx_hash_wildcard_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, 3 ngx_uint_t nelts)
這個函數是nginx實現通配hash的關鍵所在,該函數通過對通配鍵值對建立多級hash來實現通配hash。實現的hash表大致如下圖所示:
上面的圖只顯示了二級hash,實際上可以由多級hash。下面我們舉個實際的例子來加深理解,假設有下面這些鍵值對:
<*.com, "220.181.111.147">,<*.baidu.com, "220.181.111.147">,<*.baidu.com.cn, "220.181.111.147">,<*.google.com,"58.63.236.35">
(1)通過函數ngx_hash_add_key將上面的鍵值對加入到ngx_hash_keys_arrays_t結構中的dns_wc_head數組中,該數組的值如下圖所示:
{key = ("com." , 4 ), key_hash = 0 , value = "220.181.111.147"}
{key = ("cn.com.baidu." , 13), key_hash = 0 , value = "220.181.111.147"}
{key = ("com.baidu." , 10), key_hash = 0 , value = "220.181.111.147"}
{key = ("com.google." , 11), key_hash = 0 , value = "58.63.236.35"}
(2)將上面的dns_wc_head數組傳遞給ngx_hash_wildcard_init,生成的hash如下圖所示:
現在來看下ngx_hash_wildcard_init是怎樣實現上面圖所示的多級hash結構的。
1 for (n = 0; n < nelts; n = i) { 2 3 dot = 0; 4 //以.作為字段的分隔符 5 for (len = 0; len < names[n].key.len; len++) { 6 if (names[n].key.data[len] == '.') { 7 dot = 1; 8 break; 9 } 10 } 11 12 name = ngx_array_push(&curr_names); 13 if (name == NULL) { 14 return NGX_ERROR; 15 } 16 //將上面獲取的字段作為當前hash的key 17 name->key.len = len; 18 name->key.data = names[n].key.data; 19 name->key_hash = hinit->key(name->key.data, name->key.len); 20 name->value = names[n].value; 21 22 dot_len = len + 1; 23 24 if (dot) { 25 len++; 26 } 27 //收集同一前綴的所有后綴 28 next_names.nelts = 0; 29 30 if (names[n].key.len != len) { 31 next_name = ngx_array_push(&next_names); 32 if (next_name == NULL) { 33 return NGX_ERROR; 34 } 35 36 next_name->key.len = names[n].key.len - len; 37 next_name->key.data = names[n].key.data + len; 38 next_name->key_hash = 0; 39 next_name->value = names[n].value; 40 } 41 42 for (i = n + 1; i < nelts; i++) { 43 if (ngx_strncmp(names[n].key.data, names[i].key.data, len) != 0) { 44 break; 45 } 46 47 if (!dot 48 && names[i].key.len > len 49 && names[i].key.data[len] != '.') 50 { 51 break; 52 } 53 54 next_name = ngx_array_push(&next_names); 55 if (next_name == NULL) { 56 return NGX_ERROR; 57 } 58 59 next_name->key.len = names[i].key.len - dot_len; 60 next_name->key.data = names[i].key.data + dot_len; 61 next_name->key_hash = 0; 62 next_name->value = names[i].value; 63 } 64 65 if (next_names.nelts) { //next_names中有元素 66 67 h = *hinit; 68 h.hash = NULL; 69 //遞歸建立當前字段的所有后綴字段組成的hash 70 if (ngx_hash_wildcard_init(&h, (ngx_hash_key_t *) next_names.elts, 71 next_names.nelts) 72 != NGX_OK) 73 { 74 return NGX_ERROR; 75 } 76 77 wdc = (ngx_hash_wildcard_t *) h.hash; 78 79 if (names[n].key.len == len) { //當前字段已經達到末尾 80 wdc->value = names[n].value; 81 } 82 //將后綴組成的下一級hash地址作為當前字段的value保存下來 83 name->value = (void *) ((uintptr_t) wdc | (dot ? 3 : 2)); //2只有在后綴通配符的情況下才會出現 84 85 } else if (dot) { //只有一個,而且不是后綴通配符 86 name->value = (void *) ((uintptr_t) name->value | 1); 87 } 88 } 89 //根據<當前字段,value>鍵值對建立hash 90 if (ngx_hash_init(hinit, (ngx_hash_key_t *) curr_names.elts, 91 curr_names.nelts) 92 != NGX_OK) 93 { 94 return NGX_ERROR; 95 } 96 97 return NGX_OK; 98 }
怎樣標記一個鍵值對<key,value>中的value是指向實際的value,還是指向下一級的hash地址,這是上面代碼實現的一個巧妙的地方。由於每個hash表的地址或者實際value的地址都是以4字節對齊的,所以這些地址的低2位都是0,這樣通過這兩位的標記可以很好地解決這個問題。
2.5 ngx_hash_find_wc_head
1 void * 2 ngx_hash_find_wc_head(ngx_hash_wildcard_t *hwc, u_char *name, size_t len)
這個函數根據name在前綴通配符hash中查找對應的value值。有了hash表后,查找是件相對更容易的事,從后往前的獲取name的每個字段(根據.分割),用每個字段查hash表,如果獲取的value值的標記存在下一級hash,則用同樣的方法查下一個字段對應的hash表,就這樣直到查到的value為真實的值為止。另一個通配符查找函數ngx_hash_find_wc_tail這是同樣的原理,不同的是對name從前往后處理每個字段而已。
3 測試
測試的程序主要是使用nginx實現的數據結構和函數,先通過下列url和ip建立hash,然后對給定的url查找ip。
(1)測試的數據如下:
1 static ngx_str_t urls[Max_Num] = { 2 ngx_string("*.com"), //220.181.111.147 3 ngx_string("*.baidu.com.cn"), 4 ngx_string("*.baidu.com"), 5 ngx_string(".baidu.com"), 6 ngx_string("*.google.com"), 7 ngx_string("www.sina.com.cn"), //58.63.236.35 8 ngx_string("www.google.com"), //74.125.71.105 9 ngx_string("www.qq.com"), //60.28.14.190 10 ngx_string("www.163.com"), //123.103.14.237 11 ngx_string("www.sohu.com"), //219.234.82.50 12 ngx_string("abo321.org"), //117.40.196.26 13 ngx_string(".abo321.org"), //117.40.196.26 14 ngx_string("www.abo321.*") //117.40.196.26 15 }; 16 17 static ngx_str_t values[Max_Num] = { 18 ngx_string("220.181.111.147"), 19 ngx_string("220.181.111.147"), 20 ngx_string("220.181.111.147"), 21 ngx_string("220.181.111.147"), 22 ngx_string("220.181.111.147"), 23 ngx_string("58.63.236.35"), 24 ngx_string("74.125.71.105"), 25 ngx_string("60.28.14.190"), 26 ngx_string("123.103.14.237"), 27 ngx_string("219.234.82.50"), 28 ngx_string("117.40.196.26"), 29 ngx_string("117.40.196.26"), 30 ngx_string("117.40.196.26") 31 };
(2)測試代碼為:https://github.com/cc1989/nginx_report/tree/master/nginx_test/hash
(3)測試結果

1 -------------------------------- 2 create a new pool: 3 -------------------------------- 4 pool = 0x810d020 5 .d 6 .last = 0x810d048 7 .end = 0x810d420 8 .next = (nil) 9 .failed = 0 10 .max = 984 11 .current = 0x810d020 12 .chain = (nil) 13 .large = (nil) 14 .cleanup = (nil) 15 .log = 0x8051518 16 available pool memory = 984 17 18 -------------------------------- 19 create and add urls to it: 20 -------------------------------- 21 array = 0xbfed53b4 22 .elts = 0xb7542008 23 .nelts = 6 24 .size = 16 25 .nalloc = 16384 26 .pool = 0x810d450 27 elements: 28 0xb7542008: {key = ("www.sina.com.cn", 15), key_hash = 1528635686 , value = "58.63.236.35" } 29 0xb7542018: {key = ("www.google.com" , 14), key_hash = -702889725 , value = "74.125.71.105" } 30 0xb7542028: {key = ("www.qq.com" , 10), key_hash = 203430122 , value = "60.28.14.190" } 31 0xb7542038: {key = ("www.163.com" , 11), key_hash = -640386838 , value = "123.103.14.237" } 32 0xb7542048: {key = ("www.sohu.com" , 12), key_hash = 1313636595 , value = "219.234.82.50" } 33 0xb7542058: {key = ("abo321.org" , 10), key_hash = 1050805370 , value = "117.40.196.26" } 34 35 array = 0xbfed53cc 36 .elts = 0xb7501008 37 .nelts = 4 38 .size = 16 39 .nalloc = 16384 40 .pool = 0x810d450 41 elements: 42 0xb7501008: {key = ("com." , 4 ), key_hash = 0 , value = "220.181.111.147"} 43 0xb7501018: {key = ("cn.com.baidu." , 13), key_hash = 0 , value = "220.181.111.147"} 44 0xb7501028: {key = ("com.baidu." , 10), key_hash = 0 , value = "220.181.111.147"} 45 0xb7501038: {key = ("com.google." , 11), key_hash = 0 , value = "220.181.111.147"} 46 47 array = 0xbfed53e4 48 .elts = 0xb74c0008 49 .nelts = 1 50 .size = 16 51 .nalloc = 16384 52 .pool = 0x810d450 53 elements: 54 0xb74c0008: {key = ("www.abo321" , 10), key_hash = 0 , value = "117.40.196.26" } 55 56 -------------------------------- 57 the pool: 58 -------------------------------- 59 pool = 0x810d020 60 .d 61 .last = 0x810d2a9 62 .end = 0x810d420 63 .next = (nil) 64 .failed = 0 65 .max = 984 66 .current = 0x810d020 67 .chain = (nil) 68 .large = (nil) 69 .cleanup = (nil) 70 .log = 0x8051518 71 available pool memory = 375 72 73 array = 0xbfed53cc 74 .elts = 0xb7501008 75 .nelts = 4 76 .size = 16 77 .nalloc = 16384 78 .pool = 0x810d450 79 elements: 80 0xb7501008: {key = ("cn.com.baidu." , 13), key_hash = 0 , value = "220.181.111.147"} 81 0xb7501018: {key = ("com." , 4 ), key_hash = 0 , value = "220.181.111.147"} 82 0xb7501028: {key = ("com.baidu." , 10), key_hash = 0 , value = "220.181.111.147"} 83 0xb7501038: {key = ("com.google." , 11), key_hash = 0 , value = "220.181.111.147"} 84 85 array = 0xbfed53e4 86 .elts = 0xb74c0008 87 .nelts = 1 88 .size = 16 89 .nalloc = 16384 90 .pool = 0x810d450 91 elements: 92 0xb74c0008: {key = ("www.abo321" , 10), key_hash = 0 , value = "117.40.196.26" } 93 94 -------------------------------- 95 the hash: 96 -------------------------------- 97 hash = 0xbfed53fc: **buckets = 0x810d2ac, size = 3 98 0x810d2ac: buckets[0] = 0x810d2c0 99 0x810d2b0: buckets[1] = 0x810d300 100 0x810d2b4: buckets[2] = 0x810d320 101 102 key 1528635686: buckets 2: 0x810d320: {value = "58.63.236.35" , len = 15, name = "www.sina.com.cn"} 103 key -702889725: buckets 1: 0x810d300: {value = "74.125.71.105" , len = 14, name = "www.google.com" } 104 key 203430122 : buckets 2: 0x810d338: {value = "60.28.14.190" , len = 10, name = "www.qq.com" } 105 key -640386838: buckets 0: 0x810d2c0: {value = "123.103.14.237" , len = 11, name = "www.163.com" } 106 key 1313636595: buckets 0: 0x810d2d4: {value = "219.234.82.50" , len = 12, name = "www.sohu.com" } 107 key 1050805370: buckets 2: 0x810d348: {value = "117.40.196.26" , len = 10, name = "abo321.org" } 108 value = NULL 109 hash = 0x8111cd0: **buckets = 0x8111cdc, size = 1 110 0x8111cdc: buckets[0] = 0x8111ce0 111 buckets 0: 0x8111ce0: {value = "0x810d3c8 ", len = 2, name = "cn" } 112 value = NULL 113 hash = 0x810d3c8: **buckets = 0x810d3d4, size = 1 114 0x810d3d4: buckets[0] = 0x810d3e0 115 buckets 0: 0x810d3e0: {value = "0x810d378 ", len = 3, name = "com" } 116 value = NULL 117 hash = 0x810d378: **buckets = 0x810d384, size = 1 118 0x810d384: buckets[0] = 0x810d3a0 119 buckets 0: 0x810d3a0 {value = "220.181.111.147", len = 5, name = "baidu" } 120 buckets 0: 0x8111ce8: {value = "0x8111c80 ", len = 3, name = "com" } 121 value = "220.181.111.147" 122 hash = 0x8111c80: **buckets = 0x8111c8c, size = 1 123 0x8111c8c: buckets[0] = 0x8111ca0 124 buckets 0: 0x8111ca0 {value = "220.181.111.147", len = 5, name = "baidu" } 125 buckets 0: 0x8111cac {value = "220.181.111.147", len = 6, name = "google" } 126 value = NULL 127 hash = 0x8111d70: **buckets = 0x8111d7c, size = 1 128 0x8111d7c: buckets[0] = 0x8111d80 129 buckets 0: 0x8111d80: {value = "0x8111d20 ", len = 3, name = "www" } 130 value = NULL 131 hash = 0x8111d20: **buckets = 0x8111d2c, size = 1 132 0x8111d2c: buckets[0] = 0x8111d40 133 buckets 0: 0x8111d40 {value = "117.40.196.26" , len = 6, name = "abo321" } 134 135 -------------------------------- 136 the pool: 137 -------------------------------- 138 pool = 0x810d020 139 .d 140 .last = 0x810d418 141 .end = 0x810d420 142 .next = 0x8111c70 143 .failed = 0 144 .max = 984 145 .current = 0x810d020 146 .chain = (nil) 147 .large = (nil) 148 .cleanup = (nil) 149 .log = 0x8051518 150 available pool memory = 8 151 152 pool = 0x8111c70 153 .d 154 .last = 0x8111dc0 155 .end = 0x8112070 156 .next = (nil) 157 .failed = 0 158 .max = 135339148 159 .current = 0x1 160 .chain = 0x810d0d8 161 .large = 0x8111ca0 162 .cleanup = (nil) 163 .log = (nil) 164 available pool memory = 688 165 166 -------------------------------- 167 find test: 168 -------------------------------- 169 (url = "*.com" , key = 40256957 ) found, (ip = "220.181.111.147") 170 (url = "*.baidu.com.cn" , key = 234385007 ) found, (ip = "220.181.111.147") 171 (url = "*.baidu.com" , key = -724157846 ) found, (ip = "220.181.111.147") 172 (url = ".baidu.com" , key = 1733355200 ) found, (ip = "220.181.111.147") 173 (url = "*.google.com" , key = -1465170800) found, (ip = "220.181.111.147") 174 (url = "www.sina.com.cn", key = 1528635686 ) found, (ip = "58.63.236.35 ") 175 (url = "www.google.com" , key = -702889725 ) found, (ip = "74.125.71.105 ") 176 (url = "www.qq.com" , key = 203430122 ) found, (ip = "60.28.14.190 ") 177 (url = "www.163.com" , key = -640386838 ) found, (ip = "123.103.14.237 ") 178 (url = "www.sohu.com" , key = 1313636595 ) found, (ip = "219.234.82.50 ") 179 (url = "abo321.org" , key = 1050805370 ) found, (ip = "117.40.196.26 ") 180 (url = ".abo321.org" , key = -4578520 ) not found! 181 (url = "www.abo321.*" , key = 1494696375 ) found, (ip = "117.40.196.26 ") 182 183 (url = "*.xx.xx" , key = 51835370 ) not found! 184 (url = "www.baidu.com" , key = 270263191 ) found, (ip = "220.181.111.147") 185 (url = "www.baidu." , key = -239024726 ) not found!