# PostgresNginxModule模塊的調用方式
### ngx_postgres模塊使用方法
~~~
location /postgres {
internal;
default_type text/html;
set_by_lua $query_sql 'return ngx.unescape_uri(ngx.var.arg_sql)';
postgres_pass pg_server;
rds_json on;
rds_json_buffer_size 16k;
postgres_query $query_sql;
postgres_connect_timeout 1s;
postgres_result_timeout 2s;
}
~~~
這里有很多指令要素:
- internal 這個指令指定所在的 location 只允許使用于處理內部請求,否則返回 404 。
- set_by_lua 這一段內嵌的 lua 代碼用于計算出 $query_sql 變量的值,即后續通過指令 postgres_query 發送給 PostgreSQL 處理的 SQL 語句。這里使用了GET請求的 query 參數作為 SQL 語句輸入。
- postgres_pass 這個指令可以指定一組提供后臺服務的 PostgreSQL 數據庫的 upstream 塊。
- rds_json 這個指令是 ngx_rds_json 提供的,用于指定 ngx_rds_json 的 output 過濾器的開關狀態,其模塊作用就是一個用于把 rds 格式數據轉換成 json 格式的 output filter。這個指令在這里出現意思是讓 ngx_rds_json 模塊幫助 ngx_postgres 模塊把模塊輸出數據轉換成 json 格式的數據。
- rds_json_buffer_size 這個指令指定 ngx_rds_json 用于每個連接的數據轉換的內存大小. 默認是 4/8k,適當加大此參數,有利于減少 CPU 消耗。
- postgres_query 指定 SQL 查詢語句,查詢語句將會直接發送給 PostgreSQL 數據庫。
- postgres_connect_timeout 設置連接超時時間。
- postgres_result_timeout 設置結果返回超時時間。
這樣的配置就完成了初步的可以提供其他 location 調用的 location 了。但這里還差一個配置沒說明白,就是這一行:
~~~
postgres_pass pg_server;
~~~
其實這一行引入了 名叫 pg_server 的 upstream 塊,其定義應該像如下:
~~~
upstream pg_server {
postgres_server 192.168.1.2:5432 dbname=pg_database
user=postgres password=postgres;
postgres_keepalive max=800 mode=single overflow=reject;
}
~~~
這里有一些指令要素:
-
postgres_server 這個指令是必須帶的,但可以配置多個,用于配置服務器連接參數,可以分解成若干參數:
- 直接跟在后面的應該是服務器的 IP:Port
- dbname 是服務器要連接的 PostgreSQL 的數據庫名稱。
- user 是用于連接 PostgreSQL 服務器的賬號名稱。
- password 是賬號名稱對應的密碼。
-
postgres_keepalive 這個指令用于配置長連接連接池參數,長連接連接池有利于提高通訊效率,可以分解為若干參數:
- max 是工作進程可以維護的連接池最大長連接數量。
- mode 是后端匹配模式,在postgres_server 配置了多個的時候發揮作用,有 single 和 multi 兩種值,一般使用 single 即可。
- overflow 是當長連接數量到達 max 之后的處理方案,有 ignore 和 reject 兩種值。
- ignore 允許創建新的連接與數據庫通信,但完成通信后馬上關閉此連接。
- reject 拒絕訪問并返回 503 Service Unavailable
這樣就構成了我們 PostgreSQL 后端通訊的通用 location,在使用 lua 業務編碼的過程中可以直接使用如下代碼連接數據庫(折騰了這么老半天):
~~~
local json = require "cjson"
function test()
local res = ngx.location.capture('/postgres',
{ args = {sql = "SELECT * FROM test" } }
)
local status = res.status
local body = json.decode(res.body)
if status == 200 then
status = true
else
status = false
end
return status, body
end
~~~
### 與resty-mysql調用方式的不同
先來看一下`lua-resty-mysql`模塊的調用示例代碼。
~~~
# you do not need the following line if you are using
# the ngx_openresty bundle:
lua_package_path "/path/to/lua-resty-mysql/lib/?.lua;;";
server {
location /test {
content_by_lua '
local mysql = require "resty.mysql"
local db, err = mysql:new()
if not db then
ngx.say("failed to instantiate mysql: ", err)
return
end
db:set_timeout(1000) -- 1 sec
local ok, err, errno, sqlstate = db:connect{
host = "127.0.0.1",
port = 3306,
database = "ngx_test",
user = "ngx_test",
password = "ngx_test",
max_packet_size = 1024 * 1024 }
if not ok then
ngx.say("failed to connect: ", err, ": ", errno, " ", sqlstate)
return
end
ngx.say("connected to mysql.")
-- run a select query, expected about 10 rows in
-- the result set:
res, err, errno, sqlstate =
db:query("select * from cats order by id asc", 10)
if not res then
ngx.say("bad result: ", err, ": ", errno, ": ", sqlstate, ".")
return
end
local cjson = require "cjson"
ngx.say("result: ", cjson.encode(res))
-- put it into the connection pool of size 100,
-- with 10 seconds max idle timeout
local ok, err = db:set_keepalive(10000, 100)
if not ok then
ngx.say("failed to set keepalive: ", err)
return
end
';
}
}
~~~
看過這段代碼,大家肯定會說:這才是我熟悉的,我想要的。為什么剛剛`ngx_postgres`模塊的調用這么詭異,配置那么復雜,其實這是發展歷史造成的。`ngx_postgres`起步比較早,當時`OpenResty`也還沒開始流行,所以更多的 Nginx 數據庫都是以 ngx_c_module 方式存在。有了`OpenResty`,才讓我們具有了使用完整的語言來描述我們業務能力。
后面我們會單獨說一說使用`ngx_c_module`的各種不方便,也就是我們所踩過的坑。希望能給大家一個警示,能轉到`lua-resty-***`這個方向的,就千萬不要和`ngx_c_module`玩,`ngx_c_module`的擴展性、可維護性、升級等各方面都沒有`lua-resty-***`好。
這絕對是經驗的總結。不服來辯!
- 序
- Lua簡介
- Lua環境搭建
- 基礎數據類型
- 表達式
- 控制結構
- if/else
- while
- repeat
- 控制結構for的使用
- break,return
- Lua函數
- 函數的定義
- 函數的參數
- 函數的返回值
- 函數回調
- 模塊
- String庫
- Table庫
- 日期時間函數
- 數學庫函數
- 文件操作
- 元表
- 面向對象編程
- FFI
- LuaRestyRedisLibrary
- select+set_keepalive組合操作引起的數據讀寫錯誤
- redis接口的二次封裝(簡化建連、拆連等細節)
- redis接口的二次封裝(發布訂閱)
- pipeline壓縮請求數量
- script壓縮復雜請求
- LuaCjsonLibrary
- json解析的異常捕獲
- 稀疏數組
- 空table編碼為array還是object
- 跨平臺的庫選擇
- PostgresNginxModule
- 調用方式簡介
- 不支持事務
- 超時
- 健康監測
- SQL注入
- LuaNginxModule
- 執行階段概念
- 正確的記錄日志
- 熱裝載代碼
- 阻塞操作
- 緩存
- sleep
- 定時任務
- 禁止某些終端訪問
- 請求返回后繼續執行
- 調試
- 調用其他C函數動態庫
- 我的lua代碼需要調優么
- 變量的共享范圍
- 動態限速
- shared.dict 非隊列性質
- 如何添加自己的lua api
- 正確使用長鏈接
- 如何引用第三方resty庫
- 使用動態DNS來完成HTTP請求
- 緩存失效風暴
- Lua
- 下標從1開始
- 局部變量
- 判斷數組大小
- 非空判斷
- 正則表達式
- 不用標準庫
- 虛變量
- 函數在調用代碼前定義
- 抵制使用module()函數來定義Lua模塊
- 點號與冒號操作符的區別
- 測試
- 單元測試
- API測試
- 性能測試
- 持續集成
- 灰度發布
- web服務
- API的設計
- 數據合法性檢測
- 協議無痛升級
- 代碼規范
- 連接池
- c10k編程
- TIME_WAIT問題
- 與Docker使用的網絡瓶頸
- 火焰圖
- 什么時候使用
- 顯示的是什么
- 如何安裝火焰圖生成工具
- 如何定位問題