nginx 变量的保存结构和优化
来源:互联网 发布:c语言小游戏程序 编辑:程序博客网 时间:2024/06/09 14:06
在使用nginx lua 时经常会使用ngx.var.varname 来获取变量,随着业务的越复杂,变量越来越多,发现服务的速度明显变慢了本文分析了整个变量的获取和保存逻辑
- ngx_lua 获取变量
首先ngx_lua 获取变量调用的是 ngx_http_lua_var_get(lua_State *L)
46-static int 47ngx_http_lua_var_get(lua_State *L) 48{ 49 ngx_http_request_t *r; 50 u_char *p, *lowcase; 51 size_t len; 52 ngx_uint_t hash; 53 ngx_str_t name; 54 ngx_http_variable_value_t *vv; 55 56#if (NGX_PCRE) 57 u_char *val; 58 ngx_uint_t n; 59 LUA_NUMBER index; 60 int *cap; 61#endif 62 63 r = ngx_http_lua_get_req(L); 64 if (r == NULL) { 65 return luaL_error(L, "no request object found"); 66 } ... 111 p = (u_char *) lua_tolstring(L, -1, &len);112113 lowcase = lua_newuserdata(L, len);114115 hash = ngx_hash_strlow(lowcase, p, len);116117 name.len = len;118 name.data = lowcase;119 dd("variable name: %.*s", (int) len, lowcase);120 vv = ngx_http_get_variable(r, &name, hash);
可见就是调用nginx的变量获取接口, 可以看到前面将变量名全部变为小写然后计算hash,ngx_lua是不区分大小写的
- nginx 变量的获取
546-ngx_http_variable_value_t * 547ngx_http_get_variable(ngx_http_request_t *r, ngx_str_t *name, ngx_uint_t key) 548{ 549 ngx_http_variable_t *v; 550 ngx_http_variable_value_t *vv; 551 ngx_http_core_main_conf_t *cmcf; 552 553 cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); 554 555 v = ngx_hash_find(&cmcf->variables_hash, key, name->data, name->len); 556
- nginx 变量的保存结构
前面已经猜到nginx 的变量是保存在hash表中的, 主要的结构保存在ngx_hash.c 中
这里主要分析两个函数ngx_hash_find 和 ngx_hash_init
ngx_hash_find 就是典型的拉链式hash表的处理方式,先定位bucket, 然后挨个对比,那么效率就在于hash bucket的构建上了
12-void * 13ngx_hash_find(ngx_hash_t *hash, ngx_uint_t key, u_char *name, size_t len) 14{ 15 ngx_uint_t i; 16 ngx_hash_elt_t *elt; 17 18#if 0 19 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "hf:\"%*s\"", len, name); 20#endif 21 22 elt = hash->buckets[key % hash->size]; 23 24 if (elt == NULL) { 25 return NULL; 26 } 27 28 while (elt->value) { 29 30 if (len != (size_t) elt->len) { 31 goto next; 32 } 33 34 for (i = 0; i < len; i++) { 35 if (name[i] != elt->name[i]) { 36 goto next; 37 } 38 } 39 40 return elt->value; 41 42 next: 43 44 elt = (ngx_hash_elt_t *) ngx_align_ptr(&elt->name[0] + elt->len, 45 sizeof(void *)); 46 continue; 47 } 48 49 return NULL; 50}
下面我们来看一下 hash bucket的构建
// 用于计算存储一个变量所需要的空间249#define NGX_HASH_ELT_SIZE(name) \ 250 (sizeof(void *) + ngx_align((name)->key.len + 2, sizeof(void *))) 251 252-ngx_int_t 253ngx_hash_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, ngx_uint_t nelts) 254{
NGX_HASH_ELT_SIZE 是计算每个key 所占用的空间
每一个bucket 中key的保存如下
ngx_hash_elt_t
因为key.len 是short 类型的所以需要在+2 然后对齐
nginx 对于变量的保存是存在hash 表中, 在配置中有两个参数
server_names_hash_bucket_size 每个bucket 的大小,决定着每个bucket 最多能放入多少个对象
server_names_hash_max_size 最大的bucket 数量
nginx 用拿现有的变量列表从一个hash size开始尝试, 及 尝试把所有符合条件 hash(key)%size 的key放到一个bucket 中,不过不能则增大size 直到 max_size,让然不行就会报错。
这中间就会有两个问题
1 这种尝试会使nginx 启动变慢
2 如果bucket size 过大会使太多的单个bucket 中存放了太多的对象导致,在运行中获取变量时的开销变大,相应的bucket 太小又会使size 很大消耗更多的内存
nginx 在这方面是选择了在参数内尽可能的减少内存的使用,但是在实际环境中增大内存来加快变量的访问时非常可取的也是。
252-ngx_int_t 253ngx_hash_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, ngx_uint_t nelts) { ... // 1 首先check 一下当前bucekt size 是不是能放下所有的对象 270 for (n = 0; n < nelts; n++) { 271 ngx_uint_t var_size = NGX_HASH_ELT_SIZE(&names[n]) + sizeof(void *); 272 if (hinit->bucket_size < NGX_HASH_ELT_SIZE(&names[n]) + sizeof(void *)) 273 { 274 ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0, 275 "could not build %s, you should " 276 "increase %s_bucket_size: %i", 277 hinit->name, hinit->name, hinit->bucket_size); 278 return NGX_ERROR; 279 } 280 283 if (NULL == names[n].key.data){ 284 ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0, "ngx_hash_init error var %i", n); 285 continue; 286 } } ... // 接下来开始, 顺序尝试,看能否确定一个 size 后, 看能不能把符合hash%size的key 放入同一个桶中, 如果不能则继续增大size 直至max_size 311 if (hinit->max_size > 10000 && nelts && hinit->max_size / nelts < 100) { 312 start = hinit->max_size - 1000; 313 } 314 ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0, 315 "ngx_hash_init start confirm size... " 316 "name %s start(%i) max_size(%i)", 317 hinit->name, start, hinit->max_size); 318 if (hinit->max_size < 20*nelts) 319 hinit->max_size = 20*nelts; 320 for (size = start; size <= hinit->max_size; size++) { 321 322 ngx_memzero(test, size * sizeof(u_short)); 323 324 for (n = 0; n < nelts; n++) { 325 if (names[n].key.data == NULL) { 326 continue; 327 } 328 329 key = names[n].key_hash % size; 330 test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n])); 331 332#if 0 333 ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0, 334 "%ui: %ui %ui \"%V\"", 335 size, key, test[key], &names[n].key); 336#endif 337 338 if (test[key] > (u_short) bucket_size) { 339 goto next; 340 } 341 } 342 ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0, 343 "ngx_hash_init confirm size " 344 "name %s bucket_cnt(%i) var_cnt(%i) bucket_size(%i) max_size(%i)", 345 hinit->name, size, nelts, hinit->bucket_size, hinit->max_size); 346 goto found; 347 348 next: 349 350 continue; 351 } 352 353 size = hinit->max_size; 354 355 ngx_log_error(NGX_LOG_WARN, hinit->pool->log, 0, 356 "could not build optimal %s, you should increase " 357 "either %s_max_size: %i or %s_bucket_size: %i; " 358 "ignoring %s_bucket_size", 359 hinit->name, hinit->name, hinit->max_size, 360 hinit->name, hinit->bucket_size, hinit->name); 361 362found: 364 for (i = 0; i < size; i++) { // 每一个bucket 都至少有一个元素,没有value 365 test[i] = sizeof(void *); 366 }... // 确定了size 后面就开始根据元素来分配空间,创建hash表, 填充空间了 }
nginx 的hash 是静态表不能运行是添加或删除, 一种优化的方案是在构造hash表时根据所需要存的变量计算bucket size, 例如是平均值的几倍,这样避免业务变化后需要重新调整这参数,也避免了参数不匹配造成的性能问题。另一个就是在尝试size时 初始尝试一个较大值, 毕竟size 过小也会使单个bucket 中的元素过多影响访问性能,同事也能尽快的找到合适的size 加速度nginx 启动。
后续的填充逻辑有机会再详细分析。
- nginx 变量的保存结构和优化
- IDl保存和恢复变量的方法
- Saver类--变量的保存和恢复
- tensorflow中变量的保存和加载
- nginx的变量和配置指令
- Nginx的源码结构和模块初始化
- nginx的简单优化和反向代理
- 结构体变量的初始化和赋值
- 结构体变量的引用和赋值
- 21.变量的结构和类型
- 结构变量的定义和引用
- 线程变量的保存
- 结构体的定义和结构变量的定义
- Nginx和Apche优化
- matlab工作空间,变量的保存和载入
- Nginx的变量机制
- nginx变量的使用
- [nginx]nginx的性能优化
- Opencv中 findContours 函数参数说明
- JS回到顶部按钮实现源代码
- js中的this关键字
- hdoj 1091
- 读取spring的配置来定义常量
- nginx 变量的保存结构和优化
- Jenkins进行git多分支发布
- android api学习笔记:任务和返回栈
- ios 输入数组,生成索引
- AFNetworking 3.0 源码解读(七)之 AFAutoPurgingImageCache
- 时间戳转换
- 信息查询系统编写--三层架构
- Java读取文件夹大小的6种方法及代码
- linux设备驱动模型之uevent