我有一个不断运行的应用程序(从screen -S myapp python3 app.py
开始在Linux中,然后我将其分离)。它可能是Bottle应用程序,Flask应用程序或涉及永久运行的事件循环的任何其他系统:
import anyframework # can be bottle, flask or anything else
import sqlite3
@route('/')
def index():
c = db.cursor()
c.execute('INSERT INTO test VALUES (?)', ('test',))
c.close() # we can't commit here for *each* client request, it would eat 100ms for each request
return 'hello'
@route('/makeitcrash')
def index():
sdlfksdfs # this will generate an error
def cleanup():
db.commit()
db = sqlite3.connect('test.db')
run()
在服务器终止的所有可能情况下,如何可靠地确保cleanup()
(以及DB提交)被调用?即:
如果服务器被SIGKILL杀死,SIGTERM
如果服务器代码有错误(例如,如果访问了http://example.com/makeitcrash,则为错误)
如果我在终端中(正在运行的screen
内,则执行CTRL + C)
?
我本来要使用atexit
并在各处添加try: except:
,但我认为它将引入许多代码重复,以便为每条路线插入try: except:
。
此问题的一般解决方案是什么?
“ SIGKILL本质上不能被捕获。”
查看此答案https://stackoverflow.com/a/30732799/7527728
对于可能被困的情况,您可以使用with语句,请参见https://www.python.org/dev/peps/pep-0343/
这里有一个非常简单的示例,说明如何定义enter和exit块:https://effbot.org/zone/python-with-statement.htm
一种方法是捕获信号并在收到信号时进行清理。
import signal
import sys
from bottle import run
def handle_signal(signum, frame):
print(f'handling signal {signum}')
# do cleanup
sys.exit(0)
signal.signal(signal.SIGINT, handle_signal)
# also SIGTERM, et al.
run()
注意事项
正如@MarkAllen指出的,永远无法捕获SIGKILL
。
首先,我也建议不要以这种方式解决问题。相反,请查看您是否可以设计Web应用程序,以便在关机时无需进行任何清理。 (例如,对每个请求执行持久写入,而不是在内存中进行缓冲。)减少保持的状态量将有助于简化许多事情,包括如何处理崩溃/关机。