一、問題起源及詳細異常
近日寫程序定時任務調Hadoop MR程序,然后生成報表,發送郵件,當時起了兩個任務A和B,調MR程序之前,會操作hdfs(讀寫都有),任務A每天一點跑,任務B每十分鍾跑一次,B任務不會調用MR程序,純粹采集數據。結果第一天就發現任務A沒有發送郵件,於是乎查日志,異常信息如下
java.io.IOException: Failed on local exception: java.io.InterruptedIOException: Interrupted while waiting for IO on channel java.nio.channels.SocketChannel[connected local=/10.1.23.249:52305 remote=/10.1.23.249:9000]. 60000 millis timeout left.; Host Details : local host is: "hadoop-alone-test/10.1.23.249"; destination host is: "hadoop-alone-test":9000; at org.apache.hadoop.net.NetUtils.wrapException(NetUtils.java:776) at org.apache.hadoop.ipc.Client.call(Client.java:1479) at org.apache.hadoop.ipc.Client.call(Client.java:1412) at org.apache.hadoop.ipc.ProtobufRpcEngine$Invoker.invoke(ProtobufRpcEngine.java:229) at com.sun.proxy.$Proxy109.getListing(Unknown Source) ........
當時有點懵,不知道為什么出現這種IO被中斷。於是乎,我在job控制台再調一下,並沒有出現錯誤。本來想看看源碼,但是領導安排了個比較緊急的任務。忙了幾天,期間沒有仔細去管,我只是注意了下每天的郵件,發現還是有的時候會成功的。忙完了任務,趕緊來排查,再看日志,發現報的錯不止這一種,下面列出其他錯誤
java.io.IOException: Failed on local exception: java.io.IOException; Host Details : local host is: "hadoop-alone-test/10.1.23.249"; destination host is: "hadoop-alone-test":9000; at org.apache.hadoop.net.NetUtils.wrapException(NetUtils.java:776) at org.apache.hadoop.ipc.Client.call(Client.java:1479) at org.apache.hadoop.ipc.Client.call(Client.java:1412) at org.apache.hadoop.ipc.ProtobufRpcEngine$Invoker.invoke(ProtobufRpcEngine.java:229) at com.sun.proxy.$Proxy109.getFileInfo(Unknown Source) ...........
java.io.IOException: Filesystem closed at org.apache.hadoop.hdfs.DFSClient.checkOpen(DFSClient.java:808) at org.apache.hadoop.hdfs.DFSClient.listPaths(DFSClient.java:2083) at org.apache.hadoop.hdfs.DistributedFileSystem$DirListingIterator.<init>(DistributedFileSystem.java:944) at org.apache.hadoop.hdfs.DistributedFileSystem$DirListingIterator.<init>(DistributedFileSystem.java:927) at org.apache.hadoop.hdfs.DistributedFileSystem$19.doCall(DistributedFileSystem.java:872) at org.apache.hadoop.hdfs.DistributedFileSystem$19.doCall(DistributedFileSystem.java:868) at org.apache.hadoop.fs.FileSystemLinkResolver.resolve(FileSystemLinkResolver.java:81) ................
java.io.IOException: The client is stopped at org.apache.hadoop.ipc.Client.getConnection(Client.java:1507) at org.apache.hadoop.ipc.Client.call(Client.java:1451) at org.apache.hadoop.ipc.Client.call(Client.java:1412) at org.apache.hadoop.ipc.ProtobufRpcEngine$Invoker.invoke(ProtobufRpcEngine.java:229) at com.sun.proxy.$Proxy109.getFileInfo(Unknown Source) at org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolTranslatorPB.getFileInfo(ClientNamenodeProtocolTranslatorPB.java:771) at sun.reflect.GeneratedMethodAccessor69.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498)
就是上述這四個錯誤,都是和io通信有關,然后都出現在操作Hadoop的FileSystem那段代碼,且任務A和B都不等量的出現過,而且出現在凌晨一點,兩個線程同時操作的時候,所以問題很明確,出在並發上面,聯系到上面的FileSystem被關閉,我代碼確實是調用了close方法
於是查看了一下源碼,創建FileSystem的時候有如下代碼
String disableCacheName = String.format("fs.%s.impl.disable.cache", scheme); return conf.getBoolean(disableCacheName, false) ? createFileSystem(uri, conf) : CACHE.get(uri, conf);
創建FileSystem的時候讀取配置"fs.%s.impl.disable.cache",默認為false,所以第二次走了緩存, FileSystem的URI相同的話,一定只創建一個FileSystem
涉及到多線程訪問,而線程B已經調用了filesystem.close()方法,這個時候線程A還在操作filesystem,所以報錯上面種種異常
二、解決辦法
1、代碼同步(這里就不貼代碼,用synchronized、lock這些都行)
2、禁用FileSystem緩存
代碼里
Configuration conf = new Configuration(); conf.set("fs.hdfs.impl.disable.cache", "true");
core-site.xml文件里面配置(二者選其一)
<property>
<name>fs.hdfs.impl.disable.cache</name>
<value>true</value>
</property>