上一章:C語言PHP擴展高性能數據庫ORM框架ycdb(2): 構建穩定的數據庫連接池
目錄:
- Instruction
- Requirement
- 創建測試表
- 在linux中編譯ycdb
- Start ycdatabase
- 初始化ycdb連接
- 原生SQL執行
- 錯誤處理
- Where 語句
- Select 語句
- Insert 語句
- Replace 語句
- Update 語句
- Delete 語句
- 完整例句
- 數據庫事務
- 數據緩存
- PHP數據庫連接池
- Redis 連接池方案
Redis連接池方案
同理,Redis也可以採用相同的方法解決連接池問題。
源碼 github 地址: https://github.com/caohao-php/ycdatabase
Redis連接池配置
~/openresty-pool/conf/nginx.conf ,
worker_processes 1; #nginx worker 數量
error_log logs/error.log; #指定錯誤日誌文件路徑
events {
worker_connections 1024;
}
stream {
lua_code_cache on;
lua_check_client_abort on;
server {
listen unix:/tmp/redis_pool.sock;
content_by_lua_block {
local redis_pool = require "redis_pool"
pool = redis_pool:new({ip = "127.0.0.1", port = 6379, auth = "password"})
pool:run()
}
}
server {
listen unix:/tmp/mysql_pool.sock;
content_by_lua_block {
local mysql_pool = require "mysql_pool"
local config = {host = "127.0.0.1",
user = "root",
password = "test",
database = "collect",
timeout = 2000,
max_idle_timeout = 10000,
pool_size = 200}
pool = mysql_pool:new(config)
pool:run()
}
}
}
PHP代碼
$redis = new Redis();
$redis->pconnect('/tmp/redis_pool.sock');
var_dump($redis->hSet("foo1", "vvvvv42", 2));
var_dump($redis->hSet("foo1", "vvvv", 33));
var_dump($redis->expire("foo1", 111));
var_dump($redis->hGetAll("foo1"));
Redis連接池Lua代碼
~/openresty-pool/redis_pool.lua
local redis = require "resty.redis"
local assert = assert
local rawget = rawget
local setmetatable = setmetatable
local tonumber = tonumber
local byte = string.byte
local sub = string.sub
-- 解析請求
local function parse_request(sock)
local line, err = sock:receive()
if not line then
if err == "timeout" then
sock:close()
end
return nil, err
end
local result = line .. "\r\n"
local prefix = byte(line)
if prefix == 42 then -- char '*'
local num = tonumber(sub(line, 2))
if num <= 0 then
return result
end
for i = 1, num do
local res, err = parse_request(sock)
if res == nil then
return nil, err
end
result = result .. res
end
elseif prefix == 36 then -- char '$'
local size = tonumber(sub(line, 2))
if size <= 0 then
return result
end
local res, err = sock:receive(size)
if not res then
return nil, err
end
local crlf, err = sock:receive(2)
if not crlf then
return nil, err
end
result = result .. res .. crlf
end
return result
end
-- 異常退出
local function exit(err)
ngx.log(ngx.ERR, "Redis ERROR EXIT: [", err, "]")
return ngx.exit(ngx.ERROR)
end
----------------------------------------
local _M = {}
_M._VERSION = "1.0"
function _M.new(self, config)
local t = {
_ip = config.ip or "127.0.0.1",
_port = config.port or 6379,
_auth = config.auth,
_timeout = config.timeout or 1000, -- default 1 sec
_pool_size = config.pool_size or 100,
_max_idle_timeout = config.max_idle_timeout or 10000
}
return setmetatable(t, { __index = _M })
end
function _M.run(self)
local downstream_sock = assert(ngx.req.socket(true))
-- 解析客戶端請求
local res, err = parse_request(downstream_sock)
if not res then
return exit("parse_request failed : " .. err)
end
-- 創建 redis 連接
local red = redis:new()
red:set_timeout(self._timeout)
local ok, err = red:connect(self._ip, self._port)
if not ok then
return exit(err)
end
-- redis auth 授權
if self._auth then
local times = assert(red:get_reused_times())
if times == 0 then
local ok, err = red:auth(self._auth)
if not ok then
return exit("auth failed : " .. err)
end
end
end
-- 發送請求到 redis
local upstream_sock = rawget(red, "_sock")
upstream_sock:send(res)
-- 接收 redis 應答,並解析
local res, err = parse_request(upstream_sock)
if not res then
return exit("receive from redis server error: " .. err)
end
-- 發送應答給客戶端
downstream_sock:send(res)
-- 設置 redis 連接池
local ok, err = red:set_keepalive(self._max_idle_timeout, self._pool_size)
if not ok then
ngx.log(ngx.ERR, "redis set_keepalive failed: [", err, "]")
end
end
return _M