2月29日 ftpClient.listFiles问题
一、2020年2月29日问题描述
- 像往常一样,我们的一个定时任务从服务器A通过FTP方式连接到服务器B和服务器C下载日期为20200229的文件,loginin,cd 都是成功的,但是到执行FTPClient.listFiles()方法时返回的文件列表为null,同时从日志看,报了NPE(NullPointerException空指针异常)。但手动登录到服务器B和C时,发现想下载的文件是存在且有内容的!
- 从服务器B FTP到服务器A,通过FTPClient.listFiles()方法查看文件指定文件,开始都是可以正常获取到文件,但FTP上传覆盖了一次这个文件之后,再通过FTPClient.listFiles()方法获取该文件,就获取不到了。但是,同路径下的另一个文件,仍然可以获取到。
二、问题分析历程
- 与网络部确认,网络策略近一段时间都没有任何变动
- 与所有相关开发人员确认,近期没有上线操作
- 确认文件存在后,手动重复执行下载任务代码,结果仍然失败
- 手动FTP下载文件全部可以成功,于是先手动处理了线上文件,然后再安心去排查问题
- 通过 /usr/sbin/lsof 排查系统句柄占用情况,总句柄数正常(不过发现了其他一些应用占用句柄情况,后面也顺便处理掉了^_^)
- 把有问题的应用复制到其他机器,再次执行任务,还是出现同样的问题
以上正常排查都做完之后,问题仍然存在,于是换了一个思路去思考整个问题,今天是四年一遇的一天,会不会是工具包有BUG呢?然后总结关键字 2月29日 ftpClient.listFiles 找度娘问了一下,结果还真发现了一些端倪,因此开启了发现问题真因之旅。
三、问题解决过程
- 通过度娘的第一个发现,是2012年的一篇文章,截图如下:
重点是这篇文章下面的评论给了我更明确的方向: - 有了藤条,就能快速的顺藤摸瓜了,首先确认我们用的的确是commons-net-1.4.1.jar,话不多说,直接去官网下载了一个新版本的commons-net-3.1.jar
- 生产环境不能轻易实验,于是先在测试环境,用旧版本的包重现问题,然后换上新版本的包,果真,成功了,第一个解决方案出炉:升级jar包。
至此,问题找到了原因,也得到了解决。
但是,虽然知其然了,我们也要知其所以然。也就是接下来我们深入分析的内容。
四、深入探索
换jar包解决应用问题,只是第一步,第二部则是继续发扬程序猿的探索精神,一个问题,一定要探索到根源,到底了才是最爽的嘛,就跟警察办案一样,你知道罪犯是谁了,下一步要知道的是他为什么会犯这种错误。因此,探索之路仍然继续。
- commons-net-1.4.1.jar是apache的包,看到了apache的bug修改记录NET-188,截图如下:
-
新旧版本代码对比
1.4.1版本相关代码如下图:3.1版本代码如下图:
也就是由于1970年不是闰年,没有2月29日,默认没有年份的话,则认为2020年的2月29日是一个无效年份,而recentDateFormat定义是(this.setRecentDateFormat(“MMM d HH:mm”);)因此走默认年份的话报异常了,修改后,增加了严格解析日期方法(setLenient用于设置Calendar是否宽松解析字符串,如果为false,则严格解析;默认为true,宽松解析),然后将当前要解析的日期,带上年份,精确解析,就解决了此问题。源码的注释也表明了问题及解决方式。
- 分析完根本原因之后,结合生产上升级jar风险考虑,我们换了另一种解决方案,也就是在服务器执行了一下touch 修改了文件时间,这样再通过代码执行的时候就不会出现NPE了。
五、经验总结
处理问题,首先是要按照常规问题去考虑,如代码问题,服务器磁盘空间、服务器内存、网络联通性问题等,这些问题都排除后,就需要考虑特殊情况了,小到跨天问题,跨月问题,跨年问题,然后是闰年问题,再有就是年份是两位还是四位问题,更严重的还有千年虫(1999-2000),当然这个问题我们下半辈子是遇不到了(^-^)。
总之,一定要把问题精细化,然后是多总结多记录多分享。