動態外掛
之前已經拆解細點逐個介紹了 tcp 、http 代理相關核心點,現在介紹一個讓 api gateway 變得很靈活的功能實現: 動態外掛。
由於 lua 的動態語言特點,我們可以比較方便做到動態外掛機制。
首先我們來了解這一切的基石:lua 模組載入機制。
lua 模組載入機制
一個模組是什麼樣?
例如: xxxmodule.lua 檔案內容
local module = {} -- 注意不要使用全域性變數,會造成變數汙染,導致無法解除安裝模組
-- 定義一個函式
function module.func1()
io.write("這是一個公有函式!\n")
end
return module
如何載入模組?
Lua提供了一個名為require的函式用來載入模組。要載入一個模組,只需要簡單地呼叫就可以了。例如:
local a = require("xxxmodule")
a.func1() -- "這是一個公有函式!\n"
到底是怎麼工作的呢?
require
函式會在模組path列表搜尋模組,openresty可以指定如下兩種:
lua 庫: lua_package_path "./?.lua;/usr/local/openresty/luajit/share/luajit-2.1.0-beta3/?.lua;";
c 庫: lua_package_cpath "./?.so;/usr/local/lib/lua/5.1/?.so;";
找到模組檔案之後,就會解析執行整個檔案的內容(類似函式 loadstring),由於最後是return 模組變數,我們就可以使用這個變數的函式等等一切了
如果開啟了 lua_code_cache on, require 函式會將第二步拿到的變數存在 package.loaded 這個table 中,達到快取效果
那麼如何解除安裝呢?
非常簡單,只需一句:
package.loaded['xxxmodule'] = nil
所以基於lua的模組管理,我們就可以非常容易實現外掛模組的管理
lua severless function simple demo
所以我們可以基於這樣的動態機制,實現 lua severless function 或者動態外掛機制,示例如下:
http {
default_type application/json;
lua_code_cache on;
lua_package_path "$prefix/deps/share/lua/5.1/?.lua;$prefix/deps/share/lua/5.1/?/init.lua;$prefix/src/?.lua;$prefix/src/?/init.lua;;./?.lua;/usr/local/openresty/luajit/share/luajit-2.1.0-beta3/?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua;/usr/local/openresty/luajit/share/lua/5.1/?.lua;/usr/local/openresty/luajit/share/lua/5.1/?/init.lua;";
lua_package_cpath "$prefix/deps/lib64/lua/5.1/?.so;$prefix/deps/lib/lua/5.1/?.so;;./?.so;/usr/local/lib/lua/5.1/?.so;/usr/local/openresty/luajit/lib/lua/5.1/?.so;/usr/local/lib/lua/5.1/loadall.so;";
# 簡單模擬模組
init_by_lua_block {
MockPackages = {}
}
server {
listen 8222;
server_name localhost;
location /add {
# 比如替換為 request body 去做模組建立,這裡為了簡單就用寫死的程式碼來模擬
# 內容為透過 loadstring 轉換 lua code 字串為函式
# 並將函式結果 當前時間存在全域性變數中
access_by_lua_block {
local lua_src = [[
ngx.update_time()
return tostring(ngx.now())
]]
local f, e = loadstring(lua_src, "module xxxmodule")
MockPackages['xxxmodule'] = f()
ngx.say('add function success')
}
}
location /run {
# 這裡獲取快取結果並輸出出來
access_by_lua_block {
if MockPackages['xxxmodule'] then
ngx.say(MockPackages['xxxmodule'])
else
ngx.say('no function')
end
}
}
}
}
啟動並測試
mkdir -p logs && /usr/bin/openresty -p ./ -c nginx.conf -g 'daemon off;'
call http://127.0.0.1:8222/run return no function
call http://127.0.0.1:8222/add return add function success
call http://127.0.0.1:8222/run return 1624022896.703
call http://127.0.0.1:8222/add return add function success
call http://127.0.0.1:8222/run return 1624022918.674
可以看到值已經被改變了
這種severless function demo的問題
-
管理以及定位問題
實際環境會有很多機器例項,對應的severless function 在哪幾臺機器哪幾個nginx中的哪些worker 程式上載入,載入多久, 需要完整規劃方案
-
資源隔離
所有的severless function 其實都是在worker內, 所以記憶體cpu等於是共享,一個特耗效能的程式碼必然影響其他
-
安全問題
由於多個函式會同在一個worker 程式,無論效能和資源都會收到相互影響 別人可以在其中輕鬆加入惡意程式碼
所以如果在公用的api gateway中,大家還是不要把它當成雲端計算中的 severless function 使用,只是當成一個 動態filter function 就好。