先说一下设计思路,看图
通过设计的流程图,我的处理流程就是按照这个执行的
今天说 STOP 0
当用户请求到了,我首先需要获取用户真实IP。
1:连接IP就是用户真实IP
2:走CDN时,用户IP在指定的http头中
所以这里我们就需要有配置文件来配置取用户真实IP是怎么取的,我先设计了一个json
{
"id.game.com": {
"ips": [
"111.206.199.57"
],
"realipset": "CDN-R-IP"
},
"101.200.122.200": {
"ips": [
"101.254.241.149",
"106.37.236.170"
],
"realipset": "x-for-f"
},
"game.game.com": {
"ips": "*",
"realipset": "x-forword-for"
}
}
解释一下,host是id.game.com的请求,直连IP为:111.206.199.57的,用户真实IP在http头CDN-R-IP中。ips就是我们的CDN节点服务器列表了,目前仅支持IP列表的写法,还没有增加使用IP段这种写法。另外2个也同样这么理解即可。
在开启看取用户IP的代码:
local remoteIp = ngx.var.remote_addr
local headers = ngx.req.get_headers()
local host = ngx.req.get_headers()["Host"] or "unknownhost"
local method = ngx.var.request_method
local url = ngx.unescape_uri(ngx.var.uri)
local referer = headers["referer"] or "unknownreferer"
local agent = headers["user_agent"] or "unknownagent"
--local request_url = ngx.unescape_uri(ngx.var.request_uri)
local config_dict = ngx.shared.config_dict
local limit_ip_dict = ngx.shared.limit_ip_dict
local ip_dict = ngx.shared.ip_dict
local cjson_safe = require "cjson.safe"
local config_base = cjson_safe.decode(config_dict:get("base")) or {}
--- 判断config_dict中模块开关是否开启
local function config_is_on(config_arg)
if config_base[config_arg] == "on" then
return true
end
end
--- 取config_dict中的json数据
local function getDict_Config(Config_jsonName)
local re = cjson_safe.decode(config_dict:get(Config_jsonName)) or {}
return re
end
-- 传入 (host 连接IP http头)
local function loc_getRealIp(host,remoteIP,headers)
if config_is_on("realIpFrom_Mod") then
local realipfrom = getDict_Config("realIpFrom_Mod")
local ipfromset = realipfrom[host]
if type(ipfromset) ~= "table" then return remoteIP end
if ipfromset.ips == "*" then
local ip = headers[ipfromset.realipset]
if ip then
if type(ip) == "table" then ip = ip[1] end --- http头中又多个取第一个
else
ip = remoteIP
end
return ip
else
for i,v in ipairs(ipfromset.ips) do
if v == remoteIP then
local ip = headers[ipfromset.realipset]
if ip then
if type(ip) == "table" then ip = ip[1] end --- http头中又多个取第一个
else
ip = remoteIP
end
return ip
end
end
return remoteIP
end
else
return remoteIP
end
end
这里用了几个API,参考NGINX API for Lua。
取连接ip,headers,host;host是从headers这个table中取出来的。还有一些其他需要用到的如method,url(已经url转码后),referer,useragnet等,
config_is_on 函数,是判断是否开启了模块,从http头中获取用户真实IP。
PS:下一篇我说一下,程序的初始化阶段
config_base 是从dict中取的总配置数据,并转化成json的。"on" 表示开启,区分大小写的,代码中使用字符串直接进行判断的。
loc_getRealIp 函数,是取用户真实IP的主函数,首先判断模块"realIpFrom_Mod"是否开启从http头取用户IP;其次读取"realIpFrom_Mod"配置列表(json转化后),就是通过函数getDict_Config来取的;接下来就是通过请求的host从列表中取自己对应的配置,如果没有就直接返回连接IP作为用户真实ip,如果取到了先判断ips是不是 *,ips=* 表示所有ip来的都从http头取用户真实ip,否则就循环匹配ips中所有ip和当前ip是否相等,没有相等的就返回连接ip作为用户真实ip,剩下就是从http头中去用户真实ip了,如果多个只取第一个。
文字解释的比较繁琐,大家直接阅读代码吧。
写在前面:
代码写的比较水,功能上是没啥问题,性能测试了也还行,就是不规范、不优雅。