上一章: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