最近在对系统中某个接口进行压力测试的时候发现,有个 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 readRequests/sec: 6003.59Transfer/sec: 662.51KB
复制代码
这时请求量上去了,可以达到 6K,在查看服务端以后发现上面的 6W 个请求,在服务端只需要大概 500 多个 redis 连接即可。
评论