共享内存 -- ngx_http_limit_req_module模块实例
首先,要使用共享内存,大致为以下几步,可能不同的应用之间会小有点区别,但我们先学会使用简单的,然后我们等对nginx中的共享内存的原理了解之后,就可以轻松运用了。
1. 一般我们需要几个变量来管理共享内存,一般放到全局的配置结构全中去,或直接一个全局变量。ngx_shm_zone_t指针来引用 我们创建的共享内存、ngx_slab_pool_t指针来管理我们共享内存的分配与释放。
2. 在读取或初始化配置文件时,调用ngx_shared_memory_add向全局共享内存链表中添加一个共享内存,然后保存到之前创建的ngx_shm_zone_t指针。接着,注册我们自己的共享内存初始化函数。
3. 在我们自己的初始化函数中, 我们将共享内存初始地址转换为ngx_slab_pool_t指针保存到在步骤一中创建的那个ngx_slab_pool_t指针中。那么在后面,我们就通过这个ngx_slab_pool_t来进行共享内存的分配与释放。
首先来看一些数据结构
typedef struct { u_char color; u_char dummy; u_short len; ngx_queue_t queue; ngx_msec_t last; /* integer value, 1 corresponds to 0.001 r/s */ ngx_uint_t excess; ngx_uint_t count; u_char data[1]; } ngx_http_limit_req_node_t; typedef struct { ngx_rbtree_t rbtree; ngx_rbtree_node_t sentinel; ngx_queue_t queue; } ngx_http_limit_req_shctx_t; typedef struct { ngx_http_limit_req_shctx_t *sh; //我们的共享的数据结构 ngx_slab_pool_t *shpool; //共享内存slab /* integer value, 1 corresponds to 0.001 r/s */ ngx_uint_t rate; ngx_int_t index; ngx_str_t var; ngx_http_limit_req_node_t *node; } ngx_http_limit_req_ctx_t; typedef struct { ngx_shm_zone_t *shm_zone; //保存我们创建的共享内存 /* integer value, 1 corresponds to 0.001 r/s */ ngx_uint_t burst; ngx_uint_t nodelay; /* unsigned nodelay:1 */ } ngx_http_limit_req_limit_t; typedef struct { ngx_array_t limits; ngx_uint_t limit_log_level; ngx_uint_t delay_log_level; } ngx_http_limit_req_conf_t;
在配置文件读取时,我们会创建共享内存.该模块在配置项limit_req_zone读取时调用函数ngx_limit_req_zone,而在此函数中,我们会调用ngx_shared_memory_add来创建我们的共享内存,以及我们注册的初始化函数.
ngx_limit_req_zone函数:
static char * ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ....... shm_zone = ngx_shared_memory_add(cf, &name, size, &ngx_http_limit_req_module); //创建共享内存 ........ shm_zone->init = ngx_http_limit_req_init_zone; //初始化设置函数 shm_zone->data = ctx;//ctx为本地的配置文件,而ctx中保存共享内存的地址 }
然后,在读取配置文件完成后,进程在对共享内存初始化时,回调用我们的初始化函数ngx_http_limit_req_init_zone().
static ngx_int_t ngx_http_limit_req_init_zone(ngx_shm_zone_t *shm_zone, void *data) { ngx_http_limit_req_ctx_t *octx = data; ngx_http_limit_req_ctx_t *ctx; ctx = shm_zone->data; * * * // 在做reload操作时,会传进来 if (octx) { if (ngx_strcmp(ctx->var.data, octx->var.data) != 0) { ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0, "limit_req \"%V\" uses the \"%V\" variable " "while previously it used the \"%V\" variable", &shm_zone->shm.name, &ctx->var, &octx->var); return NGX_ERROR; } // 由于octx是本地内存中分配的,也是在old_cycle中分配的,所以需要在新的ctx中重新初始化一下 // 所以这里只是关于本地内存的重新初始化,而关于共享内存中的初始化工具就不需要再做了 ctx->sh = octx->sh; ctx->shpool = octx->shpool; return NGX_OK; } // 下面是关于共享内存中数据结构的初始化 // 将共享内存指针转换成slab分配池管理,并保存 ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; if (shm_zone->shm.exists) { ctx->sh = ctx->shpool->data; return NGX_OK; } // 之后,就可以使用此共享内存进行空间的分配了 ctx->sh = ngx_slab_alloc(ctx->shpool, sizeof(ngx_http_limit_req_shctx_t)); if (ctx->sh == NULL) { return NGX_ERROR; } return NGX_OK; }
在后面的模块中,我们可以使用ngc_slab_alloc_locked以及ngx_slab_free_locked,对共享内存进行分配和释放.
static ngx_int_t ngx_http_limit_req_handler(ngx_http_request_t *r) { * * * // 在共享内存中创建一个结点 node = ngx_slab_alloc_locked(ctx->shpool, n); if (node == NULL) { // 共享内存空间不足,则尝试释放一点空间 ngx_http_limit_req_expire(ctx, 0); node = ngx_slab_alloc_locked(ctx->shpool, n); if (node == NULL) { ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_HTTP_SERVICE_UNAVAILABLE; } } // 下面对该结点进行初始化操作 lr = (ngx_http_limit_req_node_t *) &node->color; node->key = hash; lr->len = (u_char) len; tp = ngx_timeofday(); lr->last = (ngx_msec_t) (tp->sec * 1000 + tp->msec); lr->excess = 0; ngx_memcpy(lr->data, vv->data, len); // 加入到rbtree里面 ngx_rbtree_insert(&ctx->sh->rbtree, node); * * * }