
首先,介绍下我的项目:Flask+Flask-SQLAlchemy,相关配置如下:
# config.py MYSQL_URL = 'mysql+mysqlconnector://xxx/db_name' DEBUG = True SQLALCHEMY_ECHO = True SQLALCHEMY_DATABASE_URI = MYSQL_URL SQLALCHEMY_TRACK_MODIFICATIOnS= False SQLALCHEMY_POOL_RECYCLE = 3000 SQLALCHEMY_ENCODING = "utf8mb4" # application.py from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy() ...... self.config.from_pyfile('config.py') db.init_app(self) 我现在的功能是:前面的功能一切正常,数据库增删改查也正常,随后进入一个扫描过程,是一个第三方的扫描工具,扫描完成后生成一串数据量很大的扫描结果( json 串),然后我循环这个 json 串,拿出里面的 ip ( ip 数量很多),然后在循环里根据 ip 查询其 id,就这个过程报异常:
(mysql.connector.errors.OperationalError) MySQL Connection not available 大概代码如下:
for result in nminfo['scan'].values(): if result['status']['state'] == "up": ip = result['addresses']['ipv4'] host_os = "xxx" # 位置 1 host_assets_id = db.session.query(HostAsset.host_id).filter(HostAsset.host_ip == ip).first() if host_assets_id: query_host_id = host_assets_id[0] # 位置 2 HostAsset.query.filter_by(host_id=query_host_id).update({'host_os': host_os}) HostAssetsHistory.query.filter_by(host_id=query_host_id, task_stream_id=self.task_stream_id).update({'host_os': host_os}) db.session.commit() else: pass 这个扫描过程,当这个扫描数据比较少的时候,扫描完成的时间就比较短。经过测试,扫描时间短的时候,不会报异常,程序正常跑完。但是当这个扫描的数据量大的时候,待扫描完成,查询数据库就报如上异常。
很奇怪,感觉是不是:扫描时间长,扫描过程与数据库没有交互,自动与数据库断开连接了?
我的 mysql 相关配置如下图:

1 CallMeReznov 2019 年 11 月 24 日 你的 close 呢? |
2 Raul7 OP @CallMeReznov 没有 close Flask-SQLAlchemy 不是自动 close 的吗? |
3 vZexc0m 2019 年 11 月 25 日 试试把 ip 全部循环之后用 IN 进行查询呢!条件允许的话可以添加上索引。 |
4 Eds1995 2019 年 11 月 25 日 加这个参数 SQLALCHEMY_ENGINE_OPTIOnS= { "pool_pre_ping": True, "pool_recycle": 300, } |
5 Eds1995 2019 年 11 月 25 日 增加 idle_timeout 时间 |
6 lbfeng 2019 年 11 月 25 日 |
7 neoblackcap 2019 年 11 月 25 日 request 对象泄露了被,没有被正确析构,所以导致数据库连接没有正确返回连接池 |
8 Raul7 OP @neoblackcap 老哥 你说的这个是什么意思?如何解决呢? |
10 Latin 2019 年 11 月 25 日 SQLALCHEMY_TRACK_MODIFICATIOnS= False SQLALCHEMY_RECORD_QUERIES = True SQLALCHEMY_POOL_SIZE = 1024 SQLALCHEMY_POOL_TIMEOUT = 90 SQLALCHEMY_POOL_RECYCLE = 3 SQLALCHEMY_MAX_OVERFLOW = 1024 这个配置服务跑了一年也没挂过 自己看下文档调下配置 |
11 Ccob 2019 年 11 月 25 日 |
12 neoblackcap 2019 年 11 月 25 日 @Raul7 你资源泄露了,Flask-SQLAlchemy 将 scoped_session 绑在 request 对象上,request 对象的生命周期结束了,那么就会调用对应的钩子函数,对 scoped_session 对象进行析构。那么数据库连接自然就会返回连接池。 你会出现这样的情况,要不就是 request 对象没有被正确结束,比如抛异常了,没有处理。或者你进行的任务实在是太长了,你的 request 对象一直没有析构(比如你在 celery 里面用了,但是 session 却又是用 request 绑定的) 你可以自己先排查一遍看看,反正绝大多数情况都应该是资源泄露引起的。 |
13 lolizeppelin 2019 年 11 月 25 日 在 pool 代码里加日志 从 pool 里取出 connection 的时候打印一条 还回去的时候打印一条 这不就知道哪次没还回去了么? 打日志打日志打日志 |
14 Raul7 OP |
15 linlance 2019 年 11 月 29 日 哎,我都放弃 Flask 了,太多小插件需要自己弄了,简洁优美,但是弄完了,最后也快变成个 Django 了,耦合度还高。。 我转 Django 了,有个个人项目,开发了两年了,还没第一版出来。 |