在官網上對 OpenResty 是這樣介紹的(http://openresty.org):
“OpenResty 是一個基于 Nginx 與 Lua 的高性能 Web 平臺,其內部集成了大量精良的 Lua 庫、第三方模塊以及大多數的依賴項。用于方便地搭建能夠處理超高并發、擴展性極高的動態 Web 應用、Web 服務和動態網關。”
“OpenResty 通過匯聚各種設計精良的 Nginx 模塊(主要由 OpenResty 團隊自主開發),從而將 Nginx 有效地變成一個強大的通用 Web 應用平臺。這樣,Web 開發人員和系統工程師可以使用 Lua 腳本語言調動 Nginx 支持的各種 C 以及 Lua 模塊,快速構造出足以勝任 10K 乃至 1000K 以上單機并發連接的高性能 Web 應用系統。”
“OpenResty 的目標是讓你的 Web 服務直接跑在 Nginx 服務內部,充分利用 Nginx 的非阻塞 I/O 模型,不僅僅對 HTTP 客戶端請求,甚至于對遠程后端諸如 MySQL、PostgreSQL、Memcached 以及 Redis 等都進行一致的高性能響應。”
從以上官網描述里我們可以知道,OpenResty 官網對其定位是以 Nginx 為核心集成 Lua,打造一個兼具開發效率和高性能的服務端開發平臺。
OpenResty 的核心是基于 Nginx 的一個 C 模塊(lua-Nginx-module),該模塊將 LuaJIT 嵌入到 Nginx 服務器中,并對外提供一套完整的 Lua API,透明地支持非阻塞 I/O,提供了輕量級線程、定時器等高級抽象。
我們可以用 Lua 語言來進行字符串和數值運算、查詢數據庫、發送 HTTP 請求、執行定時任務、調用外部命令等,還可以用 FFI 的方式調用外部 C 函數。這基本上可以滿足服務端開發需要的所有功能。
掌握好了 OpenResty,我們就可以同時擁有腳本語言的開發效率和迭代速度,以及 Nginx C 模塊的高并發和高性能優勢。
下面為大家介紹本文大綱:
OpenResty 的安裝有多種方法,比如使用操作系統的包管理器、源碼編譯或者 docker 鏡像。推薦優先使用 yum、apt-get、brew 這類包管理系統,來安裝 OpenResty。
對于 Mac OS X 或 macOS 用戶,強烈推薦您使用 homebrew 包管理工具安裝 OpenResty。可以直接使用下面 這一條命令:
brew install openresty/brew/openresty
對于一些常見的 Linux 發行版本(Ubuntu、Debian、CentOS、RHEL、Fedora、OpenSUSE、Alpine 和 Amazon Linux), OpenResty 提供 官方預編譯包。確保首先用這種方式來安裝。這里用 CentOS 舉例,可以使用如下方式,
# add the yum repo:wget https://openresty.org/package/centos/openresty2.reposudo mv openresty2.repo /etc/yum.repos.d/openresty.repo# update the yum index:sudo yum check-update
# add the yum repo:wget https://openresty.org/package/centos/openresty.reposudo mv openresty.repo /etc/yum.repos.d/openresty.repo# update the yum index:sudo yum check-update
然后就可以像下面這樣安裝軟件包,比如 openresty:
sudo yum install -y openresty
Docker 安裝的方式就最為簡單了,只需要輸入以下命令,就可以獲取打包好的鏡像。
docker pull openresty/openresty
安裝 OpenResty 成功后的目錄結構如下(以默認安裝目錄為例):
/usr/local/openresty/ #安裝主目錄├── bin #存放可執行文件├── luajit #LuaJIT運行庫├── lualib #Lua組件├── Nginx #Nginx核心運行平臺├── pod #參考手冊(restydoc)使用的數據└── site #包管理工具(opm)使用的數據
yum 安裝完后,就可以直接運行 openresty 命令,啟動 OpenResty 服務。
/usr/local/openresty/bin/openresty #啟動OpenResty服務
OpenResty 默認開啟了 localhost:80 服務,使用 wget 或者 curl 這樣的工具就可以驗證 OpenResty 是否正常工作:
curl http://localhost:80 #curl命令發送HTTP請求
下面是一些其他常用命令,
/usr/local/openresty/bin/openresty -s stop #停止 OpenResty 服務/usr/local/openresty/bin/openresty -s reload #重新加載 Nginx 配置文件/usr/local/openresty/bin/openresty -t #檢查 Nginx 配置文件是否正確/usr/local/openresty/bin/openresty -c #指定配置文件啟動
OpenResty 的操作命令跟 Nginx 保持一致。可以執行 openresty -h 以及 nginx -h 對比看出,
圖片
如果你想安裝命令行工具 resty,那么可以像下面這樣安裝 openresty-resty 包:
sudo yum install -y openresty-resty
resty 是一個 cli 工具,可以使用 -e 參數可以在命令行里直接執行 Lua 代碼,我們可以在命令行執行如下命令,
[root@VM-4-5-centos ~]# resty -e "print('hello world')"hello OpenResty
resty 工具還有很多選項用于配置行為,非常靈活,-e 之外較常用的有
-c :指定最大并發連接數(默認值是64);-I :指定Lua庫的搜索路徑;-l :指定加載某個Lua庫;--http-conf :定制在http域里的指令;--main-include :定制在main域里的指令;--shdict :定制使用的共享內存(參見10.2節);--resolve-ipv6 :允許解析ipv6的地址。
想了解完整的列表,可以查看 resty -h 命令。
包管理工具 opm
跟大多數語言一樣有包管理工具一樣,OpenResty 也有自己的包管理工具 opm(OpenResty Package Manager),opm 在 openresty-opm 包里,安裝命令如下,
sudo yum install -y openresty-opm
opm 是 OpenResty 自帶的包管理器,在你安裝好 OpenResty 之后,就可以直接使用。一些常見用法如下,
opm search http #搜索關鍵字httpopm search kafka #搜索關鍵字kafkaopm get agentzh/lua-resty-http #安裝組件,注意需要sudoopm info agentzh/lua-resty-http #顯示組件的版本、作者等信息opm remove agentzh/lua-resty-http #移除組件,同樣需要sudoopm --install-dir=/opt get xxx #把組件安裝到/opt目錄下opm --cwd get xxx #安裝到當前目錄的/resty_modules下
在上文中我們使用命令行工具 resty 寫了一個比較簡單的 OpenResty 程序,沒有 master 進程,也不會監聽端口。下面讓我們寫一個需要啟動 OpenResty 服務的 hello world。
首先找到 OpenResty 安裝目錄下 nginx/conf/nginx.conf 文件,在 server 下新增 OpenResty 的 content_by_lua 指令,里面嵌入了 ngx.say 的代碼:
server { listen 88; server_name localhost; location / { root html; index index.html index.htm; } location /hello { content_by_lua ' ngx.say("hello, world") '; }}
接著我們執行 openresty -s reload 命令,重新加載 nginx.conf 配置文件。沒有報錯的話,OpenResty 的服務就已經成功啟動了。
最后使用 curl 命令,來查看結果的返回:
[root@VM-4-5-centos conf]# curl localhost:88/hellohello, world
到這里,一個真正的 OpenResty 開發的 hello world 程序就完成了。
我們不用專門去安裝標準 Lua 5.1 之類的環境,因為 OpenResty 已經不再支持標準 Lua,而只支持 LuaJIT。這里我介紹的 Lua 語法,也是和 LuaJIT 兼容的部分,而不是基于最新的 Lua 5.3,這一點需要特別注意。
在 OpenResty 的安裝目錄下,可以找到 LuaJIT 的目錄和可執行文件。在 CentOS 系統下,LuaJIT 的目錄如下,
[root@VM-4-5-centos luajit]# cd /usr/local/openresty/luajit/bin/[root@VM-4-5-centos bin]# lltotal 536lrwxrwxrwx 1 root root 18 Oct 12 11:22 luajit -> luajit-2.1.0-beta3-rwxr-xr-x 1 root root 547728 Jul 18 12:38 luajit-2.1.0-beta3
我們可以執行 cp luajit /usr/local/bin/ 將 luajit 文件復制到 /usr/local/bin/ 目錄下,進而可以直接使用 luajit 命令。
查看 LuaJIT 的版本號,
[root@VM-4-5-centos ~]# luajit -vLuaJIT 2.1.0-beta3 -- Copyright (C) 2005-2022 Mike Pall. https://luajit.org/
執行 lua 腳本,
[root@VM-4-5-centos ~]# echo 'print("hello world")' > 1.lua[root@VM-4-5-centos ~]# cat 1.luaprint("hello world")[root@VM-4-5-centos ~]# luajit 1.luahello world[root@VM-4-5-centos ~]#
也可以使用 resty 來直接運行,它最終也是用 LuaJIT 來執行的,
[root@VM-4-5-centos ~]# resty -e 'print("hello world")'hello world
在 Lua 中聲明變量,可以如下代碼所示,
local a = 'hello'b = "world"
加了 local 關鍵字,用于聲明局部變量。
不加 local 關鍵字的話,變量默認是全局的。
兩個減號是單行注釋
-- 注釋
多行注釋
--[[ 多行注釋 多行注釋 --]]
Lua 中代碼的行尾結束都不需要添加特殊字符,這跟 Java 不同(Java 在行尾需要添加 ;)。
local a = 'a'print(a)
Lua 中的數據類型不多,你可以通過 type 函數來返回一個值的類型,比如下面這樣的操作:
[root@VM-4-5-centos ~]# resty -e 'print(type("hello world"))> print(type(print))> print(type(true))> print(type(360.0))> print(type({}))> print(type(nil))> '
打印如下,
stringfunctionbooleannumbertablenil
這幾種就是 Lua 中的基本數據類型了。下面我們來簡單介紹一下它們。
在 Lua 中,有三種方式可以表達一個字符串:單引號、雙引號,以及長括號([[]]),示例如下,
新建 str.lua 文件,寫入以下內容,
local s = 'a'local s1 = "b"local s2 = [[c]]print(s)print(s1)print(s2)
執行 luajit str.lua 返回結果如下,
abc
在 Lua 中,字符串拼接采用 .. 的方式,示例如下,
編輯 str.lua 文件,寫入以下內容,
local s = 'a'local s1 = "b"local s2 = [[c]]print(s)print(s1)print(s2)local s3 =s .. s1 ..s2print(s3)
執行 luajit str.lua 返回結果如下,
abcabc
在 Lua 中,只有 nil 和 false 為假,其他都為 true,包括 0 和空字符串也為真。我們可以用示例印證一下:
新建 bool.lua 腳本文件,寫入以下內容,
local a = 0local bif a then print("true")enda = ""if a then print("true")endprint(b)
執行 luajit str.lua 返回結果如下,
truetruenil
在 Lua 中,空值就是 nil。如果你定義了一個變量,但沒有賦值,它的默認值就是 nil,對應的就是上面示例代碼的局部變量 b。
Lua 的 number 類型,是用雙精度浮點數來實現的。值得一提的是,LuaJIT 支持 dual-number(雙數)模式,也就是說,LuaJIT 會根據上下文來用整型來存儲整數,而用雙精度浮點數來存放浮點數。示例如下,
新建 number.lua 腳本文件,寫入以下內容,
print(type(2))print(type(2.2))print(type(0.2))print(type(2e+1))print(type(0.2e-1))print(type(7.8263692594256e-06))print(2 + 2)print(2 + 22.2)
執行 luajit number.lua 返回結果如下,
numbernumbernumbernumbernumbernumber424.2
函數在 Lua 中是一等公民,你可以把函數存放在一個變量中,也可以當作另外一個函數的入參和出參。示例如下,
新建 fun.lua 文件,寫入以下代碼,
-- 階乘function factorial1(n) if n == 0 then return 1 else return n * factorial1(n - 1) endendprint(factorial1(5))factorial2 = factorial1print(factorial2(5))
執行 luajit fun.lua 返回結果如下,
120120
Lua 提供了以下兩種分支控制結構語句:
Lua if 語句語法格式如下:
if(布爾表達式)then --[ 在布爾表達式為 true 時執行的語句 --]end
以下是一個判斷變量 a 的值是否小于 20 的示例,
新建 if1.lua,寫入以下內容,
--[ 定義變量 --]a = 10;--[ 使用 if 語句 --]if (a < 20) then --[ if 條件為 true 時打印以下信息 --] print("a 小于 20" );endprint("a 的值為:", a);
執行 luajit if1.lua 返回結果如下,
a 小于 20a 的值為: 10
Lua if 語句可以與 else 語句搭配使用, 在 if 條件表達式為 false 時執行 else 語句代碼塊。
Lua if...else 語句語法格式如下:
if(布爾表達式)then --[ 布爾表達式為 true 時執行該語句塊 --]else --[ 布爾表達式為 false 時執行該語句塊 --]end
以下是一個判斷變量 a 值的示例,
新建 if2.lua,寫入以下內容,
--[ 定義變量 --]a = 100;--[ 檢查條件 --]if( a < 20 )then --[ if 條件為 true 時執行該語句塊 --] print("a 小于 20" )else --[ if 條件為 false 時執行該語句塊 --] print("a 大于 20" )endprint("a 的值為 :", a)
執行 luajit if2.lua 返回結果如下,
a 大于 20a 的值為 : 100
Lua if 語句可以與 elseif...else 語句搭配使用, 在 if 條件表達式為 false 時執行 elseif...else 語句代碼塊,用于檢測多個條件語句。
Lua if...elseif...else 語句語法格式如下:
if( 布爾表達式 1)then --[ 在布爾表達式 1 為 true 時執行該語句塊 --]elseif( 布爾表達式 2)then --[ 在布爾表達式 2 為 true 時執行該語句塊 --]elseif( 布爾表達式 3)then --[ 在布爾表達式 3 為 true 時執行該語句塊 --]else --[ 如果以上布爾表達式都不為 true 則執行該語句塊 --]end
以下是一個判斷變量 a 值的示例,
新建 if3.lua,寫入以下內容,
--[ 定義變量 --]a = 100--[ 檢查布爾條件 --]if( a == 10 )then --[ 如果條件為 true 打印以下信息 --] print("a 的值為 10" )elseif( a == 20 )then --[ if else if 條件為 true 時打印以下信息 --] print("a 的值為 20" )elseif( a == 30 )then --[ if else if condition 條件為 true 時打印以下信息 --] print("a 的值為 30" )else --[ 以上條件語句沒有一個為 true 時打印以下信息 --] print("沒有匹配 a 的值" )endprint("a 的真實值為: ", a )
執行 luajit if3.lua 返回結果如下,
沒有匹配 a 的值a 的真實值為: 100
Lua 編程語言中 for 循環語句可以重復執行指定語句,重復次數可在 for 語句中控制。
Lua 編程語言中 for 語句有兩大類:
Lua 編程語言中數值 for 循環語法格式:
for var=exp1,exp2,exp3 do <執行體>end
var 從 exp1 變化到 exp2,每次變化以 exp3 為步長遞增 var,并執行一次 "執行體"。exp3 是可選的,如果不指定,默認為 1。示例如下,
新建 for1.lua 文件,寫入以下內容,
function f(x) print("function") return x*2endfor i = 1, f(5) do print(i)end
執行 luajit for1.lua 返回結果如下,
function12345678910
泛型 for 循環通過一個迭代器函數來遍歷所有值,類似 java 中的 foreach 語句。
Lua 編程語言中泛型 for 循環語法格式:
--打印數組a的所有值local a = {"one", "two", "three"}for i, v in ipairs(a) do print(i, v)end
i 是數組索引值,v 是對應索引的數組元素值。ipairs 是 Lua 提供的一個迭代器函數,用來迭代數組。
將以上內容下入 for2.lua 文件,打印結果如下,
1 one2 two3 three
模塊類似于一個封裝庫,從 Lua 5.1 開始,Lua 加入了標準的模塊管理機制,可以把一些公用的代碼放在一個文件里,以 API 接口的形式在其他地方調用,有利于代碼的重用和降低代碼耦合度。
Lua 提供了一個名為 require 的函數用來加載模塊。要加載一個模塊,只需要簡單地調用就可以了。例如:
require("cjson")-- 或者require "cjson"
Lua 比較小巧,內置的標準庫并不多。在 OpenResty 的環境中默認支持了一些官方模塊,如 cjson 可以直接使用,其他的一些第三方庫則需要先使用 lua_package_path 指令配置 OpenResty 的文件尋址路徑,又或者直接使用 opm 包管理工具來安裝一些第三方模塊。
OpenResty 中默認啟用了下面列表的絕大部分組件,想要了解更多 OpenResty 相關組件的話,可以翻閱官網說明 https://openresty.org/cn/components.html。
LuaJITArrayVarNginxModuleAuthRequestNginxModuleCoolkitNginxModuleDrizzleNginxModuleEchoNginxModuleEncryptedSessionNginxModuleFormInputNginxModuleHeadersMoreNginxModule...
本文的 Lua 語法介紹到這里就足夠在 OpenResty 中編寫 lua 腳本了,想要了解更多 Lua 內容,如 table、文件、調式等可以自行翻閱 https://www.runoob.com/lua/lua-tutorial.html 網站。
OpenResty 在內置 Lua 引擎中新增了一些常用的內置變量如下所示。
圖片
圖片來源https://zhuanlan.zhihu.com/p/539546173
OpenResty 在內置 Lua 引擎中新增了一些常用的內置常量大致如下所示。
圖片
圖片
這些內置變量和常量都可以在 Lua 腳本中直接使用。
OpenResty 定義了一系列 Nginx 配置指令,用于配置何時運行用戶 Lua 腳本以及如何返回 Lua 腳本的執行結果,這些指令可以直接在 nginx.conf 配置文件中使用。
OpenResty 定義的 Nginx 配置指令大致如下所示。
圖片
圖片來源https://zhuanlan.zhihu.com/p/539546173
這些指令中有 9 個 *_by_lua 指令,它們和 Nginx 的關系如下圖所示
圖片
圖片來自 lua-Nginx-module 文檔
其中,init_by_lua 只會在 Master 進程被創建時執行,init_worker_by_lua 只會在每個 Worker 進程被創建時執行。其他的 *_by_lua 指令則是由終端請求觸發,會被反復執行。
所以在 init_by_lua 階段,我們可以預先加載 Lua 模塊和公共的只讀數據,這樣可以利用操作系統的 COW(copy on write)特性,來節省一些內存。
對于業務代碼來說,其實大部分的操作都可以在 content_by_lua 里面完成,但更推薦的做法,是根據不同的功能來進行拆分,比如下面這樣:
利用這些階段的特性,我們可以一些通用邏輯進行拆分處理,比如我們可以在 access 階段解密,在 body filter 階段加密就可以了,在 content 階段的代碼是不用做任何修改的。
# 加密協議版本location /test { access_by_lua '...'; # 請求體解密 content_by_lua '...'; # 處理請求,不需要關心通信協議 body_filter_by_lua '...'; # 應答體加密}
Web 應用防火墻(Web Application Firewall,簡稱 WAF)對網站或者 App 的業務流量進行惡意特征識別及防護,在對流量清洗和過濾后,將正常、安全的流量返回給服務器,避免網站服務器被惡意入侵導致性能異常等問題,從而保障網站的業務安全和數據安全。
目前 WAF 相關產品主要有三類:
鑒于極客精神(白嫖萬歲
本文鏈接:http://www.tebozhan.com/showinfo-26-13623-0.html每個后端都應該了解的OpenResty入門以及網關安全實戰
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: 微服務部署—HAProxy+Keepalived高可用負載均衡集群配置
下一篇: 每個開發人員都想使用的編程語言