使用lua擴充套件nginx的功能

2021-07-25 02:23:27 字數 4194 閱讀 5053

通常,nginx是作為乙個負載均衡伺服器或者web伺服器使用,但是存在這樣一種情況:當太多的無效請求通過nginx分發到上游的伺服器的時候,雖然上游的伺服器集群進行了水平擴充套件,但是當nginx僅僅做乙個請求分發的動作的時候,太多的無效請求直接到達上游伺服器,這樣顯然不太好。所以需要擴充套件一下nginx的功能,讓他執行一些預定義的規則,攔截掉無效的請求。比如最簡單的攔截ip黑名單,最簡單的就是在nginx配置檔案中寫入黑名單ip,但是顯然這樣是很費力的,而且每次加入黑名單後需要nginx -reload才能生效,這麼做顯然很糟糕。其實可以通過在nginx中嵌入lua指令碼來方便的對nginx的功能進行擴充套件,並且這樣也能比較方便的跟業務系統銜接起來,幾乎實時生效,不用每次nginx -reload,這裡的黑名單生效邏輯如下:

業務系統產生ip黑名單 --> 放入redis --> lua從redis中將黑名單同步到nginx的共享記憶體 --> request到達nginx的時候,執行lua**,檢查訪問者的ip是否存在於nginx的共享記憶體,如果訪問者ip在黑名單,就直接在nginx處攔截,不讓請求到達上游伺服器

建議直接使用openresty,它編譯的時候已經包含了nginx和lua相關的模組

以下是部分lua邏輯的**:

-- 更新黑名單處理

local function denyhandler(conn)

-- 首先檢查黑名單是否有更新

local ok,err = conn:get(rc.update_deny_check_key)

if not ok then

ngx.log(ngx.err,err)

elseif ok==null or ok==0 then

-- 說明沒有黑名單

return true

else

-- 比較黑名單更新tag 與本地快取tag是否相同

-- 如果不同,則進行快取的更新

local share = ngx.shared.rshare

local check = share:get(rc.update_deny_check_key)

if check == ok then

-- 沒有更新,成功返回

return true

else

-- 更新快取

local cache,err = conn:smembers(rc.update_deny_cache_key)

if err then

ngx.log(ngx.err,err)

return false

else

local denies = ngx.shared.denies

-- 將舊有的全部置為過期

denies:flush_all()

if cache ~= null then

for __,deny in ipairs(cache) do

denies:set(deny,true)

endend

-- 設定新的黑名單更新tag

share:set(rc.update_deny_check_key, ok)

return true

endend

endend

-- 更新系統黑名單

local updatedenycache

updatedenycache = function(premature)

if premature then

return

end-- 系統黑名單放在共享記憶體中,進行互斥更新即可

local sync = sync.new()

sync.mutex(true,rc.dict_share_name,

rc.update_deny_last_key,rc.update_deny_delay,

denyhandler,updatedenycache,"updatedenycache")

end-- 通用的執行遞迴操作的函式

function _m.mutex(need_redis,dict_name,upd_time_key,delay_time,handler,schedule,err_log,...)

-- 鎖定 保證執行的唯一性

local share = shared[dict_name]

local lock = locks:new(rc.dict_locks_name, rc.lock_opt)

local red,conn

if need_redis then

red = redis:new()

-- 連線及失敗處理

conn = red:getcoonect()

if not conn then

return

endend

local elapsed, err = lock:lock(upd_time_key)

ngx.log(ngx.err,"upd_time_key:"..upd_time_key..",elaspsed:"..elapsed..",err:")

if elapsed ~= nil then

ngx.log(ngx.err,"lock acquired successfully !")

-- 獲取現在距離上次執行的時間差

local sub = ngx.now() - shared:get(upd_time_key)

-- 增加調節係數,避免細微的時間差異的問題

-- 這個時間的增加並不會導致對不同任務執行已否造成錯判

-- 因為這個時間與 delay time 差距是巨大的

sub = sub + rc.delay_adjust_time

-- 當時間大於等於 delay 時,說明本次還沒執行 可以繼續

-- 執行成功後會更新執行時間,這樣當下乙個任務獲得鎖之後

-- 瞬時內是不可能獲得超過delay time的時間差值的

-- 最壞的情況 這裡執行多次也是可以接受的,雖然這不太可能出現

ngx.log(ngx.err,"sub:"..sub..",delay_time:"..delay_time)

if sub > delay_time then

ngx.log(ngx.err,"i am entring")

if handler(conn,...) then

ngx.log(ngx.err,"executed success !")

share:set(upd_time_key,ngx.now())

endend

-- 解鎖

local ok,err = lock:unlock()

if not ok then

ngx.log(ngx.err,err)

endelse

ngx.log(ngx.err,err)

end-- redis例項用完後,放回redis的連線池

if need_redis then

red:closetopool()

end-- 存在繼續執行的計畫任務,則再次設定

if schedule then

-- 再次設定timer 為避免不同任務的差異 需要根據共享值設定

-- 即使細小的差異 長久以後可能變成多個任務執行在不同的時間段

-- 導致根據時間差值判斷誰先執行過 變得不準確

local lastutime = share:get(upd_time_key)

-- 要執行的時間 = 計畫時間 - (現在 - 上次執行任務的時間)

local delay = delay_time - (ngx.now() - lastutime)

ngx.log(ngx.err,"delay_time:"..delay_time..",lastutime:"..lastutime..",ngx.now - lastutime:"..(ngx.now() - lastutime))

ngx.log(ngx.err,"unknown value:")

-- 避免極端情況 同時不賦值為0 避免錯誤持續不斷的執行下去

if delay < 0 then

delay = 1

endlocal ok,err = ngx.timer.at(delay,schedule)

if not ok then

ngx.log(ngx.err, "failed to create the "..err_tag..": ", err)

return

endend

end

Nginx新增Lua擴充套件模組

nginx安裝目錄 with http ssl module 支援 ssl with http stub status module nginx狀態模組 add module usr local src ngx devel kit 0.3.0 lua模組 add module usr local s...

nginx功能擴充套件整理

0 基本負載均衡配置 更新 etc nginx conf.d default.conf,配置反向 location 重新啟動nginx sudo service nginx restart1 子請求 當乙個請求發起乙個 子請求 的時候,依照 nginx 的術語,習慣把前者稱為後者的 父請求 pare...

Redis擴充套件功能之Lua指令碼

指令碼對於unix linux系統的使用者是再熟悉不過了。lua也是一種輕量小巧的指令碼語言,用標準c語言編寫並以源 形式開放,其設計目的是為了嵌入應用 程式中,從而為應用程式提供靈活的擴充套件和定製功能。lua應用場景 遊戲開發 獨立應用指令碼 web應用指令碼 擴充套件和資料庫外掛程式 ngin...