我在将 SIGINT 发送到使用 MySQLdb(mysql-python)连接到 MySQL 数据库的 python 脚本时遇到问题。python 脚本在无限循环中运行,我想在完成脚本的当前循环后捕获 SIGINT 并优雅地退出。但是,SIGINT 会中断正在运行的任何 mysql 查询,并与服务器断开连接。这会导致错误,因为 python 脚本试图完成它的当前循环。
我已将行为范围缩小到以下脚本:
无穷大.py
#! /usr/bin/python
import time
import signal
import MySQLdb
_connection = MySQLdb.connect(host='dummyhost', user='dummyuser', passwd='dummypasswd')
sigint_received = False
def handle_sigint(signal_number, frame):
global sigint_received
print 'handling SIGINT'
sigint_received = True
signal.signal(signal.SIGINT, handle_sigint)
while True:
if sigint_received:
print 'SIGINT was received - ignoring'
sigint_received = False
print 'Starting very long query'
_connection.cursor().execute('Select * from very_large_table')
print 'Finished very long query'
print 'sleeping for 5 seconds'
time.sleep(5)
如果我从命令行运行此脚本,然后从另一个 shell(使用sudo kill -INT $PID
)向它发送一个 SIGINT,我会得到以下输出:
Starting very long query
handling SIGINT
Finished very long query
sleeping for 5 seconds
SIGINT was received - ignoring
Starting very long query
Traceback (most recent call last):
File "reporting/scripts/infinity.py", line 24, in <module>
_connection.cursor().execute('Select * from agile_clicks_arrivals')
File "/home/chris.palmer/src/adverplex-src/redistributable/MySQLdb/MySQLdb/cursors.py", line 174, in execute
self.errorhandler(self, exc, value)
File "/home/chris.palmer/src/adverplex-src/redistributable/MySQLdb/MySQLdb/connections.py", line 36, in defaulterrorhandler
raise errorclass, errorvalue
_mysql_exceptions.OperationalError: (2006, 'MySQL server has gone away')
handling SIGINT
发送信号后立即输出,紧随其后Finished very long query
。所以很明显,execute
通话被打断了。我还用 gdb 调试了上面的脚本,当我发送 SIGINT 时,gdb 停止并给出以下输出:
(gdb) run infinity.py
Starting program: python infinity.py
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7ffff53ff700 (LWP 991)]
[Thread 0x7ffff53ff700 (LWP 991) exited]
Starting very long query
Program received signal SIGINT, Interrupt.
0x00007ffff7bcbd2d in read () from /lib/x86_64-linux-gnu/libpthread.so.0
(gdb) c
Continuing.
Finished very long query
sleeping for 5 seconds
Starting very long query
Traceback (most recent call last):
File "reporting/scripts/infinity.py", line 24, in <module>
_connection.cursor().execute('Select * from agile_clicks_arrivals')
File "/home/chris.palmer/src/adverplex-src/redistributable/MySQLdb/MySQLdb/cursors.py", line 174, in execute
self.errorhandler(self, exc, value)
File "/home/chris.palmer/src/adverplex-src/redistributable/MySQLdb/MySQLdb/connections.py", line 36, in defaulterrorhandler
raise errorclass, errorvalue
_mysql_exceptions.OperationalError: (2006, 'MySQL server has gone away')
[Inferior 1 (process 988) exited with code 01]
(gdb)
有趣的是,当我通过 gdb 运行 python 信号处理程序的打印行时,它不会执行,但它确实会中断 libpthread.so.0 中的执行。
MySqldb 包底层的 C 代码是否注册了它自己的信号处理程序?查询是否在单独的线程中运行,如果是,它是否接收信号以及 python 脚本?
有什么办法可以防止这种行为?我发现我可以修改信号处理程序以重新连接到 MySQL(通过MySQLdb.connect
再次调用。这将防止任何未来的查询失败,但它仍然会产生当前查询中止的效果。
谢谢克里斯