首页 > 工具 > Lua 版写了个简化的 MogileFS 的 Nginx 模块配合多数据中心的地域感知功能
2015
07-24

Lua 版写了个简化的 MogileFS 的 Nginx 模块配合多数据中心的地域感知功能

MogileFS 可以做多数据库中心存储, 这是象我这种方案中非常方便的. 我使用 MogileFS 在电信和联通分别做了一个源站. 我然后请求到联通的直接从联通数据中心的 MogileFS 中取文件, 电信从电信取文件.  ( 怎么样保证存储文件时是二个节点, 请参照 “为 MogileFS 配置使用多个网络段/多数据中心” )

 

默认其实使用 Multiple Networks 是可以保证不同的数据库中心各自有自己的一份文件, 但使用 MogileFS 吐出来的时候, 发现还是有可能命中其它的节点来取文件. 不单有这个问题. 前端使用 Nginx 做为前端加 MogileFS 模块的时候. 因为多数据库中心, 都是使用的公网来连接,所以吐出 500M 的流量, 就要从其它的机器取 500M 的公网流量, 为了减少这个,我需要给  Nginx 返回的取后端 MogileFS 数据的路径修改成内网.

 

为了实现以上需求, 花了点时间研究 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
75
76
77
78
79
80
81
82
83
84
85
86
87
-- 二个内部函数, 用于切 querystring
local function insert_field (tab, name, value, overwrite)
if overwrite or not tab[name] then
tab[name] = value
else
local t = type (tab[name])
if t == "table" then
table.insert (tab[name], value)
else
tab[name] = { tab[name], value }
end
end
end
 
function parse(qs, tab, overwrite)
tab = tab or {}
if type(qs) == "string" then
for key, val in string.gmatch(qs, "([^&=]+)=([^&=]*)&?") do
insert_field(tab, key, val, overwrite)
end
elseif qs then
ngx.log(ngx.ERR, "failed to read the data stream: ".. err)
error("lua.querystring.parse: invalid query string")
end
return tab
end
 
 
-- 正式处理
-- 连接
local sock = ngx.socket.tcp()
local server_pool = { ["xxx.xxx.xxx.xx0"] = "7001", ["xxx.xxx.xxx.xx1"] = "7001" }
 
local ok, err = nil, nil;
for server, port in pairs(server_pool) do
ok, err = sock:connect(server, port)
if ok then
break
end
end
 
if not ok then
ngx.log(ngx.ERR, "failed to connect to server: ".. err)
return
end
 
sock:settimeout(5000)
 
local get_path = "get_paths key=" .. ngx.var.key .. "&domain=" .. ngx.var.domain .."&noverify=1\r\n"
local bytes, err = sock:send(get_path)
local reader = sock:receiveuntil("\r\n")
local data, err, partial = reader()
if not data then
ngx.log(ngx.ERR, "failed to read the data stream: ".. err)
return
end
--sock:setkeepalive()
sock:close()
 
-- 解析
-- 检查 MogileFS 的结果是否存在本文件
if string.sub(data, 0, 3) == 'OK ' then
else
ngx.log(ngx.ERR, "not find domain: " .. ngx.var.domain .. " key" .. ngx.var.key)
ngx.exit(ngx.HTTP_NOT_FOUND)
end
 
-- 1. 按空格切开, 第二部分是需要的. 2. 按 & 符号切开
-- OK path1=http://xxx.xxx.xxx.xxx:7500/dev148/0/002/987/0002987184.fid&paths=1
qs = string.sub(data, 4)
local tab = {}
parse(qs, tab)
 
if tonumber(tab.paths) >= 1 then
local newstr, n, err = ngx.re.sub(tab.path1, "http://wanip.xxx.xxx", "http://192.168.1")
if tonumber(tab.paths) == 1 then
ngx.log(ngx.ERR, "少于指定的份数 domain: " .. ngx.var.domain .. " key" .. ngx.var.key)
end
if newstr then
ngx.var.mogilefs_path = newstr
else
ngx.var.mogilefs_path = tab.path1
end
else
ngx.log(ngx.ERR, "not find domain: " .. ngx.var.domain .. " key" .. ngx.var.key)
ngx.exit(ngx.HTTP_NOT_FOUND)
end

连接本地的 tracker 直接使用那个能连接上就连接的顺序连接….判断是否本地直接使用 ngx.re.sub 来做查找, 顺便替换成本地地址, 这样就实现了我要的功能.

1
2
3
4
5
6
7
8
9
10
location ~ ([^\/]+)\.\w\w\w$ {
expires 365d;
set $domain $host;
set $key $1;
set $mogilefs_path '';
rewrite_by_lua_file "/etc/nginx/conf.d/ngx_lua/mogilefs.lua";
proxy_hide_header Content-Type;
proxy_buffering off;
proxy_pass $mogilefs_path;
}

 

Nginx 就象上面这样配置好了, 本来不使用 rewrite 的阶段, 使用 set_by_lua_file 会更加合适,但会不能使用 tcp:sock 所以只能使用这个. 使用 nginx 原生的 proxy_pass 对于大文件会性能更加好. 修改完后, 所有的本地请求只从本地出.并且通过内网来取真实文件.

最后编辑:
作者:saunix
大型互联网公司linux系统运维攻城狮,专门担当消防员

留下一个回复