最近在对系统中某个接口进行压力测试的时候发现,有个 redis 的查询接口大量的报错,于是查看后台日志,发现是 aioredis 报了Too many connections
, 起初我认为是由于在进行压力测试,由于这个接口需要访问 redis 数据,同一时间来了大量的请求,所以会对 redis 服务器造成大量的请求,redis 服务器抗不住返回了Too many connections
错误,但是经过排查不是这样的。
查看详细的报错信息
Traceback (most recent call last):
File "handle_request", line 83, in handle_request
FutureStatic,
File "/app/handlers/test_handler.py", line 67, in get
n = await userdao.getTaskNum()
File "/app/dao/user_dao.py", line 33, in getTaskNum
resutl = await self.request.app.ctx.redis.get("task_num")
File "/usr/local/lib/python3.8/site-packages/aioredis/client.py", line 1082, in execute_command
conn = self.connection or await pool.get_connection(command_name, **options)
File "/usr/local/lib/python3.8/site-packages/aioredis/connection.py", line 1411, in get_connection
connection = self.make_connection()
File "/usr/local/lib/python3.8/site-packages/aioredis/connection.py", line 1449, in make_connection
raise ConnectionError("Too many connections")
aioredis.exceptions.ConnectionError: Too many connections
复制代码
查看 aioredis 源代码,
def make_connection(self):
"""Create a new connection"""
if self._created_connections >= self.max_connections:
raise ConnectionError("Too many connections")
self._created_connections += 1
return self.connection_class(**self.connection_kwargs)
复制代码
这里报错是由于_created_connections
大于max_connections
,这个 max_connections 又是在哪里初始化的呢?
我的代码在初始化 redis 连接池时设置了 max_connections
参数我这里设置成了 50,
def make_redis(redisConf):
'''
redis://[[username]:[password]]@localhost:6379/0
rediss://[[username]:[password]]@localhost:6379/0
unix://[[username]:[password]]@/path/to/socket.sock?db=0
:param redisConf:
:return:
'''
password = redisConf.get("password")
host = redisConf.get("host")
port = redisConf.get("port")
db = redisConf.get("db")
address = f'redis://:{password}@{host}:{port}/{db}'
try:
pool = aioredis.ConnectionPool.from_url(address, max_connections=50)
redis = aioredis.Redis(connection_pool=pool, encoding='utf-8')
return redis
except:
logger.error("连接redis失败")
raise Exception("连接redis失败")
复制代码
于是就有了最大的连接数为 50,也就是我这个系统与 redis 服务器最多会有 50 个连接,redis 的 ConnectionPool 的实例中, 有两个 list,一个是_available_connections
, _in_use_connections
, 在获取连接的时候从_available_connections
中弹出一个连接放到_in_use_connections
async with self._lock:
try:
connection = self._available_connections.pop()
except IndexError:
connection = self.make_connection()
self._in_use_connections.add(connection)
复制代码
也就是说,当报 Too many connections
时,后台想要去连接 redis 时,如果此时由于访问量太大,前一个请求还没有处理完,这时就要创建一个新的连接,但是如果此时连接数超过了 max_connections
则就会报 Too many connections
错误了!
解决方法也很简单,就是不要设置 max_connections
参数,当不设置该参数的时候,aioreids 将设置为 2 ** 31, 也就是 2 个 31 次方,该值已经非常大。
但是 redis 服务器也不能无限的连接,可以登录到 redis 服务器上,查看 redis 服务器自身可以连接的数量。该值默认为 10000.
在我取消了 aioredis 连接池最大连接数限制以后,再次进行压测
10 threads and 2000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 318.38ms 115.42ms 1.09s 70.99%
Req/Sec 622.78 241.92 1.58k 68.12%
Latency Distribution
50% 309.36ms
75% 377.44ms
90% 453.45ms
99% 711.74ms
60149 requests in 10.02s, 6.48MB read
Requests/sec: 6003.59
Transfer/sec: 662.51KB
复制代码
这时请求量上去了,可以达到 6K,在查看服务端以后发现上面的 6W 个请求,在服务端只需要大概 500 多个 redis 连接即可。
评论