OpenResty學習筆記
Nginx作為基礎網關服務在現在主流服務端開發中占據重要作用,要在網關層支持更多的特性,這時候就需要OpenResty了,本文記錄一下OpenResty 做api gateway的一些知識
概述
OpenResty
: 是基於Nginx與Lua的高性能Web平台,帶有很多優秀的Lua庫,可以做動態服務網關。
OpenResty
:設計哲學:對於不同的階段,進行相應的不同的處理
- Nginx非阻塞I/O模型
- Lua:是一種擴展式的動態類型語言,沒有main函數概念,只能嵌入宿主功能,高性能。關於Lua的語言介紹可參考Lua中文手冊
架構
Nginx的請求處理階段有11,其中最重要的也是最常見的3個階段依次為rewrite
, access
,content
.
下圖為Lua Nginx Module指令的執行順序圖,可以根據具體的業務場景設置不同的lua腳本。幾個重要的部分:
init_by_lua
init_by_lua_block
: 運行在Nginx loading-config 階段,注冊Nginx Lua全局變量,和一些預加載模塊。是Nginx master進程在加載Nginx配置時執行。init_worker_by_lua
: 在Nginx starting-worker階段,即每個nginx worker啟動時會調用,通常用來hook worker進程,並創建worker進行的計時器,用來健康檢查,或者設置熔斷記時窗口等等。access_by_lua
: 在access tail
階段,用來對每次請求做訪問控制,權限校驗等等,能拿到很多相關變量。例如:請求體中的值,header中的值,可以將值添加到ngx.ctx
, 在其他模塊進行相應的控制balancer_by_lua
: 通過Lua設置不同的負載均衡策略, 具體可以參考lua-resty-balancercontent_by_lua
: 在content階段,即content handler
的角色,即對於每個api請求進行處理,注意不能與proxy_pass放在同一個location下proxy_pass
: 真正發送請求的一部分, 通常介於access_by_lua
和log_by_lua
之間header_filter_by_lua
:在output-header-filter
階段,通常用來重新響應頭部,設置cookie等,也可以用來作熔斷觸發標記body_filter_by_lua
:對於響應體的content進行過濾處理log_by_lua
:記錄日志即,記錄一下整個請求的耗時,狀態碼等

常用命令
ngx.var.<arg>
, lua使用nginx內置的綁定變量.ngx.var.remote_addr
為獲取遠程的地址,ngx.var.http_cookie
獲取cookie信息ngx.ctx
: 每次請求的上下文,可以在ctx里記錄,每次請求上下文的一些信息,例如:request_id
,access_key
等等upstream
塊里的的balancer_by_lua_file
, 使用ngx.balancer
模塊,可以實現不同的負載均衡策略,並調用set_current_peer
函數設置當前query調用的backend- 對Openresty寫的網關,測試也至關重要,測試是基於perl的單元測試,包含了Nginx C model和OpenResty文檔地址 OpenResty Test guide
ngx.shared.DICT dict = ngx.shared[name_var]
: 獲取共享內存的lua dict. 且shared.DICT在當前nginx server實例中被所有nginx worker進程共享。ngx.shared.DICT
當到達定義的大小限制時,再次調用set操作,會使用LRU淘汰一些key。set操作會返回三個值(success, err, forcible).ngx.shared.DICT
支持過期操作,expire等等- 對於ngx.shared.DICT的並發安全,使用
resty.lock
庫進行加速控制 cosocket
可以理解為coroutine + socket
: 協程特性支撐 + nginx事件循環回調機制ngx.timer.at(delay, callback, user_arg1)
為cosocket
API,定時任務, 定時執行某些異步任務,即異步執行某些操作,不必等待結果返回時調用。例如:統計一段窗口期的請求耗時,發送通知管理員郵件的等等。ngx.timer.at
通過遞歸的方式,來實現每隔多長時間,執行某些操作。
配置
lua_package_path
: lua擴展的庫的地址,;;
為設置默認的路徑lua_package_cpath:
Lua 擴展c 的so庫地址,;;
為設置默認的路徑
-
# set search paths for pure Lua external libraries (';;' is the default path):
-
lua_package_path '/foo/bar/?.lua;/blah/?.lua;;';
-
-
# set search paths for Lua external libraries written in C (can also use ';;'):
-
lua_package_cpath '/bar/baz/?.so;/blah/blah/?.so;;';
-
-
server {
-
location /lua_content {
-
# MIME type determined by default_type:
-
default_type 'text/plain';
-
-
content_by_lua_block {
-
ngx.say('Hello,world!')
-
}
-
}
-
-
location /nginx_var {
-
# MIME type determined by default_type:
-
default_type 'text/plain';
-
-
# try access /nginx_var?a=hello,world
-
content_by_lua_block {
-
ngx.say(ngx.var.arg_a)
-
}
-
}
-
-
location = /request_body {
-
client_max_body_size 50k;
-
client_body_buffer_size 50k;
-
-
content_by_lua_block {
-
-
local data = ngx.req.get_body_data()
-
if data then
-
-
ngx.print(data)
-
return
-
end
-
-
-
local file = ngx.req.get_body_file()
-
if file then
-
-
else
-
-
end
-
}
-
}
-
-
# transparent non-blocking I/O in Lua via subrequests
-
# (well, a better way is to use cosockets)
-
location = /lua {
-
# MIME type determined by default_type:
-
default_type 'text/plain';
-
-
content_by_lua_block {
-
local res = ngx.location.capture("/some_other_location")
-
if res then
-
-
-
ngx.print(res.body)
-
end
-
}
-
}
-
-
location = /foo {
-
rewrite_by_lua_block {
-
res = ngx.location.capture("/memc",
-
-
)
-
}
-
-
proxy_pass http://blah.blah.com;
-
}
-
-
location = /mixed {
-
rewrite_by_lua_file /path/to/rewrite.lua;
-
access_by_lua_file /path/to/access.lua;
-
content_by_lua_file /path/to/content.lua;
-
}
-
-
# use nginx var in code path
-
# CAUTION: contents in nginx var must be carefully filtered,
-
# otherwise there'll be great security risk!
-
location ~ ^/app/([-_a-zA-Z0-9/]+) {
-
set $path $1;
-
content_by_lua_file /path/to/lua/app/root/$path.lua;
-
}
-
-
location / {
-
client_max_body_size 100k;
-
client_body_buffer_size 100k;
-
-
access_by_lua_block {
-
-
if ngx.var.remote_addr == "132.5.72.3" then
-
ngx.exit(ngx.HTTP_FORBIDDEN)
-
end
-
-
-
if ngx.var.uri and
-
-
then
-
return ngx.redirect("/terms_of_use.html")
-
end
-
-
-
}
-
-
# proxy_pass/fastcgi_pass/etc settings
-
}
-
}
Reference
- Lua中文手冊
- lua-nginx-module
- OpenResty最佳實踐
- OpenResty API server