需求:做一個windows服務,實現從ftp伺服器實時下載或者更新檔案到本地磁碟。
功能挺簡單的。直接寫個ftp工具類用定時器跑就能搞定,那我為什麼不用呢?
別問,問就是我無聊啊,然後研究一下Flume打發時間。哈哈~
一、Flume部分
Source元件和Sink元件用的都是第三方。
source元件:https://github.com/keedio/flume-ftp-source
Sink元件用的誰的目前已經找不到了,網上搜到了一個升級版的。
File sink元件:https://github.com/huyanping/flume-sinks-safe-roll-file-sink
因為一些個性化的需求,所以我對他們原始碼做了些變動。
2019/02/15: 新增了採集至HDFS的sink.因為flume自帶的hdfs sink不支援高可用環境。所以依然對原始碼做了些改動
具體修改:
HDFSEventSink.java
1 public void configurateHA(Context context) { 2 String nns = Preconditions.checkNotNull( 3 context.getString("nameNodeServer"), "nameNodeServer is required"); 4 hdfsEnv.set("fs.defaultFS", "hdfs://" + nns); 5 hdfsEnv.set("dfs.nameservices", nns); 6 hdfsEnv.set("fs.hdfs.impl","org.apache.hadoop.hdfs.DistributedFileSystem"); 7 8 Map<String, String> servers = context.getSubProperties("server."); 9 List<String> serverNames = Lists.newArrayListWithExpectedSize(servers.size()); 10 11 servers.forEach((key, value) -> { 12 String name = Preconditions.checkNotNull( 13 key, "server.name is required"); 14 String[] hostAndPort = value.split(":"); 15 Preconditions.checkArgument(2 == hostAndPort.length, "hdfs.server is error."); 16 17 hdfsEnv.set(String.format("dfs.namenode.rpc-address.%s.%s", nns, name), value); 18 serverNames.add(name); 19 }); 20 21 hdfsEnv.set(String.format("dfs.ha.namenodes.%s", nns), Joiner.on(",").join(serverNames)); 22 hdfsEnv.set(String.format("dfs.client.failover.proxy.provider.%s", nns), 23 "org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider"); 24 hdfsEnv.setBoolean("fs.automatic.close", false); 25 }
Flume偽叢集配置
1 # Describe the sink 2 a1.sinks.k1.type = com.syher.flume.sink.hdfs.HDFSEventSink 3 # 目標路徑 4 a1.sinks.k1.hdfs.path = hdfs://zoeBigData:8020/flume-wrapper/pdf-201901301740 5 a1.sinks.k1.hdfs.fileType = DataStream 6 a1.sinks.k1.hdfs.useLocalTimeStamp = true 7 #a1.sinks.k1.hdfs.batchSize = 3000 8 #a1.sinks.k1.hdfs.rollSize = 1024000000 9 #a1.sinks.k1.hdfs.rollCount = 0
Flume高可用配置檔案
1 # Describe the sink 2 a1.sinks.k1.type = com.syher.flume.sink.hdfs.HDFSEventSink 3 # hdfs伺服器是否高可用 4 a1.sink.k1.hdfs.HA = true 5 # 目標路徑 6 a1.sinks.k1.hdfs.path = hdfs://zoeBigData/flume-wrapper/pdf-201901301805 7 a1.sinks.k1.hdfs.nameNodeServer = zoeBigData 8 a1.sink.k1.hdfs.server.nn1 = master:9000 9 a1.sink.k1.hdfs.server.nn2 = slave1:9000 10 a1.sinks.k1.hdfs.fileType = DataStream 11 a1.sinks.k1.hdfs.useLocalTimeStamp = true 12 #a1.sinks.k1.hdfs.batchSize = 3000 13 #a1.sinks.k1.hdfs.rollSize = 0 14 #a1.sinks.k1.hdfs.rollInterval = 60 15 #a1.sinks.k1.hdfs.rollCount = 0
具體程式碼參考:https://gitee.com/syher/spring-boot-project/tree/master/spring-boot-flume
Ftp-Source元件的關鍵技術是Apache FtpClient,而TailDir-sink則用的RandomAccessFile。
Junit測試類我已經寫好了,如果不想安裝服務又有興趣瞭解的朋友,可以自己改下配置跑一下看看。
二、JSW服務部分
用的java service wrapper把java程式做成了windows服務。
工具包已經上傳到百度雲:https://pan.baidu.com/s/1mtpZ8PR2lFOStFvbGRwD8A 提取碼: rkwf
解壓後在conf目錄可以看到兩個配置檔案。一個是flume的,一個是jsw的。
bin目錄裡面是一些裝卸啟停的批命令。
lib目錄裡面有專案執行依賴的jar包。
lib.d目錄沒啥用,是我備份了從flume拷出來的一些無用的jar包。可刪。
具體的配置和用法可以看壓縮包裡的使用說明文件。
注意,jsw的logfile的日誌級別最好指定ERROR級別的,不然聽說、可能會造成記憶體不足。
三、採集結果
可以看到,檔案採集效率還是很穩的。一分鐘不到就搞定了。
hdfs採集結果:
四、問題記錄
hdfs採集時,用junit測試沒有問題,用jsw測試一直沒動靜,也不報錯。然後開了遠端除錯。除錯方法:
在wrapper.conf中加入如下程式碼:
1 # remote debug 2 #wrapper.java.additional.1=-Xdebug 3 #wrapper.java.additional.2=-Xnoagent 4 #wrapper.java.additional.3=-Djava.compiler=NONE 5 6 #wrapper.java.additional.4=-Xrunjdwp:transport=dt_socket,server=y,address=5005,suspend=y
遠端聯調以後,終於有拋異常了
java.lang.NoClassDefFoundError: Could not initialize class org.apache.commons.lang.SystemUtils
找了下lib資料夾,裡面確實有這個包,也沒衝突。不得已在SystemUtils類裡面打了個斷電一步一步除錯。最後發現是java.version的問題。
jdk10版本在下面程式碼的第5行報錯了。因為JAVA_VERSION_TRIMMED值是10長度只有兩位導致了越界。
1 private static float getJavaVersionAsFloat() { 2 if (JAVA_VERSION_TRIMMED == null) { 3 return 0f; 4 } 5 String str = JAVA_VERSION_TRIMMED.substring(0, 3); 6 if (JAVA_VERSION_TRIMMED.length() >= 5) { 7 str = str + JAVA_VERSION_TRIMMED.substring(4, 5); 8 } 9 try { 10 return Float.parseFloat(str); 11 } catch (Exception ex) { 12 return 0; 13 } 14 }
由於之前為了學習jdk10新特性,搞了個jdk8,jdk10雙環境,然後換回jdk8的時候是直接改的JAVA_HOME。
所以在dos視窗敲了下java -version 輸出的是1.8_xxxxx。
但是不知道為啥java的System.getProperties("java.version")獲取的依舊是10。
然後重新檢查了JDK的J三個環境變數。
最後發現把PATH中的一個C:\ProgramData\Oracle\Java\javapath;路徑去掉就沒問題了。
1 C:\ProgramData\Oracle\Java\javapath;G:\syhenian\oracle_11_21;E:\Program Files\Python36\Scripts\;E:\Program Files\Python36\;%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\;%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;%M2_HOME%\bin;C:\Program Files (x86)\Intel\OpenCL SDK\2.0\bin\x86;C:\Program Files (x86)\Intel\OpenCL SDK\2.0\bin\x64;%CURL_HOME%\bin;E:\Program Files\scala\bin;%SCALA_HOME%\bin;%SCALA_HOME%\jre\bin;E:\Program Files\Python36;C:\Program Files (x86)\Git\cmd;%GRADLE_HOME%\bin;%ANDROID_HOME%\tools; 2 %ANDROID_HOME%\platform-tools;%ANDROID_HOME%\build-tools\27.0.3;E:\Program Files\7-Zip;F:\Program Files\nodejs\;%HADOOP_HOME%\bin;
總結:common-lang包不知道升級管不管用,反正flume自帶的這個包暫時是不支援jdk10的。有需要的可以自己改原始碼。