7.7.3 带通配符散列表的使用例子
散列表元素ngx_hash_elt_t中value指针指向的数据结构为下面定义的TestWildcardHash-Node结构体,代码如下。
typedef struct{
//用于散列表中的关键字
ngx_str_t servername;
//这个成员仅是为了方便区别而已
ngx_int_t seq;
}TestWildcardHashNode;
每个散列表元素的关键字是servername字符串。下面先定义ngx_hash_init_t和ngx_hash_keys_arrays_t变量,为初始化散列表做准备,代码如下。
//定义用于初始化散列表的结构体
ngx_hash_init_t hash;
/ngx_hash_keys_arrays_t用于预先向散列表中添加元素,这里的元素支持带通配符/
ngx_hash_keys_arrays_t ha;
//支持通配符的散列表
ngx_hash_combined_t combinedHash;
ngx_memzero(&ha,sizeof(ngx_hash_keys_arrays_t));
combinedHash是我们定义的用于指向散列表的变量,它包括指向3个散列表的指针,下面会依次给这3个散列表指针赋值。
//临时内存池只是用于初始化通配符散列表,在初始化完成后就可以销毁掉
ha.temp_pool=ngx_create_pool(16384,cf->log);
if(ha.temp_pool==NULL){
return NGX_ERROR;
}
/*由于这个例子是在ngx_http_mytest_postconf函数中的,所以就用了ngx_conf_t类型的cf下的内存池作为散列表的内存池
ha.pool=cf->pool;
调用ngx_hash_keys_array_init方法来初始化ha,为下一步向ha中加入散列表元素做好准备,代码如下。
if(ngx_hash_keys_array_init(&ha,NGX_HASH_LARGE)!=NGX_OK){
return NGX_ERROR;
}
本节按照图7-12和图7-15中的例子建立3个数据,并且会覆盖7.7节中介绍的散列表内容。我们建立的testHashNode[3]这3个TestWildcardHashNode类型的结构体,分别表示可以用前置通配符匹配的散列表元素、可以用后置通配符匹配的散列表元素、需要完全匹配的散列表元素。
TestWildcardHashNode testHashNode[3];
testHashNode[0].servername.len=ngx_strlen("*.test.com");
testHashNode[0].servername.data=ngx_pcalloc(cf->pool,ngx_strlen("*.test.com"));
ngx_memcpy(testHashNode[0].servername.data,".test.com",ngx_strlen(".test.com"));
testHashNode[1].servername.len=ngx_strlen("www.test.*");
testHashNode[1].servername.data=ngx_pcalloc(cf->pool,ngx_strlen("www.test.*"));
ngx_memcpy(testHashNode[1].servername.data,"www.test.",ngx_strlen("www.test."));
testHashNode[2].servername.len=ngx_strlen("www.test.com");
testHashNode[2].servername.data=ngx_pcalloc(cf->pool,ngx_strlen("www.test.com"));
ngx_memcpy(testHashNode[2].servername.data,"www.test.com",ngx_strlen("www.test.com"));
下面通过调用ngx_hash_add_key方法将testHashNode[3]这3个成员添加到ha中。
for(i=0;i<3;i++)
{
testHashNode[i].seq=i;
ngx_hash_add_key(&ha,&testHashNode[i].servername,
&testHashNode[i],NGX_HASH_WILDCARD_KEY);
}
注意,在上面添加散列表元素时,flag设置为NGX_HASH_WILDCARD_KEY,这样才会处理带通配符的关键字。
在调用ngx_hash_init_t的初始化函数前,先得设置好ngx_hash_init_t中的成员,如槽的大小、散列方法等,如下所示。
hash.key=ngx_hash_key_lc;
hash.max_size=100;
hash.bucket_size=48;
hash.name="test_server_name_hash";
hash.pool=cf->pool;
ha的keys动态数组中存放的是需要完全匹配的关键字,如果keys数组不为空,那么开始初始化第1个散列表,代码如下。
if(ha.keys.nelts){
/需要显式地把ngx_hash_init_t中的hash指针指向combinedHash中的完全匹配散列表/
hash.hash=&combinedHash.hash;
//初始化完全匹配散列表时不会使用到临时内存池
hash.temp_pool=NULL;
/将keys动态数组直接传给ngx_hash_init方法即可,ngx_hash_init_t中的hash指针就是初始化成功的散列表/
if(ngx_hash_init(&hash,ha.keys.elts,ha.keys.nelts)!=NGX_OK)
{
return NGX_ERROR;
}
}
下面继续初始化前置通配符散列表,代码如下。
if(ha.dns_wc_head.nelts){
hash.hash=NULL;
//注意,ngx_hash_wildcard_init方法需要使用临时内存池
hash.temp_pool=ha.temp_pool;
if(ngx_hash_wildcard_init(&hash,ha.dns_wc_head.elts,
ha.dns_wc_head.nelts)!=NGX_OK)
{
return NGX_ERROR;
}
/ngx_hash_init_t中的hash指针是ngx_hash_wildcard_init初始化成功的散列表,需要将它赋到combinedHash.wc_head前置通配符散列表指针中/
combinedHash.wc_head=(ngx_hash_wildcard_t*)hash.hash;
}
下面继续初始化后置通配符散列表,代码如下。
if(ha.dns_wc_tail.nelts){
hash.hash=NULL;
//注意,ngx_hash_wildcard_init方法需要使用临时内存池
hash.temp_pool=ha.temp_pool;
if(ngx_hash_wildcard_init(&hash,ha.dns_wc_tail.elts,
ha.dns_wc_tail.nelts)!=NGX_OK)
{
return NGX_ERROR;
}
/ngx_hash_init_t中的hash指针是ngx_hash_wildcard_init初始化成功的散列表,需要将它赋到combinedHash.wc_tail后置通配符散列表指针中/
combinedHash.wc_tail=(ngx_hash_wildcard_t*)hash.hash;
}
到此,临时内存池已经没有存在的意义了,也就是说,ngx_hash_keys_arrays_t中的这些数组、简易散列表都可以销毁了。这时,只需要简单地把temp_pool内存池销毁就可以了,代码如下。
ngx_destroy_pool(ha.temp_pool);
下面检查一下散列表是否工作正常。首先,查询关键字www.test.org,实际上,它应该匹配后置通配符散列表中的元素www.test.*,代码如下。
//首先定义待查询的关键字字符串findServer
ngx_str_t findServer;
findServer.len=ngx_strlen("www.test.org");
/为什么必须要在内存池中分配空间以保存关键字呢?因为我们使用的散列方法是ngx_hash_key_lc,它会试着把关键字全小写/
findServer.data=ngx_pcalloc(cf->pool,ngx_strlen("www.test.org"));
ngx_memcpy(findServer.data,"www.test.org",ngx_strlen("www.test.org"));
/ngx_hash_find_combined方法会查找出www.test.对应的散列表元素,返回其指向的用户数据ngx_hash_find_combined,也就是testHashNode[1]*/
TestWildcardHashNode*findHashNode=
ngx_hash_find_combined(&combinedHash,
ngx_hash_key_lc(findServer.data,findServer.len),
findServer.data,findServer.len);
如果没有查询到的话,那么findHashNode值为NULL空指针。
下面试着查询www.test.com,实际上,testHashNode[0]、testHashNode[1]、testHashNode[2]这3个节点都是匹配的,因为.test.com、www.test.、www.test.com明显都是匹配的。但按照完全匹配最优先的规则,ngx_hash_find_combined方法会返回testHashNode[2]的地址,也就是www.test.com对应的元素。
findServer.len=ngx_strlen("www.test.com");
findServer.data=ngx_pcalloc(cf->pool,ngx_strlen("www.test.com"));
ngx_memcpy(findServer.data,"www.test.com",ngx_strlen("www.test.com"));
findHashNode=ngx_hash_find_combined(&combinedHash,
ngx_hash_key_lc(findServer.data,findServer.len),
findServer.data,findServer.len);
下面测试一下后置通配符散列表。如果查询的关键字是"smtp.test.com",那么查询到的应该是关键字为*.test.com的元素testHashNode[0]。
findServer.len=ngx_strlen("smtp.test.com");
findServer.data=ngx_pcalloc(cf->pool,ngx_strlen("smtp.test.com"));
ngx_memcpy(findServer.data,"smtp.test.com",ngx_strlen("smtp.test.com"));
findHashNode=ngx_hash_find_combined(&combinedHash,
ngx_hash_key_lc(findServer.data,findServer.len),
findServer.data,findServer.len);