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