开源API⽹关Orange代码分析
⾸先,了解⼀下Orange,Orange 是⼀个基于 OpenResty 的API⽹关。除 Nginx 的基本功能外,它还可⽤于API监控、访问控制(鉴权、WAF)、流量筛选、访问限速、AB测试、静/动态分流 等。
说句实在的,它已经实现了绝⼤部分的功能,只不过⽬前已经处于停滞状态了
项⽬⽬录结构
api
⽬测是提供的接⼝,官⽅⽂档中提到了该项⽬提供了API接⼝⽤于实现第三⽅服务
bin
应该是运⾏⽬录,⾥⾯主要是lua的⼀些第三⽅包
conf
配置模板,类⽐nginx中的配置
dashboard
控制台程序
docs
运⾏⽂档
install
安装⽅法,包含了sql语句和安装执⾏脚本
orange
也是⼀些Lua的代码,主要是核⼼代码所在地
rockspec
似乎是对该项⽬的打包,类似于java的jar⼀样,相当于给别⼈直接使⽤
test
测试库
代码分析
启动Orange
初始化
先贴代码再分析
-- 执⾏过程:
-- 加载配置
-- 实例化存储store
-- 加载插件
-- 插件排序
function Orange.init(options)
options = options or{}
local store, config
local status, err =pcall(function()
local conf_file_path = fig
config = config_loader.load(conf_file_path)
store =require("sql_store")(config.store_mysql)nginx和网关怎么配合使用
loaded_plugins =load_node_plugins(config, store)
ngx.update_time()
end)
if not status or err then
ngx.log(ngx.ERR,"Startup error: ".. err)
end
local consul =require("sul_balancer")
consul.set_shared_dict_name("consul_upstream","consul_upstream_watch")
Orange.data ={
store = store,
config = config,
consul = consul
}
-- init dns_client
assert(dns_client.init())
return config, store
end
不得不感叹,lua⾥⾯的匿名函数⽤得可针对,将⽅法直接作为函数的参数的⽅式,对编译器的词法分析器和语法分析器带来的挑战吧,有机会⼀定要拜读⼀下Lua的编译器。
⾸先,读取mysql的配置,然后加载配置的所有插件。
其余的内容,就是定义的插件的基本⽅法的调⽤
插件定义的关键代码
对于插件,⼀共有下⾯的⼏种⽅法
redirect()
rewrite()
access()
balance()
header_filter()
body_filter()
log()
也就是重定向,重写,接收(正常处理),平衡,请求响应头处理,内容处理以及⽇志处理。
rewrite
redirect⽅法,默认的实现为:
function BasePlugin:rewrite()
ngx.log(ngx.DEBUG," executing plugin \"", self._name,"\": rewrite")
end
只是⽤ngx.log打印出了⼀个⽇志。
我们随便拎⼀个实现了该⽅法的插件⼦类来看看,这⾥以headers插件为例:
function HeaderHandler:rewrite(conf)
write(self)
local enable = ("able")
local meta = _json("a")
local selectors = _json("headers.selectors")
local ordered_selectors = meta and meta.selectors
if not enable or enable ~=true or not meta or not ordered_selectors or not selectors then
return
end
for i, sid in ipairs(ordered_selectors)do
local selector = selectors[sid]
ngx.log(ngx.INFO,"==[Headers][START SELECTOR:", sid,"][NAME:",selector.name,']')
if selector able ==true then
local selector_pass
pe ==0then-- 全流量选择器
selector_pass =true
else
selector_pass = judge_util.judge_selector(selector,"headers")-- selector judge
end
if selector_pass then
if selector.handle and selector.handle.log ==true then
ngx.log(ngx.INFO,"[Headers][PASS-SELECTOR:", sid,"]")
end
local stop =filter_rules(sid,"headers")
if stop then-- 不再执⾏此插件其他逻辑
return
end
else
if selector.handle and selector.handle.log ==true then
ngx.log(ngx.INFO,"[Headers][NOT-PASS-SELECTOR:", sid,"] ")
end
end
-- if continue or break the loop
if selector.handle and inue ==true then
-- continue next selector
else
break
end
end
end
end
通过看源代码,可以发现,这⾥的⼀个个插件,优先像Java⾥⾯的过滤器或者PHP⾥⾯的中间件,利⽤装饰者模式,在外⾯不断套壳。然后这⾥调⽤了处理逻辑函数filter_rules
local function filter_rules(sid, plugin)
local rules = _json(plugin ..".selector.".. sid ..".rules")
if not rules or type(rules)~="table"or#rules <=0then
return false
end
for i, rule in ipairs(rules)do
able ==true then
-- judge阶段
local pass = judge_util.judge_rule(rule,"headers")
-- handle阶段
if pass then
-- extract阶段
headers_util:set_headers(rule)
end
end
end
return false
end
如果满⾜条件,就会写⼊headers,
接着让我们回到Orange的主代码中看看rewrite⽅法是如何在上下⽂中被使⽤的。
write()
for _, plugin in ipairs(loaded_plugins)do
plugin.handler:rewrite()
end
local now_time =now()
end
令⼈吃惊的是,主代码就是循环调⽤每个插件的rewrite⽅法。这就会带来⼀个问题,两个插件如果作⽤于同⼀个属性,那么后执⾏的插件就会覆盖先执⾏的插件的结果。
redirect
默认实现为:
function BasePlugin:redirect()
ngx.log(ngx.DEBUG," executing plugin \"", self._name,"\": redirect")
end
⼀个例⼦分析⼀下,以RedirectHandler为例
function RedirectHandler:redirect()
direct(self)
local enable = ("able")
local meta = _json("a")
local selectors = _json("redirect.selectors")
local ordered_selectors = meta and meta.selectors
if not enable or enable ~=true or not meta or not ordered_selectors or not selectors then
return
end
local ngx_var = ngx.var
local ngx_var_uri = ngx_var.uri
local ngx_var_host = ngx_var.http_host
local ngx_var_scheme = ngx_var.scheme
local ngx_var_args = ngx_var.args
for i, sid in ipairs(ordered_selectors)do
ngx.log(ngx.INFO,"==[Redirect][PASS THROUGH SELECTOR:", sid,"]")
local selector = selectors[sid]
if selector able ==true then
local selector_pass
pe ==0then-- 全流量选择器
selector_pass =true
else
selector_pass = judge_util.judge_selector(selector,"redirect")-- selector judge
end
if selector_pass then
if selector.handle and selector.handle.log ==true then
ngx.log(ngx.INFO,"[Redirect][PASS-SELECTOR:", sid,"] ", ngx_var_uri)
end
local stop =filter_rules(sid,"redirect", ngx_var_uri, ngx_var_host, ngx_var_scheme, ngx_var_args) local selector_continue = selector.handle and inue
if stop or not selector_continue then-- 不再执⾏此插件其他逻辑
return
end
else
if selector.handle and selector.handle.log ==true then
ngx.log(ngx.INFO,"[Redirect][NOT-PASS-SELECTOR:", sid,"] ", ngx_var_uri)
end
end
end
end
end
可以发现,代码结构与rewrite⾮常相似。在来看看它的处理函数
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论