共享内存 -- 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);  
  
    * * *  
}  

 

 

posted @ 2012-11-26 18:38  风去无痕  阅读(1618)  评论(0编辑  收藏  举报