在django中使用grpc时,一定时间后再次请求接口的时候会出现
'MySQL server has gone away'
的错误,搜索后有说加大mysql的最大连接数,试了后发现无效果。应用还在测试阶段,几乎没有请求量,而且总会发生这个问题,开始使用的是docker的mysql镜像作为数据库,疑心是这个的原因,于是换了阿里云的rds,问题依旧。
查了很多资料后发现问题:线程池处理任务时,正常使用的连接中不会被关闭,但由于数据库端有最长连接时间的限制(默认为8小时),在超时后会发生InterfaceError: (0, '')(连接关闭后使用连接/游标)或Error(2006, 'MySQL server has gone away')(mysql 服务器主动关闭连接)这类错误,所以一般会在每个任务线程中调用django.db.connection.close()进行关闭操作。
附上代码:
if __name__ == '__main__':
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
bus_pb2_grpc.add_BusInfoServicer_to_server(BusGreeter(), server)
server.add_insecure_port('0.0.0.0:41000')
server.start()
print('server is start.')
print('bind to port 41000.')
try:
while True:
time.sleep(_ONE_DAY_IN_SECONDS)
except KeyboardInterrupt:
server.stop(0)
官方的demo使用了线程池,每次django从中处理任务后不会主动关闭数据库连接,等超过时限(8小时)后另一个任务从线程池中取到这个未关闭的数据库连接后就会一直失败('MySQL server has gone away')
随后查看了django关于view的源码,发现在每次请求进来时会清空旧连接,打开一个新的数据库连接,在view结束的时候关闭连接
而grpc的调用不属于django view的范畴,所以就需要手动添加这个关闭数据库的操作
# 在每次操作model前调用
def close_old_connections(func):
def wrapper(*args, **kwargs):
for conn in connections.all():
conn.close_if_unusable_or_obsolete()
return func(*args, **kwargs)
return wrapper
@close_old_connections
def xxx():
pass