概述
OpenResty提供了很好的网络IO能力,甚至高性能的访问MySQL、Memcached 以及 Redis 等能力,但对文件的IO,却缺少高效的封装,只能写原生的lua文件处理函数。也可能是我孤陋寡闻,如果知道OpenResty的文件操作函数,麻烦告诉我一声。
为什么OpenResty需要写文件,不是有ngx.log(level,message)函数提供写日志的功能吗?因为有些时候需要对数据进行落盘,只靠ngx.log数据,格式有限,而且与log混在一起,数据不好处理,所以需要单独的写文件功能。
比如,统一采集接口,原先是收到数据后,OpenResty异步写kafka,所谓的异步写kafka,意思是数据先存在内存,再去写kafka。正常情况下,是没有问题的,极端情况下,如kafka服务挂了,内存的容量又有限,很快内存就会存不下,新上报的数据就会丢失。为了避免这种情况,我们设计成数据先落磁盘,再异步写kafka。
lua操作文件的函数很简单。
//打开文件
filehandle = io.open(filePath, "a")
//写文件
fileHandle:write(message, "n")
//数据刷到磁盘
fileHandle:flush()
//文件关闭
fileHandle:close()
问题一:如果每个请求都这么做,都要打开,关闭,效率太低。
思考: 只要想办法把打开的文件描述符存到table里,要写文件时先到table检查文件描述符是否存在,存在即用,不存在则先创建。
遇到难题: 编写在location块里的lua是没能创建全局变量的。
解决难题: 用init_worker_by_lua_block提前创建woker级别的变量。在http块里增加:init_worker_by_lua_block { fileHandleList = {} }//增加worker级别的table变量声明
init_worker_by_lua_block说明:
语法 | init_worker_by_lua_block {lua-script-str} |
配置环境 | http |
阶段 | starting-worker |
含义 | 当master进程被启动后,每个worker进程都会执行Lua代码。如果Nginx禁用了master进程,init_by_lua将会直接运行。 |
location的代码对应更新为:
local filePath = "/data/" .. filename .. ".log"
if io.type(fileHandleList[filePath]) ~= 'file' then
fileHandleList[filePath] = assert(io.open(filePath, "a"))
end
local fileHandle = fileHandleList[filePath]
fileHandle:write(message, "n")
fileHandle:flush()
local res = { code = 1, msg = "send data success!" }
ngx.say(cjson.encode(res))
问题二:在OpenResty多进程的情况下,如果同时写同一个文件,会造成数据混乱。
思考: 为了避免同时写,需要加锁。
遇到难题: 需要一个跨worker的全局锁。
解决难题: 用lua_shared_dict设置一块共享内存区域,可以被各个worker共享。在http块里增加:lua_shared_dict file_locks 1m;//声明全局锁的空间为1M
lua_shared_dict说明:
语法 | lua_shared_dict |
默认值 | 否 |
上下文 | http |
阶段 | 取决于使用 |
含义 | 声明一个共享内存区域,作为基于shm的Lua字典的存储空间ngx.shared.。 共享内存区域始终由当前nginx服务器实例中的所有nginx工作进程共享。 该参数接受大小的单位,如k和m: |
location的代码对应更新为:
local resty_lock = require('resty.lock')
local lock, err = resty_lock:new("file_locks")
local filePath = "/data/" .. filename .. ".log"
if io.type(fileHandleList[filePath]) ~= 'file' then
fileHandleList[filePath] = assert(io.open(filePath, "a"))
end
local fileHandle = fileHandleList[filePath]
local elapsed, err = lock:lock(filename)
if not elapsed then
ngx.log(ngx.WARN, "failed to lock: " .. err)
end
fileHandle:write(message, "n")
fileHandle:flush()
local ok, err = lock:unlock()
if not ok then
ngx.log(ngx.WARN, "failed to unlock: " .. err)
end
local res = { code = 1, msg = "send data success!" }
ngx.say(cjson.encode(res))
最后
以上就是炙热背包为你收集整理的OpenResty的文件IO操作的全部内容,希望文章能够帮你解决OpenResty的文件IO操作所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复