FTP踩坑经历

摘要:定时任务,线程池,ftp文件传输协议

背景:最近在公司做一个项目需要给政府传输数据,数据是写入文件的,然后定时任务进行将文件发送给政府。

起因:线上发布之后发现定时任务执行了几次之后不在执行了,执行的几次都是正常的没有报错。

跟踪:查询线上日志,定时任务不在执行之后并无异常抛出,初步定位定时任务线程阻塞了。

既然猜测是线程阻塞导致的那么就说干就干

1.使用jps命令查询一下线上机器的java任务

jps是jdk提供的一个查看当前java进程的小工具可以看做是JavaVirtual Machine Process Status Tool的缩写

得到应用的pid:5034

2.使用jstack命令将当前应用的线程信息打印到/tmp/log.txt文件中

jstack -l5034>> /tmp/log.txtjstack 是JVM自带的Java堆栈跟踪工具用于打印出给定的java进程ID、core file、远程调试服务的Java堆栈信息.

3.得到线程堆栈信息后,使用grep命令定位具体线程

"pool-10-thread-1"#593 prio=5 os_prio=0 tid=0x00007f0895b29800 nid=0x77d runnable [0x00007f084023c000]java.lang.Thread.State:RUNNABLEatjava.net.SocketInputStream.socketRead0(Native Method)atjava.net.SocketInputStream.socketRead(SocketInputStream.java:116)atjava.net.SocketInputStream.read(SocketInputStream.java:171)atjava.net.SocketInputStream.read(SocketInputStream.java:141)atsun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)atsun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)atsun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)-locked <0x0000000643f03330> (a java.io.InputStreamReader)atjava.io.InputStreamReader.read(InputStreamReader.java:184)atjava.io.BufferedReader.fill(BufferedReader.java:161)atjava.io.BufferedReader.read(BufferedReader.java:182)-locked <0x0000000643f03330> (a java.io.InputStreamReader)atorg.apache.commons.net.io.CRLFLineReader.readLine(CRLFLineReader.java:58)-locked <0x0000000643f03330> (a java.io.InputStreamReader)atorg.apache.commons.net.ftp.FTP.__getReply(FTP.java:310)atorg.apache.commons.net.ftp.FTP.__getReply(FTP.java:290)atorg.apache.commons.net.ftp.FTP.sendCommand(FTP.java:479)atorg.apache.commons.net.ftp.FTP.sendCommand(FTP.java:552)atorg.apache.commons.net.ftp.FTP.sendCommand(FTP.java:601)atorg.apache.commons.net.ftp.FTP.pasv(FTP.java:952)atorg.apache.commons.net.ftp.FTPClient._openDataConnection_(FTPClient.java:755)atorg.apache.commons.net.ftp.FTPClient._storeFile(FTPClient.java:565)atorg.apache.commons.net.ftp.FTPClient.__storeFile(FTPClient.java:557)atorg.apache.commons.net.ftp.FTPClient.storeFile(FTPClient.java:1795)atcom.our.my.dep.schedule.job.tianjin.TianJinFilePushSchedule.send(TianJinFilePushSchedule.java:161)atsun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)atsun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)atsun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)atjava.lang.reflect.Method.invoke(Method.java:498)atorg.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:65)atorg.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)atorg.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81)atjava.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)atjava.util.concurrent.FutureTask.run(FutureTask.java:266)atjava.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)atjava.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)atjava.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)atjava.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)atjava.lang.Thread.run(Thread.java:748)

第27行显示此时线程处在发送文件过程,下面是具体的传输代码

161行正是ftp数据请求中的代码。

4.为了进一步验证线程是否是堵塞 我在2分钟后又导出了一份线程堆栈信息

"pool-10-thread-1"#593 prio=5 os_prio=0 tid=0x00007f0895b29800 nid=0x77d runnable [0x00007f084023c000]java.lang.Thread.State:RUNNABLEatjava.net.SocketInputStream.socketRead0(Native Method)atjava.net.SocketInputStream.socketRead(SocketInputStream.java:116)atjava.net.SocketInputStream.read(SocketInputStream.java:171)atjava.net.SocketInputStream.read(SocketInputStream.java:141)atsun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)atsun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)atsun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)-locked <0x0000000643f03330> (a java.io.InputStreamReader)atjava.io.InputStreamReader.read(InputStreamReader.java:184)atjava.io.BufferedReader.fill(BufferedReader.java:161)atjava.io.BufferedReader.read(BufferedReader.java:182)-locked <0x0000000643f03330> (a java.io.InputStreamReader)atorg.apache.commons.net.io.CRLFLineReader.readLine(CRLFLineReader.java:58)-locked <0x0000000643f03330> (a java.io.InputStreamReader)atorg.apache.commons.net.ftp.FTP.__getReply(FTP.java:310)atorg.apache.commons.net.ftp.FTP.__getReply(FTP.java:290)atorg.apache.commons.net.ftp.FTP.sendCommand(FTP.java:479)atorg.apache.commons.net.ftp.FTP.sendCommand(FTP.java:552)atorg.apache.commons.net.ftp.FTP.sendCommand(FTP.java:601)atorg.apache.commons.net.ftp.FTP.pasv(FTP.java:952)atorg.apache.commons.net.ftp.FTPClient._openDataConnection_(FTPClient.java:755)atorg.apache.commons.net.ftp.FTPClient._storeFile(FTPClient.java:565)atorg.apache.commons.net.ftp.FTPClient.__storeFile(FTPClient.java:557)atorg.apache.commons.net.ftp.FTPClient.storeFile(FTPClient.java:1795)atcom.our.my.dep.schedule.job.tianjin.TianJinFilePushSchedule.send(TianJinFilePushSchedule.java:161)atsun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)atsun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)atsun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)atjava.lang.reflect.Method.invoke(Method.java:498)atorg.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:65)atorg.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)atorg.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81)atjava.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)atjava.util.concurrent.FutureTask.run(FutureTask.java:266)atjava.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)atjava.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)atjava.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)atjava.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)atjava.lang.Thread.run(Thread.java:748)

同样的方法定位得到对应的线程,发现线程信息一模一样,由此可见线程的确是卡死在发送文件的过程了。

思考:既然问题已经定位到了,下面就是思考如何解决问题了

下面来看一下ftpClient初始化代码

privateFTPClientinitFtp(){try{FTPClient ftp =newFTPClient();// 设置连接超时(以毫秒为单位),该超时将传递给Socket对象的connect()方法。            ftp.setConnectTimeout(FTPConstants.FTP_CONNECT_TIMEOUT);// 设置从数据连接读取时使用的超时(以毫秒为单位)。 打开数据连接后,将立即设置此超时。            ftp.setDataTimeout(FTPConstants.FTP_DATA_TIMEOUT);            ftp.setControlEncoding(FTPConstants.CHARSET_UTF_8);            ftp.connect(host, port);            ftp.login(username, password);intreplyCode = ftp.getReplyCode();// 确定答复代码是否为肯定的完成答复if(!FTPReply.isPositiveCompletion(replyCode)) {                ftp.disconnect();log.error("登录失败:{}", replyCode);returnnull;            }// 文件类型为二进制            ftp.setFileType(FTPClient.BINARY_FILE_TYPE);// 被动模式 每次数据连接之前,ftp client告诉ftp server开通一个端口来传输数据 防止假卡死            ftp.enterLocalPassiveMode();// 创建目录切换目录(文件接收方)ftp.makeDirectory("/test");ftp.changeWorkingDirectory("/test");returnftp;}catch(Exception e) {log.error("ftp链接登录失败:{}", e.getMessage(), e);returnnull;        }    }

通过代码我们发现连接超时是设置了的啊,不应该在这里卡死的呀。

这里看一下官方解释:

FtpClient API

这里有个很重要的参数:

setSoTimeout()读取数据时阻塞链路的超时时间。

注意这里所说的内容:

如果超时到期,则尽管Socket仍然有效,但将引发java.net.SocketTimeoutException 。必须先启用该选项,然后才能执行阻止操作。超时时间必须> 0 。零超时被解释为无限超时。

也就是说如果不设置,可能会在网络波动时阻塞,至此无限时阻塞在此。。。

也就是连接成功之后需要设置soTimeOut

于是在此基础中我进行了改造:

文件传输时:

提取一下几个比较重要的参数

// 设置连接超时(以毫秒为单位),该超时将传递给Socket对象的connect()方法ftp.setConnectTimeout(FTPConstants.FTP_CONNECT_TIMEOUT)// 设置从数据连接读取时使用的超时(以毫秒为单位)。打开数据连接后,将立即设置此超时ftp.setDataTimeout(FTPConstants.FTP_DATA_TIMEOUT);// 连接ftp服务器ftp.connect(host, port);// 登陆ftp服务器ftp.login(username, password);// 设置字符集UTF-8ftp.setControlEncoding(FTPConstants.CHARSET_UTF_8);// 文件类型为二进制ftp.setFileType(FTPClient.BINARY_FILE_TYPE);// 被动模式 每次数据连接之前,ftp client告诉ftp server开通一个端口来传输数据 防止假卡死ftp.setControlKeepAliveTimeout(FTPConstants.CONTROL_KEEP_ALIVE_TIMEOUT);// 以毫秒为单位设置当前打开的连接的超时时间。仅在connect()打开connect()后才调用此方法。ftp.setSoTimeout(10000);

至此上线后不再出现阻塞现象。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容