Node.js函数执行顺序异常:删除Jar后复制提前执行问题
这绝对是经典的竞态条件问题!大概率你的删除函数没等操作真正完成就返回了
这种“刚复制的文件又被删除”的情况,90%以上是因为你的删除函数是异步/非阻塞执行的——调用它之后,函数立刻返回,程序直接跑去执行copyJars,但此时远程服务器上的删除操作还在后台跑,导致复制和删除操作撞在了一起。
你的删除函数可能存在这些问题:
- 没等待远程命令执行完毕:比如用SSH执行
rm命令时,只发送了命令就关闭连接/返回函数,没等服务器返回命令执行成功的信号。比如用paramiko(Python)、ssh2(Node.js)这类库时,没等回调触发、没获取退出码就结束函数。 - 忽略了删除操作的结果校验:就算你等了命令执行,也没确认文件真的被删掉了——比如远程文件系统有延迟、权限问题导致删除失败,但函数没抛出错误,直接继续执行复制。
- 用了后台进程执行删除:比如在远程服务器上用
rm &这种后台命令,删除操作本身就在后台跑,自然会和后续的复制操作并行。
怎么修复?
核心思路是:让删除函数必须等到远程删除操作100%完成,并且确认文件已不存在,才允许程序继续执行copyJars
1. 强制等待远程命令完成并校验退出码
举个Python paramiko的正确例子:
def delete_remote_jars(ssh_client, target_jars): # 执行删除命令 delete_cmd = f"rm -f {' '.join(target_jars)}" stdin, stdout, stderr = ssh_client.exec_command(delete_cmd) # 等待命令执行结束,获取退出状态码(0表示成功) exit_status = stdout.channel.recv_exit_status() if exit_status != 0: raise RuntimeError(f"删除远程jar失败: {stderr.read().decode('utf-8')}") # 额外校验:确认所有目标jar都已被删除 for jar_path in target_jars: check_cmd = f"test -f {jar_path} && echo exists || echo deleted" _, check_stdout, _ = ssh_client.exec_command(check_cmd) result = check_stdout.read().decode('utf-8').strip() if result != "deleted": raise RuntimeError(f"jar文件{jar_path}未被成功删除")
2. 如果你用的是异步语言(比如Node.js),要确保用await/回调等待操作完成
比如用ssh2-sftp-client的例子:
async function deleteRemoteJars(sftp, jarPaths) { for (const path of jarPaths) { // 等待删除操作完成 await sftp.delete(path); // 校验文件是否存在 try { await sftp.stat(path); throw new Error(`文件${path}删除失败,仍存在`); } catch (err) { // 正常情况:文件不存在时stat会报错,忽略即可 if (err.code !== 'ENOENT') throw err; } } } // 调用时必须await,确保删除完成再复制 await deleteRemoteJars(sftp, ['/path/to/old1.jar', '/path/to/old2.jar']); await copyJars(sftp, localJars, remoteDir);
3. 极端情况:分布式文件系统/缓存导致的延迟
如果你的远程存储是分布式文件系统(比如NFS),可能删除操作返回成功后,文件还会短暂存在几秒。这种情况可以加个短延迟(不推荐,但应急可用),或者用文件锁机制确保复制操作在删除完全完成后执行。
总结
你的删除函数最大的问题就是没有提供“操作已完成”的强保证,导致后续的复制操作和删除操作并行执行,出现了竞态条件。只要让删除函数同步等待操作完成,并校验结果,就能彻底解决这个问题。
内容的提问来源于stack exchange,提问作者pac
相关产品推荐
相关产品推荐

