记一次lua io使用不当导致内存泄露问题
Posted 编程阁楼
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了记一次lua io使用不当导致内存泄露问题相关的知识,希望对你有一定的参考价值。
02.分析过程
client_max_body_size 500m;
业务上要求能支持最大500m文件上传。
proxy_send_timeout 600s;
proxy_connect_timeout 600s;
proxy_read_timeout 600s;
send_timeout 600s;
三、服务端错误
对应状态码有500,502和504。
500, Internal Server Error , 服务器内部错误,服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。
502,Bad Gateway,网关错误,它往往表示网关从上游服务器中接收到的响应是无效的。
504,Gateway Timeout,网关超时。
2020/09/07 15:21:33 [error] 51#0: *4260 [lua] responses.lua:121: send(): failed to get from node cache: callback threw an error: /usr/local/share/lua/5.1/pgmoon/init.lua:233: attempt to index local 'sock' (a nil value), client: 172.168.0.1, server: kong, request: "POST /obs/zcy.obs.file.upload HTTP/1.1", host: "api.test.com"
172.18.212.3 - - [07/Sep/2020:15:21:33 +0800] api.test.com POST "/obs/zcy.obs.file.upload" "-" 500 54 "-" - - 90.599 - "Apache-HttpClient/4.5.1 (Java/1.8.0_92)" "172.18.212.3" "-" ggtest -
2020/09/07 15:22:44 [error] 48#0: *105700 lua coroutine: memory allocation error: not enough memory
stack traceback:
coroutine 0:
[C]: in function '(for generator)'
/usr/local/share/lua/5.1/multipart.lua:43: in function 'decode'
/usr/local/share/lua/5.1/multipart.lua:115: in function 'Multipart'
...are/lua/5.1/kong/plugins/hmac-auth-ocean-file/access.lua:273: in function 'init_form_args'
...are/lua/5.1/kong/plugins/hmac-auth-ocean-file/access.lua:619: in function 'do_authentication'
...are/lua/5.1/kong/plugins/hmac-auth-ocean-file/access.lua:676: in function 'execute'
...re/lua/5.1/kong/plugins/hmac-auth-ocean-file/handler.lua:14: in function <...re/lua/5.1/kong/plugins/hmac-auth-ocean-file/handler.lua:12>
coroutine 1:
[C]: in function 'resume'
coroutine.wrap:21: in function <coroutine.wrap:21>
/usr/local/share/lua/5.1/kong/init.lua:379: in function 'access'
access_by_lua(nginx-kong.conf:94):2: in function <access_by_lua(nginx-kong.conf:94):1>, client: 172.18.212.3, server: kong, request: "POST /obs/zcy.obs.file.upload HTTP/1.1", host: "api.test.com"
一、内存飙升理论可能性分析
2020/09/08 18:47:56 [warn] 60#0: *127418 a client request body is buffered to a temporary file /usr/local/kong/client_body_temp/0000000009, client: 172.18.212.3, server: kong, request: "POST /obs/zcy.obs.file.upload HTTP/1.1", host: "api.test.com"
请求体的size大于nginx配置里的client_body_buffer_size,则会导致请求体被缓冲到磁盘临时文件里,client_body_buffer_size默认是8k或者16k
二、签名插件逻辑大bug
小编这里的签名插件是用lua脚本写的,核心片段如下:
--这里必须使用'Content-Type',首字符大写
if string.sub(receive_headers[CONTENT_TYPE], 1, 20) == "multipart/form-data;" then
--判断是否是multipart/form-data类型的表单
is_have_file_param = true
local body_data = ngx.req.get_body_data()
if not body_data then
local datafile = ngx.req.get_body_file()
ngx.log(ngx.ERR, "body is in file: ", tostring(datafile))
if not datafile then
error_code = 1
error_msg = "no request body found"
else
local fh, err = io.open(datafile, "r")
if not fh then
error_code = 2
error_msg = "failed to open " .. tostring(datafile) .. "for reading: " .. tostring(err)
else
fh:seek("set")
body_data = fh:read("*a")
fh:close()
if body_data == "" then
error_code = 3
error_msg = "request body is empty"
end
end
end
end
fh:seek("set") --设置文件从头开始读取
body_data = fh:read("*a") --读取全部内容
原因已经找到,接下来看怎么解决该问题。我们来看下缓存文件中的内容是什么:
------ZcyOpenBoundaryj5pjAaN060Reqm05
Content-Disposition: form-data; name="_data_"
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
{"fileName":"nohup.out","bizCode":"1071"}
------ZcyOpenBoundaryj5pjAaN060Reqm05
Content-Disposition: form-data; name="file"; filename="deaultFilename"
Content-Type: application/octet-stream
... 省略内容 ...
--这里必须使用'Content-Type',首字符大写
if string.sub(receive_headers[CONTENT_TYPE], 1, 20) == "multipart/form-data;" then
--判断是否是multipart/form-data类型的表单
is_have_file_param = true
local body_data = ngx.req.get_body_data()
if not body_data then
local datafile = ngx.req.get_body_file()
ngx.log(ngx.ERR, "body is in file: ", tostring(datafile))
if not datafile then
error_code = 1
error_msg = "no request body found"
else
local fh, err = io.open(datafile, "r")
if not fh then
error_code = 2
error_msg = "failed to open " .. tostring(datafile) .. "for reading: " .. tostring(err)
else
fh:seek("set")
local i = 0
local continue_flag = false
for line in fh:lines() do
local match_result = string.match(line, "_data_")
if match_result then
continue_flag = true
end
if continue_flag then
i = i + 1
end
if i==5 then
local position = string.find(line, "\r");
local body_data_str = string.sub(line, 1, position-1)
args["_data_"] = unescape(body_data_str)
break
end
end
fh:close()
end
end
end
local i = 0
local continue_flag = false
for line in fh:lines() do
local match_result = string.match(line, "_data_")
if match_result then
continue_flag = true
end
if continue_flag then
i = i + 1
end
if i==5 then
local position = string.find(line, "\r");
local body_data_str = string.sub(line, 1, position-1)
args["_data_"] = unescape(body_data_str)
break
end
end
05. 效果立现
以上是关于记一次lua io使用不当导致内存泄露问题的主要内容,如果未能解决你的问题,请参考以下文章