大家看到在构建一个ngx_hash_wildcard_t的时候,需要对通配符的哪些key进行预处理。这个处理起来比较麻烦。而当有一组key,这些里面既有无通配符的key,也有包含通配符的key的时候。我们就需要构建三个hash表,一个包含普通的key的hash表,一个包含前向通配符的hash表,一个包含后向通配符的hash表(或者也可以把这三个hash表组合成一个ngx_hash_combined_t)。在这种情况下,为了让大家方便的构造这些hash表,nginx提供给了此辅助类型。 该类型以及相关的操作函数也定义在src/core/ngx_hash.h|c里。我们先来看一下该类型的定义。 [](http:// "点击提交Issue,反馈你的意见...") typedef struct { ngx_uint_t hsize; ngx_pool_t *pool; ngx_pool_t *temp_pool; ngx_array_t keys; ngx_array_t *keys_hash; ngx_array_t dns_wc_head; ngx_array_t *dns_wc_head_hash; ngx_array_t dns_wc_tail; ngx_array_t *dns_wc_tail_hash; } ngx_hash_keys_arrays_t; <table class="docutils field-list" frame="void" rules="none" style="margin: 0px -0.5em; border: 0px; color: rgb(0, 0, 0); font-family: 'segoe UI', sans-serif; letter-spacing: -0.1599999964237213px; line-height: 24px; white-space: normal; background-color: rgb(255, 255, 255);"><colgroup><col class="field-name"/><col class="field-body"/></colgroup><tbody valign="top"><tr class="field-odd field"><th class="field-name" style="padding: 1px 8px 1px 5px; border: 0px !important;">hsize:</th><td class="field-body" style="padding: 1px 8px 1px 5px; border: 0px !important;">将要构建的hash表的桶的个数。对于使用这个结构中包含的信息构建的三种类型的hash表都会使用此参数。</td></tr><tr class="field-even field"><th class="field-name" style="padding: 1px 8px 1px 5px; border: 0px !important;">pool:</th><td class="field-body" style="padding: 1px 8px 1px 5px; border: 0px !important;">构建这些hash表使用的pool。</td></tr><tr class="field-odd field"><th class="field-name" style="padding: 1px 8px 1px 5px; border: 0px !important;">temp_pool:</th><td class="field-body" style="padding: 1px 8px 1px 5px; border: 0px !important;">在构建这个类型以及最终的三个hash表过程中可能用到临时pool。该temp_pool可以在构建完成以后,被销毁掉。这里只是存放临时的一些内存消耗。</td></tr><tr class="field-even field"><th class="field-name" style="padding: 1px 8px 1px 5px; border: 0px !important;">keys:</th><td class="field-body" style="padding: 1px 8px 1px 5px; border: 0px !important;">存放所有非通配符key的数组。</td></tr><tr class="field-odd field"><th class="field-name" style="padding: 1px 8px 1px 5px; border: 0px !important;">keys_hash:</th><td class="field-body" style="padding: 1px 8px 1px 5px; border: 0px !important;">这是个二维数组,第一个维度代表的是bucket的编号,那么keys_hash[i]中存放的是所有的key算出来的hash值对hsize取模以后的值为i的key。假设有3个key,分别是key1,key2和key3假设hash值算出来以后对hsize取模的值都是i,那么这三个key的值就顺序存放在keys_hash[i][0],keys_hash[i][1], keys_hash[i][2]。该值在调用的过程中用来保存和检测是否有冲突的key值,也就是是否有重复。</td></tr><tr class="field-even field"><th class="field-name" style="padding: 1px 8px 1px 5px; border: 0px !important;">dns_wc_head:</th><td class="field-body" style="padding: 1px 8px 1px 5px; border: 0px !important;">放前向通配符key被处理完成以后的值。比如:“*.abc.com” 被处理完成以后,变成 “com.abc.” 被存放在此数组中。</td></tr><tr class="field-odd field"><th class="field-name" style="padding: 1px 8px 1px 5px; border: 0px !important;">dns_wc_tail:</th><td class="field-body" style="padding: 1px 8px 1px 5px; border: 0px !important;">存放后向通配符key被处理完成以后的值。比如:“mail.xxx.*” 被处理完成以后,变成 “mail.xxx.” 被存放在此数组中。</td></tr><tr class="field-even field"><th class="field-name" colspan="2" style="padding: 1px 8px 1px 5px; border: 0px !important;">dns_wc_head_hash:</th></tr><tr class="field-even field"><td style="padding: 1px 8px 1px 5px; border: 0px !important;"> </td><td class="field-body" style="padding: 1px 8px 1px 5px; border: 0px !important;">该值在调用的过程中用来保存和检测是否有冲突的前向通配符的key值,也就是是否有重复。</td></tr><tr class="field-odd field"><th class="field-name" colspan="2" style="padding: 1px 8px 1px 5px; border: 0px !important;">dns_wc_tail_hash:</th></tr><tr class="field-odd field"><td style="padding: 1px 8px 1px 5px; border: 0px !important;"> </td><td class="field-body" style="padding: 1px 8px 1px 5px; border: 0px !important;">该值在调用的过程中用来保存和检测是否有冲突的后向通配符的key值,也就是是否有重复。</td></tr></tbody></table> 在定义一个这个类型的变量,并对字段pool和temp_pool赋值以后,就可以调用函数ngx_hash_add_key把所有的key加入到这个结构中了,该函数会自动实现普通key,带前向通配符的key和带后向通配符的key的分类和检查,并将这个些值存放到对应的字段中去, 然后就可以通过检查这个结构体中的keys、dns_wc_head、dns_wc_tail三个数组是否为空,来决定是否构建普通hash表,前向通配符hash表和后向通配符hash表了(在构建这三个类型的hash表的时候,可以分别使用keys、dns_wc_head、dns_wc_tail三个数组)。 构建出这三个hash表以后,可以组合在一个ngx_hash_combined_t对象中,使用ngx_hash_find_combined进行查找。或者是仍然保持三个独立的变量对应这三个hash表,自己决定何时以及在哪个hash表中进行查询。 [](http:// "点击提交Issue,反馈你的意见...") ngx_int_t ngx_hash_keys_array_init(ngx_hash_keys_arrays_t *ha, ngx_uint_t type); 初始化这个结构,主要是对这个结构中的ngx_array_t类型的字段进行初始化,成功返回NGX_OK。 | ha: | 该结构的对象指针。 | |-----|-----| | type: | 该字段有2个值可选择,即NGX_HASH_SMALL和NGX_HASH_LARGE。用来指明将要建立的hash表的类型,如果是NGX_HASH_SMALL,则有比较小的桶的个数和数组元素大小。NGX_HASH_LARGE则相反。 | [](http:// "点击提交Issue,反馈你的意见...") ngx_int_t ngx_hash_add_key(ngx_hash_keys_arrays_t *ha, ngx_str_t *key, void *value, ngx_uint_t flags); 一般是循环调用这个函数,把一组键值对加入到这个结构体中。返回NGX_OK是加入成功。返回NGX_BUSY意味着key值重复。 | ha: | 该结构的对象指针。 | |-----|-----| | key: | 参数名自解释了。 | | value: | 参数名自解释了。 | | flags: | 有两个标志位可以设置,NGX_HASH_WILDCARD_KEY和NGX_HASH_READONLY_KEY。同时要设置的使用逻辑与操作符就可以了。NGX_HASH_READONLY_KEY被设置的时候,在计算hash值的时候,key的值不会被转成小写字符,否则会。NGX_HASH_WILDCARD_KEY被设置的时候,说明key里面可能含有通配符,会进行相应的处理。如果两个标志位都不设置,传0。 | 有关于这个数据结构的使用,可以参考src/http/ngx_http.c中的ngx_http_server_names函数。