如何将非阻塞 stderr 捕获添加到线程化的 popen
how to add non blocking stderr capture to threaded popen's
我有一个 python 3 脚本,我用它来备份和加密 mysqldump 文件,我对加密和压缩后 67gb 的数据库有一个特殊的问题。
mysqldump 正在输出错误代码 3,所以我想捕获实际的错误消息,因为这可能意味着几件事。
随机的是备份文件的大小正确,所以不确定错误是什么意思。它曾在此数据库上工作过...
代码如下所示,当 return 代码对于 p1 和 p2 都不是 0 时,我非常感谢有关如何添加 stderr 的非阻塞捕获的一些帮助。
此外,如果我做错了什么明显的错误,请务必告诉我,因为我想确保这是一个可靠的过程。它在我的 15gb 压缩数据库上运行良好。
def dbbackup():
while True:
item = q.get()
#build up folder structure, daily, weekly, monthy & project
genfile = config[item]['DBName'] + '-' + dateyymmdd + '-'
genfile += config[item]['PubKey'] + '.sql.gpg'
if os.path.isfile(genfile):
syslog.syslog(item + ' ' + genfile + ' exists, removing')
os.remove(genfile)
syslog.syslog(item + ' will be backed up as ' + genfile)
args = ['mysqldump', '-u', config[item]['UserNm'],
'-p' + config[item]['Passwd'], '-P', config[item]['Portnu'],
'-h', config[item]['Server']]
args.extend(config[item]['MyParm'].split())
args.append(config[item]['DBName'])
p1 = subprocess.Popen(args, stdout=subprocess.PIPE)
p2 = subprocess.Popen(['gpg', '-o', genfile, '-r',
config[item]['PubKey'], '-z', '9', '--encrypt'], stdin=p1.stdout)
p2.wait()
if p2.returncode == 0:
syslog.syslog(item + ' encryption successful')
else:
syslog.syslog(syslog.LOG_CRIT, item + ' encryption failed '+str(p2.returncode))
p1.terminate()
p1.wait()
if p1.returncode == 0:
#does some uploads of the file etc..
else:
syslog.syslog(syslog.LOG_CRIT, item + ' extract failed '+str(p1.returncode))
q.task_done()
def main():
db2backup = []
for settingtest in config:
db2backup.append(settingtest)
if len(db2backup) >= 1:
syslog.syslog('Backups started')
for database in db2backup:
q.put(database)
syslog.syslog(database + ' added to backup queue')
q.join()
syslog.syslog('Backups finished')
q = queue.Queue()
config = configparser.ConfigParser()
config.read('backup.cfg')
backuptype = 'daily'
dateyymmdd = datetime.datetime.now().strftime('%Y%m%d')
for i in range(2):
t = threading.Thread(target=dbbackup)
t.daemon = True
t.start()
if __name__ == '__main__':
main()
简化您的代码:
- 避免不必要的全局变量,而是将参数传递给相应的函数
- 避免重新实现线程池(这会损害可读性并且会错过多年来积累的便利功能)。
捕获stderr的最简单方法是使用stderr=PIPE
和.communicate()
(阻塞调用):
#!/usr/bin/env python3
from configparser import ConfigParser
from datetime import datetime
from multiprocessing.dummy import Pool
from subprocess import Popen, PIPE
def backup_db(item, conf): # config[item] == conf
"""Run `mysqldump ... | gpg ...` command."""
genfile = '{conf[DBName]}-{now:%Y%m%d}-{conf[PubKey]}.sql.gpg'.format(
conf=conf, now=datetime.now())
# ...
args = ['mysqldump', '-u', conf['UserNm'], ...]
with Popen(['gpg', ...], stdin=PIPE) as gpg, \
Popen(args, stdout=gpg.stdin, stderr=PIPE) as db_dump:
gpg.communicate()
error = db_dump.communicate()[1]
if gpg.returncode or db_dump.returncode:
error
def main():
config = ConfigParser()
with open('backup.cfg') as file: # raise exception if config is unavailable
config.read_file(file)
with Pool(2) as pool:
pool.starmap(backup_db, config.items())
if __name__ == "__main__":
main()
注意:如果 gpg
过早死亡,则无需调用 db_dump.terminate()
:mysqldump
在尝试向已关闭的 gpg.stdin
写入内容时死亡。
如果配置中有大量项目,那么您可以使用 pool.imap()
而不是 pool.starmap()
(the call should be modified slightly)。
为了稳健性,包装 backup_db()
函数以捕获并记录所有异常。
我有一个 python 3 脚本,我用它来备份和加密 mysqldump 文件,我对加密和压缩后 67gb 的数据库有一个特殊的问题。 mysqldump 正在输出错误代码 3,所以我想捕获实际的错误消息,因为这可能意味着几件事。 随机的是备份文件的大小正确,所以不确定错误是什么意思。它曾在此数据库上工作过...
代码如下所示,当 return 代码对于 p1 和 p2 都不是 0 时,我非常感谢有关如何添加 stderr 的非阻塞捕获的一些帮助。
此外,如果我做错了什么明显的错误,请务必告诉我,因为我想确保这是一个可靠的过程。它在我的 15gb 压缩数据库上运行良好。
def dbbackup():
while True:
item = q.get()
#build up folder structure, daily, weekly, monthy & project
genfile = config[item]['DBName'] + '-' + dateyymmdd + '-'
genfile += config[item]['PubKey'] + '.sql.gpg'
if os.path.isfile(genfile):
syslog.syslog(item + ' ' + genfile + ' exists, removing')
os.remove(genfile)
syslog.syslog(item + ' will be backed up as ' + genfile)
args = ['mysqldump', '-u', config[item]['UserNm'],
'-p' + config[item]['Passwd'], '-P', config[item]['Portnu'],
'-h', config[item]['Server']]
args.extend(config[item]['MyParm'].split())
args.append(config[item]['DBName'])
p1 = subprocess.Popen(args, stdout=subprocess.PIPE)
p2 = subprocess.Popen(['gpg', '-o', genfile, '-r',
config[item]['PubKey'], '-z', '9', '--encrypt'], stdin=p1.stdout)
p2.wait()
if p2.returncode == 0:
syslog.syslog(item + ' encryption successful')
else:
syslog.syslog(syslog.LOG_CRIT, item + ' encryption failed '+str(p2.returncode))
p1.terminate()
p1.wait()
if p1.returncode == 0:
#does some uploads of the file etc..
else:
syslog.syslog(syslog.LOG_CRIT, item + ' extract failed '+str(p1.returncode))
q.task_done()
def main():
db2backup = []
for settingtest in config:
db2backup.append(settingtest)
if len(db2backup) >= 1:
syslog.syslog('Backups started')
for database in db2backup:
q.put(database)
syslog.syslog(database + ' added to backup queue')
q.join()
syslog.syslog('Backups finished')
q = queue.Queue()
config = configparser.ConfigParser()
config.read('backup.cfg')
backuptype = 'daily'
dateyymmdd = datetime.datetime.now().strftime('%Y%m%d')
for i in range(2):
t = threading.Thread(target=dbbackup)
t.daemon = True
t.start()
if __name__ == '__main__':
main()
简化您的代码:
- 避免不必要的全局变量,而是将参数传递给相应的函数
- 避免重新实现线程池(这会损害可读性并且会错过多年来积累的便利功能)。
捕获stderr的最简单方法是使用stderr=PIPE
和.communicate()
(阻塞调用):
#!/usr/bin/env python3
from configparser import ConfigParser
from datetime import datetime
from multiprocessing.dummy import Pool
from subprocess import Popen, PIPE
def backup_db(item, conf): # config[item] == conf
"""Run `mysqldump ... | gpg ...` command."""
genfile = '{conf[DBName]}-{now:%Y%m%d}-{conf[PubKey]}.sql.gpg'.format(
conf=conf, now=datetime.now())
# ...
args = ['mysqldump', '-u', conf['UserNm'], ...]
with Popen(['gpg', ...], stdin=PIPE) as gpg, \
Popen(args, stdout=gpg.stdin, stderr=PIPE) as db_dump:
gpg.communicate()
error = db_dump.communicate()[1]
if gpg.returncode or db_dump.returncode:
error
def main():
config = ConfigParser()
with open('backup.cfg') as file: # raise exception if config is unavailable
config.read_file(file)
with Pool(2) as pool:
pool.starmap(backup_db, config.items())
if __name__ == "__main__":
main()
注意:如果 gpg
过早死亡,则无需调用 db_dump.terminate()
:mysqldump
在尝试向已关闭的 gpg.stdin
写入内容时死亡。
如果配置中有大量项目,那么您可以使用 pool.imap()
而不是 pool.starmap()
(the call should be modified slightly)。
为了稳健性,包装 backup_db()
函数以捕获并记录所有异常。