现象
最近线上的redis服务器会莫名出现无法突然之间无法连接的情况,需要重启一下redis服务器才能正常工作,感觉很诡异. 查看redis日志,发现报如下错误:
Error allocating resoures for the client
网上搜索之后,找到
http://blog.sina.com.cn/s/blog_6262a50e0101cjyf.html
根据描述, 怀疑可能是redis连接数太多导致,于是查看
$ redis-cli -a password info | grep connected_clients
connected_clients:6997
这还是我刚刚才重新启动了redis server不久看到的数据。
为什么会用这么高连接数?上面的文章中介绍的是由于代码引起不停地创建redis Connection pool导致。仔细检查了自己的代码之后,发现不会有这种情况,但为什么还是会有这么高的连接?
验证
经过调试发现。虽然使用py-redis连接redis, 它使用到了连接池,但是,连接池默认是没有设置连接数的上限(可以通过相关参数设置),当高并发时,它会不断的创建连接,并且这些连接用完之后也不会自动释放。
由于我们的服务运行采用的gevent方式。 因此, 更容易引起这个问题。为了验证自己的
猜测, 写了个简单的计数程序app.py
测试一下。
from flask import Flask
from flask_redis import FlaskRedis
REDIS_URL = "redis://:password@localhost:6379/0"
DEBUG = True
app = Flask(__name__)
app.config.from_object(__name__)
redis = FlaskRedis(app, True)
@app.route("/")
def index():
redis.incr("hit", 1)
return redis.get("hit")
if __name__ == '__main__':
app.run()
采用gevent方式, 单worker启动
$ gunicorn -w 1 app:app --worker-class gevent --error-logfile -
使用ab模拟500个用户发送50000次请求的高并发
$ ab -c 500 -t 30 -r "http://127.0.0.1:8000/"
查看连接数
$ redis-cli -a password info |grep connected_clients
connected_clients:232
可以看到, 在高并发, 单worker的条件下, 一个连接池的连接数会就有两百多。然后测试开启4个worker的话,能达到一千二百多。
这些连接在连接池里使用之后,基本上都长期处于idle状态,白白浪费资源.
$ redis-cli -a password client list
addr=127.0.0.1:45327 fd=170 name= age=35 idle=34 flags=N db=0 sub=0 psub=0
multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=get
addr=127.0.0.1:43684 fd=44 name= age=36 idle=10 flags=N db=0 sub=0 psub=0
multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=get
addr=127.0.0.1:43704 fd=45 name= age=36 idle=10 flags=N db=0 sub=0 psub=0
multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=get
addr=127.0.0.1:43705 fd=46 name= age=36 idle=10 flags=N db=0 sub=0 psub=0
multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=get
addr=127.0.0.1:43729 fd=47 name= age=36 idle=10 flags=N db=0 sub=0 psub=0
multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=get
addr=127.0.0.1:43730 fd=48 name= age=36 idle=10 flags=N db=0 sub=0 psub=0
multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=get
addr=127.0.0.1:43731 fd=49 name= age=36 idle=10 flags=N db=0 sub=0 psub=0
multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=get
addr=127.0.0.1:43732 fd=50 name= age=36 idle=10 flags=N db=0 sub=0 psub=0
.....
解决方式:
-
从目前的情况来看,是由于连接池维持了太多的连接导致. 因此不用的连接池应该及 时被释放掉。通过设置
$ reidis-cli -a password CONFIG SET timeout 30
让redis自动释放掉idle时间超过30秒的连接, 设置之后,服务器上redis连接数一 下子降下来了。
-
让redis连接也异步
根据另一篇文章介绍使用redis异步, 自己测试之后发现没有什么效果,也不知道哪 里没有做对, 有机会再研究。
https://gehrcke.de/2013/01/highly-concurrent-connections-to-redis-with-gevent-and-redis-py/