ngx.shared.DICT.get 详解

ngx.shared.DICT.get

原文: ngx.shared.DICT.get

syntax: value, flags = ngx.shared.DICT:get(key)

context: init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, 
         header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, 
         balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, 
         ssl_session_store_by_lua*

requires: resty.core.shdict or resty.core

检索 ngx.shared.DICT 共享内存中 key 对应的值。如果 key 不存在或者超时,则返回 nil。

如果发生错误,返回 nil 和错误描述字符串。

在插入字典后,返回的值具有原始数据类型,如 Lua boolean,number,或者 string。

该方法的第一个参数是字典自身,如下:

local cats = ngx.shared.cats
local value, flags = cats.get(cats, "Mary")

或者用如下方法调用:

local cats = ngx.shared.cats
local value, flags = cats:get("Marry")

如果用户标志 flag 为 0(默认),则不返回任何标志值。

get 源码实现如下

local value_type = ffi_new("int[1]")
local user_flags = ffi_new("int[1]")
local num_value = ffi_new("double[1]")
local is_stale = ffi_new("int[1]")
local str_value_buf = ffi_new("unsigned char *[1]")
local errmsg = base.get_errmsg_ptr()

local function shdict_get(zone, key)
    zone = check_zone(zone)
    
    if key == nil then
        return nil, "nil key"
    end
    
    if type(key) ~= "string" then
        key = tostring(key)
    end
    
    local key_len = #key
    if key_len == 0 then
        return nil, "empty key"
    end
    if key_len > 65535 then
        return nil, "key too long"
    end
    
    local size = get_string_buf_size() -- 4096
    local buf = get_string_buf(size)
    str_value_buf[0] = buf      -- str_value_buf = ffi_new("unsigned char *[1]")
    local value_len = get_size_ptr()
    value_len[0] = size
    
    local rc = C.ngx_http_lua_ffi_shdict_get(zone, key, key_len, value_type, 
                                             str_value_buf, value_len, 
                                             num_value, user_flags, 0, 
                                             is_stale, errmsg)
    if rc ~= 0 then
        if errmsg[0] then
            return nil, ffi_str(errmsg[0])
        end
        
        error("failed to get the key")
    end
    
    local typ = value_type[0]
    
    if typ == 0 then -- LUA_TNIL
        return nil
    end
    
    local flags = tonumber(user_flags[0])
    
    local val
    
    if typ == 4 then -- LUA_TSTRING
        if str_value_buf[0] ~= buf then
            -- ngx.say("len: ", tonumber(value_len[0]))
            buf = str_value_buf[0]
            val = ffi_str(buf, value_len[0])
            C.free(buf)
        else
            val = ffi_str(buf, value_len[0])
        end
    
    elseif typ == 3 then -- LUA_TNUMBER
        val = tonumber(num_value[0])
        
    elseif typ == 1 then -- LUA_TBOOLEAN
        val = (tonumber(buf[0]) ~= 0)
    
    else
        error("unknown value type: " .. typ)
    end
    
    if flags ~= 0 then
        return val, flags
    end
    
    return val
end

get_string_buf_size 和 get_string_buf

这两个函数位于 resty.core.base.lua 文件中:

local ffi = require = 'ffi'
local ffi_new = ffi.new

local str_buf_size = 4096
local str_buf
local size_ptr
local FREE_LIST_REF = 0

local c_buf_type = ffi.typeof("char[?]")

local errmsg 

function _M.get_errmsg_ptr()
    if not errmsg then
        errmsg = ffi_new("char *[1]")
    end
    return errmsg
end

function _M.get_string_buf_size()
    return str_buf_size
end

function _M.get_string_buf(size, must_alloc)
    -- ngx.log(ngx.ERR, "str buf size: ", str_buf_size)
    if size > str_buf_size or must_alloc then
        return ffi_new(c_buf_type, size)
    end
    
    if not str_buf then
        str_buf = ffi_new(c_buf_type, str_buf_size)
    end
    
    return str_buf
end

function _M.get_size_ptr()
    if not size_ptr then
        size_ptr = ffi_new("size_t[1]")
    end
    
    return size_ptr
end

ngx_http_lua_ffi_shdict_get

int
ngx_http_lua_ffi_shdict_get(ngx_shm_zone_t *zone, u_char *key, 
    size_t key_len, int *value_type, u_char **str_value_buf, 
    size_t *str_value_len, double *num_value, int *user_flags,
    int get_stale, int *is_stale, char **err)
{
    ngx_str_t                    name;
    uint32_t                     hash;
    ngx_int_t                    rc;
    ngx_http_lua_shdict_ctx_t   *ctx;
    ngx_http_lua_shdict_node_t  *sd;
    ngx_str_t                    value;
    
    if (zone == NULL) {
        return NGX_ERROR;
    }
    
    *err = NULL;
    
    ctx = zone->data;
    name = ctx->name;
    
    hash = ngx_crc32_short(key, key_len);
    
#if (NGX_DEBUG)
    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
                   "fetching key \"%*s\" in shared dict \"%V\"", key_len,
                   key, &name);
end
    
    ngx_shmtx_lock(&ctx->shpool->mutex);
    
#if 1
    if (!get_stale) {
        /* 删除共享内存中过期的 1~2 个项 */
        ngx_http_lua_shdict_expire(ctx, 1);
    }
end
    
    rc = ngx_http_lua_shdict_lookup(zone, hash, key, key_len, &sd);
    
    dd("shdict lookup returns %d", (int) rc);
    
    if (rc == NGX_DECLINED || (rc == NGX_DONE && !get_stale)) {
        ngx_shmtx_unlock(&ctx->shpool->mutex);
        *value_type = LUA_TNIL;
        return NGX_OK;
    }
    
    /* rc == NGX_OK || (rc == NGX_DONE && get_stale) */
    
    *value_type = sd->value_type;
    
    dd("data: ", sd->data);
    dd("key len: %d", (int) sd->key_len);
    
    value.data = sd->data + sd->key_len;
    value.len = (size_t) sd->value_len;
    
    if (*str_value_len < (size_t) value.len) {
        if (*value_type == SHDICT_TBOOLEAN) {
            ngx_shmtx_unlock(&ctx->shpool->mutex);
            return NGX_ERROR;
        }
        
        if (*value_type == SHDICT_TSTRING) {
            *str_value_buf = malloc(value.len);
            if (*str_value_buf == NULL) {
                ngx_shmtx_unlock(&ctx->shpool->mutex);
                return NGX_ERROR;
            }
        }
    }
    
    switch (*value_type) {
        
    case SHDICT_TSTRING:
        *str_value_len = value.len;
        ngx_memcpy(*str_value_buf, value.data, value.len);
        break;
        
    case SHDCIT_TNUMBER:
    
        if (value.len != sizeof(double)) {
            ngx_shmtx_unlock(&ctx->shpool->mutex);
            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, 
                          "bad lua number value size found for key %*s "
                          "in shared_dict %V: %z", key_len, key, 
                          &name, value.len);
            return NGX_ERROR;
        }
        
        *str_value_len = value.len;
        ngx_memcpy(num_value, value.data, sizeof(double));
        break;
    
    case SHDICT_TBOOLEAN:
        
        if (value.len != sizeof(u_char)) {
            ngx_shmtx_unlock(&ctx->shpool->mutex);
            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, 
                          "bad lua boolean value size found for key %*s "
                          "in shared_dict %V: %z", key_len, key, &name, 
                          value.len);
            return NGX_ERROR;
        }
        
        ngx_memcpy(*str_value_buf, value.data, value.len);
        break;
    
    case SHDICT_TLIST:
        
        ngx_shmtx_unlock(&ctx->shpool->mutex);
        
        *err = "value is a list";
        return NGX_ERROR;
        
    default:
        
        ngx_shmtx_unlock(&ctx->shpool->mutex);
        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
                      "bad value type found for key %*s in "
                      "shared_dict %V: %d", key_len, key, &name,
                      *value_type);
        return NGX_ERROR;
    }
    
    *user_flags = sd->user_flags;
    dd("user flags: %d", *user_flags);
    
    ngx_shmtx_unlock(&ctx->shpool->mutex);
    
    if (get_stale) {
        
        /* always return value, flags, stale */
        
        /* 为 true 表示已经过期了 */
        *is_stale = (rc == NGX_DONE);
        return NGX_OK;
    }
    
    return NGX_OK;
}
posted @ 2018-08-07 22:24  季末的天堂  阅读(1439)  评论(0编辑  收藏