因为业务需要使用ngx.shared 共享内存,但是存储大内存变量时报no memory,实际显示还有内存,怀疑是小内存碎片,分配不出大内存,通过走读源码发现,nginx 是在一块连续的地址上进行内存分配,并将内存按照page大小进行操作。
ngx.shared.dict set实现
下面的代码基于openresty1.21.4.3 版本
走读ngx.shared源码,分析set操作时,如何从内存池中获取内存
ngx.shared.dict 实现逻辑位于文件 shdict.lua中
local _, dict = next(ngx_shared, nil)
if dict then
local mt = getmetatable(dict)
if mt then
mt = mt.__index
if mt then
mt.get = shdict_get
mt.get_stale = shdict_get_stale
mt.incr = shdict_incr
mt.set = shdict_set
mt.safe_set = shdict_safe_set
mt.add = shdict_add
mt.safe_add = shdict_safe_add
mt.replace = shdict_replace
mt.delete = shdict_delete
mt.flush_all = shdict_flush_all
mt.ttl = shdict_ttl
mt.expire = shdict_expire
mt.capacity = shdict_capacity
mt.free_space = shdict_free_space
end
end
end
set调用share_set函数,实现为shdict_store, op为0
local function shdict_set(zone, key, value, exptime, flags)
return shdict_store(zone, 0, key, value, exptime, flags)
end
local function shdict_store(zone, op, key, value, exptime, flags)
zone = check_zone(zone)
if not exptime then
exptime = 0
elseif exptime < 0 then
error('bad "exptime" argument', 2)
end
if not flags then
flags = 0
end
if key == nil then
return nil, "nil key"
end
-- 如果key不为字符串,转换成字符串
if type(key) ~= "string" then
key = tostring(key)
end
local key_len = #key
if key_len == 0 then
return nil, "empty key"
end
-- 不能存储长度大于65535的key
if key_len > 65535 then
return nil, "key too long"
end
local str_val_buf
local str_val_len = 0
local num_val = 0
local valtyp = type(value)
-- print("value type: ", valtyp)
-- print("exptime: ", exptime)
-- 定义了value可以存储的类型:string / number /nil /boolean, nil 用于删除key时使用
if valtyp == "string" then
valtyp = 4 -- LUA_TSTRING
str_val_buf = value
str_val_len = #value
elseif valtyp == "number" then
valtyp = 3 -- LUA_TNUMBER
num_val = value
elseif value == nil then
valtyp = 0 -- LUA_TNIL
elseif valtyp == "boolean" then
valtyp = 1 -- LUA_TBOOLEAN
num_val = value and 1 or 0
else
return nil, "bad value type"
end
local rc = ngx_lua_ffi_shdict_store(zone, op, key, key_len,
valtyp, str_val_buf,
str_val_len, num_val,
exptime * 1000, flags, errmsg,
forcible)
-- print("rc == ", rc)
if rc == 0 then -- NGX_OK
return true, nil, forcible[0] == 1
end
-- NGX_DECLINED or NGX_ERROR
return false, ffi_str(errmsg[0]), forcible[0] == 1
end
核心函数
int ngx_http_lua_ffi_shdict_store(void *zone, int op,
const unsigned char *key, size_t key_len, int value_type,
const unsigned char *str_value_buf, size_t str_value_len,
double num_value, long exptime, int user_flags, char **errmsg,
int *forcible);
set时op的值为0,为了方便阅读,删除op非0是的逻辑
int
ngx_http_lua_ffi_shdict_store(ngx_shm_zone_t *zone, int op, u_char *key,
size_t key_len, int value_type, u_char *str_value_buf,
size_t str_value_len,