OpenResty 深度解析:構建高性能 Web 服務的終極方案

引言

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.reqngx.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 的全生命周期管理與流量治理。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/83520.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/83520.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/83520.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

多商戶商城系統源碼解析:開發直播電商APP的技術底層實戰詳解

隨著直播電商的火爆,越來越多的創業者和企業都在尋求打造自己的多商戶商城系統,以實現“人、貨、場”三者的深度融合。然而,從一個簡單的電商平臺到一個功能完善的直播電商APP,其技術底層架構和實現過程并非一蹴而就。本文將從架構…

桌面端進程通信

以下是關于 Electron 桌面端進程通信的基本知識點總結: 一、Electron 進程模型基礎 1. 進程類型與職責 進程類型職責權限主進程(Main)創建窗口、系統級操作、IPC中樞完全Node.js訪問權限渲染進程(Renderer)展示Web內容、UI交互默認受限(可配置開啟Node.js)預加載腳本(Prelo…

openEuler24.03 LTS下安裝MySQL8.0.42

目錄 前提步驟 刪除原有mysql及maridb數據庫 安裝MySQL 啟動MySQL 啟動查看MySQL狀態 設置MySQL開機自啟動 查看登錄密碼 登錄MySQL 修改密碼及支持遠程連接 遠程連接MySQL 前提步驟 擁有openEuler24.03 LTS環境,可參考:Vmware下安裝openEule…

idea 保證舊版本配置的同時,如何從低版本升到高版本

文章目錄 前言idea 保證舊版本配置的同時,如何從低版本升到高版本1. 備份項目2. 下載最新的idea3. 安裝安裝包4. 導入idea2019舊配置5. 驗證前言 如果您覺得有用的話,記得給博主點個贊,評論,收藏一鍵三連啊,寫作不易啊^ _ ^。 ??而且聽說點贊的人每天的運氣都不會太差,…

填坑記: 古董項目Apache POI 依賴異常排除

當你看到NoSuchMethodError的時候,不要慌,深呼吸,這可能只是JAR包版本的問題… 引子:一個平靜的周二下午 那是一個看似平常的周二下午,系統運行良好,開發團隊在有條不紊地推進著新功能的開發。突然&#x…

CAPL Class: TcpSocket (此類用于實現 TCP 網絡通信 )

目錄 Class: TcpSocketacceptopenclosebindconnectgetLastSocketErrorgetLastSocketErrorAsStringlistenreceivesendsetSocketOptionshutdown函數調用的基本流程服務器端的基本流程客戶端的基本流程Class: TcpSocket學習筆記。來自CANoe幫助文檔。 Class: TcpSocket accept /…

微信小程序的開發及問題解決

HttpClient 測試例子 SpringBootTest public class HttpClientTest {/*** 測試通過httpclient發送get方式的請求*/Testpublic void testGET() throws IOException {//創建httpclient對象CloseableHttpClient httpClient HttpClients.createDefault();//創建請求對象HttpGet ht…

foreach中使用await的問題

目錄 1.說明 2.示例 3.解決方案 1.說明 在foreach中調用異步方法,即使使用了await,不會依次執行每個異步任務,也就是說Array.prototype.forEach不會等待 Promise 完成,即使你在回調函數中返回一個 Promise,forEach …

Linux調試生成核心存儲文件

1.核心存儲文件配置: 不知道理解對不對,Linux中的核心存儲文件的配置是在/proc/sys/kernel/core_pattern中的,使用 cat /proc/sys/kernel/core_pattern # 打印出 |/usr/share/apport/apport -p%p -s%s -c%c -d%d -P%P -u%u -g%g -- %E表示核…

Compose筆記(二十三)--多點觸控

這一節主要了解一下Compose中多點觸控,在Jetpack Compose 中,多點觸控處理需要結合Modifier和手勢API來實現,一般通過組合 pointerInput、TransformableState 和 TransformModifier 來創建支持縮放、旋轉和平移的組件。 一、 API 1. Pointer…

【Java ee初階】HTTP(4)

構造HTTP請求 1)開發中,前后端交互。瀏覽器運行的網頁中,構造出HTTP請求 2)調試階段,通過構造HTTP請求測試服務器 樸素的方案: 通過tcp socket 的方式構造HTTP請求 按照HTTP請求格式,往TCP…

STM32 __main

STM32開發中__main與用戶main()函數的本質區別及工作機制 在STM32開發中,__main和用戶定義的main()函數是啟動過程中的兩個關鍵節點,分別承擔運行時初始化和用戶程序入口的職責。以下是它們的核心差異及協作機制: 一、定義與層級差異 ?__ma…

什么是PMBus

一、PMBus的定義與背景 PMBus(Power Management Bus,電源管理總線) 是一種基于SMBus(System Management Bus)的開放標準數字通信協議,專為電源設備的監控、配置和控制設計。由PMBus聯盟(現并入…

Python OOP核心技巧:如何正確選擇實例方法、類方法和靜態方法

Python方法類型全解析:實例方法、類方法與靜態方法的使用場景 一、三種方法的基本區別二、訪問能力對比表三、何時使用實例方法使用實例方法的核心場景:具體應用場景:1. 操作實例屬性2. 對象間交互3. 實現特定實例的行為 四、何時使用類方法使…

業務中臺-典型技術棧選型(微服務、容器編排、分布式數據庫、消息隊列、服務監控、低代碼等)

在企業數字化中臺建設中,業務中臺是核心支撐平臺,旨在通過技術手段將企業核心業務能力抽象、標準化和復用,以快速響應前端業務需求。其核心技術流涉及從業務抽象到服務化、治理和持續優化的全流程。以下是業務中臺建設中的核心技術體系及關鍵…

期望是什么:(無數次的均值,結合概率)21/6=3.5

https://seeing-theory.brown.edu/basic-probability/cn.html 期望是什么:(無數次的均值,結合概率)21/6=3.5 一、期望(數學概念) 在概率論和統計學中,**期望(Expectation)**是一個核心概念,用于描述隨機變量的長期平均取值,反映隨機變量取值的集中趨勢。 (一…

matlab官方免費下載安裝超詳細教程2025最新matlab安裝教程(MATLAB R2024b)

文章目錄 準備工作MATLAB R2024b 安裝包獲取詳細安裝步驟1. 文件準備2. 啟動安裝程序3. 配置安裝選項4. 選擇許可證文件5. 設置安裝位置6. 選擇組件7. 開始安裝8. 完成輔助設置 常見問題解決啟動失敗問題 結語 準備工作 本教程將幫助你快速掌握MATLAB R2024b的安裝技巧&#x…

第3章 自動化測試:從單元測試到硬件在環(HIL)

在前兩章中,我們已完成從環境搭建到流水線編譯的自動化配置。為了真正保障軟件質量、降低回歸風險,本章將聚焦測試自動化,涵蓋從最基礎的單元測試,到集成測試,再到硬件在環(Hardware-in-the-Loop, HIL)測試的全流程。通過腳本驅動、測試報告可視化和與 CI 平臺深度集成,…

信息收集+初步漏洞打點

目標:理解信息收集在滲透測試中的意義,熟悉常用工具用法,完成基本打點測試 一.理論學習: 模塊內容說明信息收集分類主動信息收集 vs 被動信息收集目標發現子域名、IP、端口、子站點、目錄、接口技術指紋識別Web框架(如…

uniapp+vue3開發項目之引入vuex狀態管理工具

前言: 我們在vue2的時候常用的狀態管理工具就是vuex,vue3開發以后,又多了一個pinia的選項,相對更輕便,但是vuex也用的非常多的,這里簡單說下在uni-app中vuex的使用。 實現步驟: 1、安裝&#x…