引言
openresty是什么?在我個人對它的理解來看相當于嵌入了lua的nginx;
我們在nginx中嵌入lua是為了不需要再重新編譯,我們只需要重新修改lua腳本,隨后重啟即可;
一.lua指令序列
?
?我們分別從初始化階段,重寫/訪問階段,內容階段,日志階段來介紹上述圖中的信息
1.初始化階段
?init_by_lua*:Nginx 啟動時,在 master 進程中僅執行一次,用于全局初始化,比如加載配置文件、初始化共享字典等。
init_worker_by_lua* :Nginx 啟動時,每個 worker 進程初始化時執行,可用于 worker 進程級別的初始化,像設置特定于 worker 的變量等 。
2.重寫/訪問階段
ssl_certificate_by_lua* :當請求是安全請求(涉及 SSL/TLS)時執行,可用于動態選擇 SSL 證書等操作。
set_by_lua* :用于設置 Nginx 變量,可在請求處理早期計算變量值。
rewrite_by_lua* :用于 URL 重寫相關操作,可修改請求的 URI 等。
access_by_lua* :用于訪問控制,比如認證、鑒權、IP 黑白名單判斷等,決定請求是否能繼續處理。
3.內容階段
balancer_by_lua* :在負載均衡階段執行,可自定義負載均衡算法,選擇合適的上游服務器。
content_by_lua* :用于生成響應內容,是核心的內容生成階段,可查詢數據庫、組裝數據并輸出響應。
header_filter_by_lua* :用于過濾和修改響應頭,比如添加自定義響應頭、修改緩存相關頭信息等。
body_filter_by_lua* :用于過濾和修改響應體,可對響應內容進行二次處理,如壓縮、加密等。
4.日志階段?
log_by_lua* :請求處理完成后,在記錄日志時執行,可自定義日志記錄邏輯,比如將日志發送到特定存儲或進行格式化處理。
5.conf代碼案例
我們可以直接在conf文件里面寫lua代碼塊從而實現一些功能
worker_processes 8;events {worker_connections 10240;
}http {error_log ./logs/error.log info;server {listen 8989;location / {rewrite_by_lua_block {local args = ngx.req.get_uri_args()if args["jump"] == "1" thenreturn ngx.redirect("http://baidu.com")elseif args["jump"] == "2" thenreturn ngx.redirect("/jump_other")end}content_by_lua_block {ngx.say("hello","\t",ngx.var.remote_addr)}}location /jump_other {content_by_lua_block{ngx.say("jump other","\t",ngx.var.remote_addr)}body_filter_by_lua_block{local chunk = ngx.arg[1]ngx.arg[1]=string.gsub(chunk,"other","lion")}log_by_lua_block{local request_method = ngx.var.request_methodlocal request_uri = ngx.var.request_urilocal status = ngx.var.statuslocal response_time = ngx.var.request_timelocal msg = string.fomat("[%s] %s -Status:%d,response time = %.2fms",os.data("%Y-%m-%d %H:%M:%S"),request_uri,status,response_time)ngx.log(ngx.INFO,msg)}}}
}
上述conf代碼塊起了8個工作線程 每個工作線程的最大連接數為10240
我們sever的監聽端口為8989,當我們頁面路由到location /時候會調用lua嵌入的rewrite_by_lua_block(重寫url)接口;
當jump=1時會直接重定向到百度
當jump=2時會重定向到location /jumpother
當我們跳轉到location /jump_other時 我們會調用content_by_lua_block(生成內容輸出http響應)接口 后面執行到body_filter_by_lua_block(對響應內容進行二次處理)接口
后面執行到日志輸出接口?log_by_lua_block(打印對應的日志信息)
執行結果展示
默認輸出 不輸入jump
輸入jump=1 重定向到百度
?
?輸入jump=2 重定向到jump_other
?
二.openresty 中 嵌入原理和 責任鏈模式
1.OpenResty 嵌入原理
OpenResty 本質是將 LuaJIT 虛擬機嵌入到 Nginx 的管理進程(master 進程)和工作進程(worker 進程)中 。具體表現為:
進程內虛擬機共享:每個 worker 進程使用一個 LuaVM ,同一個進程內的所有協程共享該虛擬機。當請求分配到 worker 進程時,會在 LuaVM 中創建一個 coroutine 協程來處理請求。比如,在處理 HTTP 請求時,不同請求的協程都在所屬 worker 進程的 LuaVM 里運行 Lua 代碼。
與 Nginx 事件模型結合:ngx_lua 模塊使 Lua 內建協程能和 Nginx 的事件驅動模型深度協作。Lua 代碼中的 IO 操作委托給 Nginx 事件模型,實現非阻塞調用。像網絡請求等 IO 操作時,Lua 協程掛起,Nginx 事件處理機制接管,操作完成后恢復協程上下文繼續執行,對開發者透明。
模塊與 API 注入:Nginx 的 I/O 原語等功能經封裝后注入 Lua 虛擬機,讓 Lua 代碼能直接訪問。例如,通過ngx.req
、ngx.resp
等 API 可在 Lua 代碼里方便操作 Nginx 的請求和響應相關功能 。
2.OpenResty 責任鏈模式
在 OpenResty 中,責任鏈模式是一種用于請求處理的設計模式 :
特點:解耦合和中斷
節點構建:把請求處理邏輯拆分成一個個獨立節點,每個節點完成單一功能,如鑒權、限流、日志記錄等。例如在 API 網關場景,鑒權節點驗證用戶身份合法性,限流節點控制請求頻率。
鏈式調用:節點按特定順序組成責任鏈,請求到達后依次流經各節點。前一節點處理完決定是否將請求傳遞給下一節點。比如鑒權通過才將請求傳給限流節點,否則直接返回錯誤響應。
動態調整:可根據業務需求靈活添加、刪除或調整節點順序。像某些高安全要求業務,在責任鏈中增加更嚴格加密和多重認證節點;普通業務則簡化節點鏈。
三.cosocket
cosocket指的是協程和socket的結合實現特定的功能
?四.openresty總結
1.我們使用openresty的三板斧
①背靠nginx,嵌入到各個階段的lua函數
②cosocket可同步非阻塞在多個階段訪問第三方庫服務,在nginx上實現業務成為可能
③ngx,shared.dict共享內存可在多個worker進程共享數據,數據實時生效
2.conf和lua代碼案例
conf文件
worker_processes 8;events {worker_connections 10240;
}http {error_log ./logs/black.log info;lua_shared_dict blks 1m;init_worker_by_lua_file ./app/init_worker.lua;server {listen 8989;location / {access_by_lua_block {local black_list= {["192.168.217.2"]=true}if black_list[ngx.var.remote_addr] thenreturn ngx.exit(404)end}content_by_lua_block {ngx.say("hello","\t",ngx.var.remote_addr)}}location /black_v1 {access_by_lua_file ./app/black_v1.lua;content_by_lua_block {ngx.say("black_v1","\t",ngx.var.remote_addr)}}location /black_v2 {access_by_lua_file ./app/black_v2.lua;content_by_lua_block {ngx.say("black_v2","\t",ngx.var.remote_addr)}}}
}
我們在worker線程初始化的時候通過lua_shared_dict接口實現一塊1m的共享內存 后我們調用init_worker_by_lua_file接口? ?進入到 init_worker.lua 文件中
我們通過conf代碼塊中的
? ? ? ? location /black_v2 {
? ? ? ? ? ? access_by_lua_file ./app/black_v2.lua;
? ? ? ? ? ? content_by_lua_block {
? ? ? ? ? ? ? ? ngx.say("black_v2","\t",ngx.var.remote_addr)
? ? ? ? ? ? }
? ? ? ? }
進行詳解
init_worker.lua文件
--只在第一個worker進程里面初始化
if ngx.worker.id() ~= 0 thenreturn
endlocal redis = require("resty.redis")
local bklist = ngx.shared.blkslocal function updata_blacklist()local red = redis:new()local ok, err = red:connect("127.0.0.1", 6379)if not ok thenreturnendlocal black_list, err = red:smembers("black_list")bklist:flush_all()for _, value in pairs(black_list) dobklist:set(value, true)endngx.timer.at(5, updata_blacklist)
endngx.timer.at(5, updata_blacklist) --每5s把redis黑名單里面的內容更新到共享內存里
?我們在上述lua代碼中實現了 對redis的連接以及實時把redis里面黑名單的內容更新到共享內存塊中
black_v2.lua 文件
?
local bklist = ngx.shared.blkslocal ip = ngx.var.remote_addrif bklist:get(ip) thenreturn ngx.exit(404)
end
當我們路由到? ? location /black_v2時候 通過access_by_lua_file?接口 會執行black_v2.lua文件
從而拿到共享內存中的黑名單ip地址從而進行判斷
實現黑白名單結果演示
啟動openresty
啟動redis
?我們在balck_list中添加兩個ip地址
http請求訪問
把本地ip地址從 black_list中刪除
?五.kong在openresty進一層的封裝
1.概念和接口
kong和konga-->web服務在開發中經常使用的?
我們只介紹其中的反向代理來演示其功能
kong在反向代理的兩個接口分別是
proxy_pass;
proxy_protocol on;? ?
2.conf和lua代碼案例?
conf文件
worker_processes 2;events {worker_connections 1024;
}# #http
##動態
http {error_log ./logs/proxy.log info;lua_shared_dict shm 1m;lua_shared_dict urls 1m;##########################################################
}#靜態---->問題--->添加刪除需要重啟生效
http {error_log ./logs/proxy.log info;upstream ups {server 192.168.217.148:7001;server 192.168.217.148:7002;server 192.168.217.148:7003;}server {listen 9001;location / {proxy_pass http://ups;proxy_protocol on;}}
}#tcp
stream {upstream tcp_ups {server 192.168.217.148:8080;}server {listen 9002;proxy_pass tcp_ups;proxy_protocol on;}server {listen 9003;content_by_lua_file ./app/proxy.lua;}
}
proxy.lua
local sock, err = ngx.req.socket()
if not sock thenreturn
endlocal upsock = ngx.socket.tcp()local ok, err = upsock:connect("192.168.217.148", 8080)
if not ok thenreturn
endupsock:send(ngx.var.remote_addr .. "\n")
-- local function handler_upstream()-- end
六.kong總結
Kong 是一款基于 NGINX 和 OpenResty 構建的開源 API 網關,支持 API 管理、流量控制、身份驗證、監控等功能,可實現對 API 的全生命周期管理與流量治理。