FTP下载大文件后命令无法正常退出
下载数据库备份200G遇到的问题
前端时间在DevOps平台的开发中遇到了需要下载200G超大数据库备份文件的需求:不论是在java程序里面还是FTP的命令行脚本里面都遇到了文件下载完成后,当前代码不能退出的问题。纠结与测试良久发现这是FTP多端口通信导致的问题。
什么是FTP主被动模式
首先简单理解一下什么是FTP主被动模式:默认情况下FTP协议使用21端口通信,但这是控制端口,只用来传输命令,主动模式下,FTP启用20端口(可以配置,不是必须20)连接客户端的随机端口传输真正的数据,如果在公网上或者长距离VPN,服务器主动模式----受到NAT等影响,往往不能穿透防火墙等,因此我们一般使用被动模式。被动模式下,服务器主动开启一个数据端口,被动等待客户端的连接,因为客户端始终主动发起连接请求,所以不受NAT等影响,能穿透防火墙。
为什么文件传输完后,命令不能退出
因为FTP的多端口通信,数据文件只在数据端口传输,假设数据文件很大需要传输一整天才能完成,那么数据端口在这个一整天都很忙碌,而到控制端口的网络连接则一直空闲。我们知道长距离网络会间隔很多路由器,有很多都不能一直保持网络连接,当某一条链路很久没有数据传输,那么这个链路会被这些中间网络设备断掉。等真正的数据传输完成,客户端打算通过控制端口告诉服务器,文件下载完毕,却发现控制端口的链路已经不能使用,所以程序出现异常(有些客户端挂起,有些抛出异常)。
怎么解决
保持网络连接的活性,心跳包是惯用手法,如果是大家使用Apache的commons-net包中的java FTP客户端,其中已经预制了保持控制端口心跳的功能:
// 设置控制端口的响应超时
this.ftpClient.setControlKeepAliveReplyTimeout(5000);
// 重要,每隔15S向控制端口发送心跳数据,保证控制端口的活性
this.ftpClient.setControlKeepAliveTimeout(15);
有了这2行代码,我发现下载200G,300G文件,无论是下载了一天还是1天半,程序都能正常工作。
当然如果使用了其他客户端,没有提供对应的心跳函数,则需要自己在下载数据之前新建一个线程,在这个线程不停的向控制端口发送空指令(NoOp指令)。