又看了一次日志 发现许多如下错误,摘录两条
2014/07/02 17:45:19 [error] 30602#0: *11175540681 lua entry thread aborted: memory allocation error: not enough memory
stack traceback:
coroutine 0:
[C]: in function 'concat'
/letv/www/api/lib/resty/memcached.lua:341: in function 'set'
nginx: lua atpanic: Lua VM crashed, reason: not enough memory
2014/07/02 21:39:41 [error] 9948#0: *11198714680 lua entry thread aborted: memory allocation error: not enough memory
/letv/www/api/lib/resty/memcached.lua:169: in function 'get'
nginx: lua atpanic: Lua VM crashed, reason: not enough memory
2014/07/02 21:44:23 [error] 32090#0: *11199251874 lua entry thread aborted: memory allocation error: not enough memory
/letv/www/api/lib/resty/memcached.lua:341: in function 'set'
难道是memcached.lua:341这个的问题?我们的访问量非常大,是不是memcached.lua里有bug造成了内存溢出,我的代码在上封邮件里。
因为在本地测试服务器打压力测试根本复现不了问题,用tcpcopy把线上的请求引导测试服务器 也是不能重现问题,只要一上线就会有问题……(现象是用top看nginx
有8个worker,其中两个内存涨到了1G多,其他还正常,这个时候就会报错了)而且测试服务器和线上的一样的软件,一样的配置。
用gdb查问题如下:
(gdb) where
#0 0x00007fe7ab288059 in ?? ()
Cannot access memory at address 0x7fffcfe30388
就没有下文了 不知道什么意思 哪段代码有问题。
请大家帮看下 困扰太长时间了
2014/07/02 17:45:19 [error] 30602#0: *11175540681 lua entry thread aborted: memory allocation error: not enough memory
stack traceback:
coroutine 0:
[C]: in function 'concat'
/letv/www/api/lib/resty/memcached.lua:341: in function 'set'
-----邮件原件-----
发件人: 郭阳3
发送时间: 2014年7月7日 14:24
收件人: openresty
主题: 答复: 答复: [openresty] nginx+lua报错libluajit-5.1.so.2.0.0
这个是我的代码:
local memcached = require "lib.resty.memcached"
local PLATFORM_PLAYPLATFORM_MAP = require "conf.platformtable"
--local cjson = require "cjson"
DEFAULT_NUM_PAGE = 1
DEFAULT_NUM_PER_PAGE = 20
MAX_NUM_PER_PAGE = 100
PORDER_ASC = -1
--缓存key生成策略 和后端二级缓存key要一致
function generateVideoListSecondLevelKey(pid, b, s, o, vid, platformId)
local secondLevelCacheKey
secondLevelCacheKey = "ALBUM_VLIST_"..pid.."_"..b.."_"..s.."_"..o
if vid ~= nil then
secondLevelCacheKey = secondLevelCacheKey.."_"..vid
end
if platformId ~= nil then
secondLevelCacheKey = secondLevelCacheKey.."_"..platformId
end
return secondLevelCacheKey
end
--加载数据从resin
function getDataFromResin(url)
res=ngx.location.capture(url)
return res.body
end
function getArgs()
local arg = ""
local args = ngx.req.get_uri_args()
for key, val in pairs(args) do
if type(val) == "table" then
arg = arg .. key .. "=" .. table.concat(val, ", ") .. "&"
else
arg = arg .. key .. "=" .. val .. "&"
end
end
return string.sub(arg,0,-2)
end
id = ngx.var.arg_id
b = ngx.var.arg_b
s = ngx.var.arg_s
o = ngx.var.arg_o
vid = ngx.var.arg_vid
p = ngx.var.arg_p
platform = ngx.var.arg_platform
--ngx.log(ngx.ERR,"run")
if id == nil then
--ngx.say(cjson.encode({callback="",data=null,msgs={"专辑ID为空"},result = 0}))
id = ""
end
if platform == nil then
platform = ""
end
if b == nil then
b = DEFAULT_NUM_PAGE;
end
if s == nil then
s = DEFAULT_NUM_PER_PAGE
elseif tonumber(s) > MAX_NUM_PER_PAGE then
s = MAX_NUM_PER_PAGE
end
if o == nil then
o = PORDER_ASC
end
local platformId
if p == nil then
platformId = PLATFORM_PLAYPLATFORM_MAP[platform]
else
platformId = p
end
if platformId == nil then
platformId = platform
end
local key = generateVideoListSecondLevelKey(id, b, s, o, vid, platformId)
local memc,err = memcached:new()
if not memc then
--ngx.say("failed")
ngx.log(ngx.ERR,"new cbase fail!")
--new cbase有问题 直接从resin取数据
result = getDataFromResin("/t"..ngx.var.uri.."?"..getArgs())
ngx.say(result)
return
end
memc:set_timeout(1000)
local ok, err = memc:connect("127.0.0.1",11211) if not ok then
--ngx.say("fail")
ngx.log(ngx.ERR,"connect the cbase fail!"..err)
--连接cbase有问题 直接从resin取数据
result = getDataFromResin("/t"..ngx.var.uri.."?"..getArgs())
ngx.say(result)
return
end
local res, flags, err = memc:get(key)
if err then
--ngx.say("failed to get : ", err)
ngx.log(ngx.ERR,"failed to get : "..key)
--获取cbase数据有问题 直接从resin取数据
result = getDataFromResin("/t"..ngx.var.uri.."?"..getArgs())
ngx.say(result)
memc:set_keepalive(10000, 100)
return
end
local result
if res ~= nil then
result = res
end
if not res then
ngx.log(ngx.ERR,"load from resin,the key "..key)
--res=ngx.location.capture("/t/mms/inner/albumInfo/getVideoList?id=76913&platform=pc")
result = getDataFromResin("/t"..ngx.var.uri.."?"..getArgs())
local ok, err = memc:set(key, result,600)
if not ok then
ngx.log(ngx.ERR,"failed to set : "..key)
--return
end
else
ngx.log(ngx.ERR,"hit it:"..key)
end
ngx.say(result)
memc:set_keepalive(10000, 100)
以上代码很简单的逻辑 我这里也没有查出什么问题,我挨个删除代码,在local res, flags, err = memc:get(key)这块取数据的时候,nginx实际占用内存会升高,
基本数据为90kb左右的,我想大并发量下把取出的数据放在变量里,nginx肯定会实际内存占用量升高的,vm应该垃圾回收不是那么快的。请帮看看代码哪里还有问题!我试过都换成最新的luajit效果貌似也不太明显(还会有nginx占用内存升高)但是没出现线上的内存溢出现象,我在测试环境测不出来线上的问题可能打压不够,模仿不了。对lua语言不太熟……,现学现用
-----邮件原件-----
发件人: openresty@googlegroups.com [mailto:openresty@googlegroups.com] 代表 Yichun Zhang (agentzh)
发送时间: 2014年7月4日 5:27
收件人: openresty
主题: Re: 答复: [openresty] nginx+lua报错libluajit-5.1.so.2.0.0
Hello!
2014-07-02 18:49 GMT-07:00 郭阳3:
> 谢谢春哥,昨天查到 nginx占用内存达到了2g而其他没有上线这个lua代码的只有200m左右,
你使用的 LuaJIT 2.0.0 这个老版本有已知的内存泄漏 bug. 我上一封邮件已经建议你至少升级到 2.0.3. 请按我说的去做。
另外,你需要仔细你的 Lua 代码本身的逻辑没有内存泄漏,比如你没有在请求处理程序中不断地向 package.path
这样的全局字符串不断追加新的内容数据,你也没有在你自己的模块级别的变量里不断追加数据(比如无限制地向一个 table 里插入数据),见
https://github.com/openresty/lua-nginx-module#data-sharing-within-an-nginx-worker
同时确保你使用的 nginx 和 ngx_lua
的版本足够新。如果不是最新版本,请首先尝试升级。正如我上一封邮件里建议的,“尽量总是使用最新的 openresty
发布以避免很多不必要的麻烦”。
特别地,对于 Lua 空间里的内存泄漏问题,可以使用我们的 lgcstat 和 lgcpath 这两个 gdb 扩展命令对进程或 core dump 进行分析和排查:
https://github.com/openresty/nginx-gdb-utils#lgcstat
https://github.com/openresty/nginx-gdb-utils#lgcpath
> 而且日志里总报
> [error] 29597#0: *11174695624 failed to load Lua inlined code: cannot
> open /letv/www/api/getVideoListOut.lua: No such file or directory,
显然你使用的是一个比较古老的 ngx_lua 模块的版本。升级后再看。
> 难道是nginx每次都加载lua文件的原因吗
>
ngx_lua 模块并不会在每次请求时现加载 .lua 文件,除非
1. 你在 nginx.conf 里面配置了“lua_code_cache off;”(强烈建议在生产环境禁用 Lua 代码缓存),或者
2. 你自己在请求处理程序里面使用 loadfile() 这样的标准 Lua 函数加载外部 .lua 文件,或者通过清空
package.loaded 表里的条目强制 require() 重新加载对应的 Lua 模块文件。
Regards,
-agentzh
--