Openresty Redis正确使用连接池(set_keepalive)

本文探讨了在OpenResty项目中遇到的高并发下Redis连接爆炸问题,提出了两种解决方案。首先尝试在init阶段创建连接并在access阶段复用,但这种方法在Redis故障时会导致功能不可用,且TCP连接可能中断。然后转向使用连接池(set_keepalive)进行管理,初期出现错误,原因是误将set_keepalive用在已关闭的连接上。最终修正了使用方法,确保在每次操作后正确设置连接池,成功解决了并发连接问题。在实际测试中,连接数与并发量相关,达到了预期效果。

最近在做一个openresty项目,每次访问需要通过openresty读取redis,判断是否可以允许访问。

问题:

如果每次访问就要与redis建立连接,当并发量大时,连接数会直接爆炸。影响性能和系统。

方案一:

在init_by_lua中先创建redis连接,在access_by_lua中直接使用:

init.lua:

local redis = require "redis"
client = redis.connect('127.0.0.1', 6379)

filter.lua(演示:简单的黑名单功能,如果request的remoteip在redis中存在key,就返回403):

client = red;

function redisHasKey(client,keyname)
	if(client:get(keyname))
	then
		return true;
	else
		return false;
	end
end

local keyname = ngx.var.remote_addr;
if(pcall(redisHasKey,client,keyname))then
	if(redisHasKey(client,keyname))
	then
		ngx.exit(403);
	else
		return
	end
else
	return
end

在nginx启动时,就创建了连接,并只使用这个连接。就不会出现连接数过多问题。

方案一问题:

1、在使用中redis故障会导致功能不可用,即使重启redis还需要重启nginx才能重新获取连接。

2、linux tcp连接不能保证长时间不中断。中断后不能修复。

3、单连接在高并发多请求情况下,会阻塞请求,多个请求会等待连接空闲后再进行操作,甚至直接报错。

方案二:

连接池,针对连接数管理

openresty官方本身有连接池管理(set_keepalve)

语法:syntax: ok, err = red:set_keepalive(max_idle_timeout, pool_size)

我就来试着用下,这次只使用filter.lua:

local redis = require "resty.redis"
local red = redis:new()
red:set_timeouts(1000, 1000, 1000)
red:set_keepalive(1000, 20)
red:connect("127.0.0.1", 6379)
client = red;

function redisHasKey(client,keyname)
	if(client:get(keyname))
	then
		return true;
	else
		return false;
	end
end

local keyname = ngx.var.remote_addr;
if(pcall(redisHasKey,client,keyname))then
	if(redisHasKey(client,keyname))
	then
		ngx.exit(403);
	else
		return
	end
else
	return
end

问题:

使用之后发现更大的问题,连接数量没有被连接池控制,同时出现大量报错:

2021/05/19 13:57:57 [error] 2734932#0: *2019 attempt to send data on a closed socket: u:00000000401F79D0, c:0000000000000000, ft:0 eof:0, client: 127.0.0.1, server: , request: "GET / HTTP/1.0", host: "127.0.0.1:8082"
2021/05/19 13:57:57 [error] 2734932#0: *2019 attempt to send data on a closed socket: u:00000000401F79D0, c:0000000000000000, ft:0 eof:0, client: 127.0.0.1, server: , request: "GET / HTTP/1.0", host: "127.0.0.1:8082"
2021/05/19 13:57:57 [alert] 2734932#0: *2019 socket() failed (24: Too many open files), client: 127.0.0.1, server: , request: "GET / HTTP/1.0", host: "127.0.0.1:8082"
2021/05/19 13:57:57 [error] 2734932#0: *2019 attempt to send data on a closed socket: u:00000000401F6290, c:0000000000000000, ft:0 eof:0, client: 127.0.0.1, server: , request: "GET / HTTP/1.0", host: "127.0.0.1:8082"

连接在被使用的时候已经中断了。

解决:

看下官方对于set_keepalive的用法说明

set_keepalive

syntax: ok, err = red:set_keepalive(max_idle_timeout, pool_size)

Puts the current Redis connection immediately into the ngx_lua cosocket connection pool.

You can specify the max idle timeout (in ms) when the connection is in the pool and the maximal size of the pool every nginx worker process.

In case of success, returns 1. In case of errors, returns nil with a string describing the error.

Only call this method in the place you would have called the close method instead. Calling this method will immediately turn the current redis object into the closed state. Any subsequent operations other than connect() on the current object will return the closed error.

简单来说,set_keepalive使用时是用来替代close的,如果使用了set_keepalive之后再对连接进行任何除了connect的操作都会报错。

看来看官方文档很重要啊!

改进下:

local redis = require "resty.redis"
local red = redis:new()
--red:set_timeouts(1000, 1000, 1000)
--red:set_keepalive(1000, 20)
red:connect("127.0.0.1", 6379)
client = red;

function redisHasKey(client,keyname)
	if(client:get(keyname))
	then
		return true;
	else
		return false;
	end
end

local keyname = ngx.var.remote_addr;
if(pcall(redisHasKey,client,keyname))then
	if(redisHasKey(client,keyname))
	then
		red:set_keepalive(1000, 200)
		ngx.exit(403);
	else
		red:set_keepalive(1000, 200)
		return
	end
else
	red:set_keepalive(1000, 200)
	return
end

虽然用起来还挺恶心,但确实解决了我们的问题。

在使用ab测试时连接数虽然不会被严格控制在set_keepalive的pool_size数值内,但能够与测试的并发量相关。

<think>好的,用户想了解如何在Nginx中配置和连接Redis服务。首先,我需要回忆一下Nginx和Redis集成的主要方法。通常,这可以通过使用第三方模块来实现,比如Redis模块或者结合Lua脚本的OpenResty。 首先,用户可能需要安装这些模块。例如,HttpRedis模块允许Nginx直接与Redis通信,但功能可能有限。而OpenResty则提供了更强大的Lua脚本支持,可以更灵活地操作Redis。因此,我应该分两种情况来介绍:使用HttpRedis模块和使用OpenRestyLua扩展。 接下来,配置Nginx的步骤是关键。需要指导用户如何编辑nginx.conf文件,添加Redis服务器的地址和端口,并设置相关的location块来处理请求。比如,使用HttpRedis模块时,可能需要配置redis_pass指令指向Redis服务器。而对于OpenResty,可能需要使用lua_resty_redis库来编写连接和查询的Lua脚本。 此外,用户可能需要了解如何测试配置是否正确。例如,发送HTTP请求并检查返回的数据是否来自Redis。同时,要注意权限和安全性问题,比如确保Nginx有权限访问Redis,并设置适当的防火墙规则。 还要考虑常见问题,比如模块是否编译正确Redis连接超时如何处理,以及性能优化的建议,如连接池使用。最后,提醒用户参考相关文档,并确保版本兼容性。 需要确认这些步骤是否全面,是否有遗漏的细节。比如,是否需要安装额外的依赖库,或者配置中的具体参数示例。另外,用户可能不熟悉Lua脚本,所以需要提供简单的示例代码,并解释每一步的作用。确保回答结构清晰,分步骤说明,让用户能逐步操作。</think>### 如何在Nginx中配置和连接Redis服务 #### 1. 使用第三方模块(推荐HttpRedisOpenResty) Nginx原生不支持直接连接Redis,需通过第三方模块实现: - **HttpRedis模块**:提供基础键值操作,需编译安装到Nginx - **OpenResty**:集成Lua脚本扩展,可通过`lua-resty-redis`库实现更复杂的Redis交互[^1] ```nginx # 使用HttpRedis的示例配置(需提前编译模块) location /cache { redis_pass 127.0.0.1:6379; redis_query get $arg_key; } ``` #### 2. OpenResty+Lua完整配置流程 ```nginx http { lua_package_path "/usr/local/openresty/lualib/?.lua;;"; server { location /redis { content_by_lua_block { local redis = require "resty.redis" local red = redis:new() red:set_timeout(1000) -- 1秒超时 local ok, err = red:connect("127.0.0.1", 6379) if not ok then ngx.say("连接失败: ", err) return end local res, err = red:get("mykey") if res then ngx.say("值: ", res) else ngx.say("查询失败: ", err) end red:set_keepalive(10000, 100) -- 连接池配置 } } } } ``` #### 3. 关键配置参数说明 | 参数 | 作用 | 推荐值 | |---------------------|----------------------------|-------------| | `redis_connect_timeout` | 连接超时时间 | 1000ms | | `redis_read_timeout` | 读取响应超时 | 3000ms | | `keepalive_pool` | 连接池大小 | 100 | | `keepalive_timeout` | 连接保持时间 | 60s | #### 4. 安全配置建议 - 启用Redis密码认证:在连接配置中添加`red:auth("yourpassword")` - 限制访问IP:Nginx配置`allow/deny`指令结合Redis的bind配置 - 使用SSL连接(Redis 6+支持):需配置`red:ssl(true)`和证书 #### 5. 性能优化方案 1. **连接复用**:通过`set_keepalive`启用连接池 2. **批量操作**:使用Redis的pipeline减少网络往返 3. **本地缓存**:结合Nginx的共享字典做二级缓存 4. **集群支持**:配置多个Redis节点实现负载均衡
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值