使用 Nginx + Lua 实现配置文件的默认选择

业务场景

当前业务需求是,当前系统中,针对项目添加不同的版本配置,并可选的设定为默认配置项(即读取项目而不传入版本号,则读取默认版本信息)

原计划使用python实现当前场景,但考虑后期并发问题,可能在python读写数据库或者缓存对服务器产生压力。而考虑以纯静态文件方式来解决后期并发问题,故选择了使用nginx+Lua来实现业务场景。
对于接口的请求,以文件目录结构方式访问, http://xxx.com/project_id/version ,可无需输入version来读取默认配置版本信息。
对于存在的文件,直接nginx读取,对于不存在的路径,则由程序处理

使用python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@mod.route('<path:subpath>', methods=['GET', 'POST'])
def configs(subpath=None):
parts = urllib.parse.unquote(subpath).split('/')
project_id = parts[0] if len(parts) > 0 else None
version = parts[1] if len(parts) > 1 else None

if not os.path.exists(os.path.join(static_path, project_id)):
return Response("None")
path = os.path.join(static_path, project_id, version)
if not os.path.exists(path):
path = os.path.join(static_path, project_id, 'index')
else:
path = os.path.join(static_path, project_id, version, 'index')

with open(path, 'r', encoding='utf-8') as f:
config_info = f.readlines()
return Response("\n".join(config_info), mimetype=ResHelper.JSON)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
server  {
listen 80;
server_name static.com;
lua_code_cache on;
location / {
default_type application/json;
charset utf-8;
add_header Content-Type "application/json; charset=utf-8";
try_files $uri $uri/ @missing_slash;

root E:\\data\\static\\;
index index;
}
# 内部重定向处理器,处理缺少尾部斜杠的路径
location @missing_slash {
internal;
add_header Content-Type "application/json; charset=utf-8";
proxy_pass http://localhost:5001/configfile$uri;
include proxy.conf;
}
}

Lua方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
server  {
listen 80;
server_name static.com;
lua_code_cache on;
location / {
default_type application/json;
charset utf-8;
add_header Content-Type "application/json; charset=utf-8";
try_files $uri $uri/ @missing_slash;

root E:\\data\\static\\;
index index;
}
# 内部重定向处理器,处理缺少尾部斜杠的路径
location @missing_slash {
internal;
add_header Content-Type "application/json; charset=utf-8";

# 定义一个变量来保存 location 的 root 路径
set $root_path "E:\\data\\static\\";
# 使用 lua脚本来校验请求路径
content_by_lua_file lua/a.lua;
}
}

使用lua 判断目录是否存在,存在读取对应的文件内容并返回给接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
function isFile(name)
if type(name)~="string" then return false end
local f = io.open(name)
if f then
f:close()
return true
end
return false
end

function file_exists_v2(file)
-- some error codes:
-- 13 : EACCES - Permission denied
-- 17 : EEXIST - File exists
-- 20 : ENOTDIR - Not a directory
-- 21 : EISDIR - Is a directory
--
local isok, errstr, errcode = os.rename(file, file)
if isok == nil then
if errcode == 13 then
-- Permission denied, but it exists
return true
end
return false
end
return true
end

local root_path = ngx.var.root_path
print("Root Path: ", root_path)

function readFiles(projectid, version)

print("Three segments: ", projectid, ", ", version)
print("", root_path , appid, file_exists_v2(root_path .. projectid .. '/') )
if file_exists_v2(root_path .. projectid) == false then
ngx.say('{"code":404}')
else
print("存在目录" .. root_path .. projectid)
local idx_path = root_path .. projectid .. '/index'
-- --目录存在,判断版本是否存在
if file_exists_v2(root_path .. projectid .. '/' .. version) then
-- -- 读取 index
idx_path = root_path .. projectid .. '/' .. version .. '/index'
end
print(idx_path)
print(isFile(idx_path))
local file = io.open(idx_path, "r")
-- --
-- 读取文件内容
local content = file:read("*all")
ngx.say(content)
file:close()
end
end
--
-- 获取请求的 URI 路径
local uri = ngx.var.uri
print(uri)
--分割 URI 路径
local segments = {}
for segment in string.gmatch(uri, "[^/]+") do
table.insert(segments, segment)
end
--根据路径长度赋值变量
if #segments == 1 then
local var1 = segments[1]
readFiles(var1, nil)
elseif #segments >= 2 then
local var1 = segments[1]
local var2 = segments[2]
readFiles(var1, var2)
end