Python多进程Pool并行rsync备份脚本功能正常但报异常错误求助
问题根源分析与优化方案
首先,咱们来拆解你遇到的错误和脚本的问题:
为什么会出现change_dir failed错误?
- 并行执行顺序无保障:
multiprocessing.Pool会并行处理所有任务,但任务的执行顺序是随机的。比如同步/Folder/New_folder-A/3A.txt的进程可能先启动,这时候/dest/Folder/New_folder-A目录还没被创建(因为同步New_folder-A目录的进程可能还没执行),rsync找不到目标目录就会报错。 - 重复同步任务冗余:你的
get_pathlist函数同时添加了「目录」和「目录下的所有文件」到任务列表。这意味着rsync既要同步整个目录(包含所有子内容),又要单独同步每个文件。虽然最终目录同步会成功并覆盖文件同步的失败,但这种重复工作不仅浪费资源,还会触发大量不必要的错误日志。
为什么最终备份结果是正确的?
因为你同步目录的rsync任务(比如同步New_folder-A)会递归同步目录下的所有文件,即使单个文件的同步任务失败了,后续的目录同步还是会把文件正确复制到目标位置,所以最终结果看起来没问题,但过程中的错误确实存在风险(比如如果目录同步任务先失败,文件同步也失败,就会丢失数据)。
优化方案(两种选择)
方案1:只同步源目录的直接子项(推荐,最简洁高效)
rsync的-a参数已经包含递归同步(-r),所以完全没必要单独同步每个文件和子目录。只需要同步源目录下的直接子文件/目录,rsync会自动处理所有子内容,既减少任务数量,又从根源避免目录不存在的问题。
修改get_pathlist函数:
def get_pathlist(folder): pathlist = [] # 仅获取源目录下的直接子项,让rsync递归处理内部内容 for item in os.listdir(folder): # 直接子项的相对路径为空字符串,项目名为item本身 pathlist.append(("", item)) return pathlist
这样每个任务只同步source/下的一个直接子项到destination/,rsync会自动创建目标目录并递归同步所有子内容,既高效又不会有错误日志。
方案2:保留单个文件/目录同步,但解决顺序与目录创建问题
如果必须坚持同步每个单独的文件和目录,可以通过排序保证父目录优先执行+让rsync自动创建目标目录来解决错误:
- 修改
get_pathlist,按目录深度排序,确保父目录先被处理:
def get_pathlist(folder): pathlist = [] for root, dirs, files in os.walk(folder): # 计算当前路径相对于源目录的深度(根目录深度为0) depth = root[len(folder):].count(os.sep) for f in files: path = root[len(folder):] pathlist.append((depth, path, f)) for d in dirs: path = root[len(folder):] pathlist.append((depth, path, d)) # 按深度从小到大排序,父目录优先执行 pathlist.sort(key=lambda x: x[0]) # 转换回原格式的(path, item)元组 return [(item[1], item[2]) for item in pathlist]
- 修改
backup函数的rsync命令,添加--mkpath参数(rsync 3.0.0+支持),让rsync自动创建不存在的目标目录:
def backup(path): src = os.path.join(source, path[0], path[1]) dest = os.path.join(destination, path[0]) # 添加--mkpath自动创建目标目录 subprocess.call(['rsync', '-azq', '--mkpath', src, dest])
额外优化建议
- 去掉
maxtasksperchild=1:这个参数会让每个进程只处理一个任务就销毁,增加进程创建/销毁的开销。除非你有内存泄漏的问题,否则建议删除该参数,让进程复用处理多个任务,提升效率。 - 可以考虑用
subprocess.check_call替代subprocess.call:这样如果rsync执行失败会抛出异常,方便你捕获并处理错误(比如重试),而不是默默忽略。
内容的提问来源于stack exchange,提问作者Xander
相关产品推荐
相关产品推荐

