修改Sqoop1.4.6源碼實現--fields-terminated-by選項支持多字節分隔符
最近項目中需要使用sqoop實現oracle與hdfs的數據交換,從oracle數據表導入到hdfs集群,以及把hdfs數據導出到oracle數據表。客戶要求Hdfs文本文件中,字段分隔符必須是“|@|”。然而發現sqoop的—fields-terminated-by選項只能支持單字節分隔符,無法支持多字節分隔符,難以滿足要求。上網搜索了一下,並沒有太多公開資料能較好的解決這個問題。這篇文章(http://www.cnblogs.com/YFYkuner/p/3748495.html)是通過修改codegen生成的Java代碼,再編譯實現多字節分隔。不過這種辦法還不夠通用,並不能完全滿足我的需求。但通過這篇文章可以學習到,分隔符主要是在DelimiterSet這個類定義。所以,我打算修改sqoop的源代碼,使其支持多字節分隔。
使用sqoop1.4.6,下載地址:http://www.apache.org/dyn/closer.lua/sqoop/1.4.6
1. Sqoop工作原理
Sqoop在進行導入導出任務時,都會先自動生成一個java文件,並編譯打成jar包,再提交到hadoop集群真正執行MapReduce任務。可以先閱讀sqoop自動生成的java文件,看看其中數據導入導出的接口,以及分隔符如何被使用。這樣可以有一個整體的印象。參考http://www.cnblogs.com/YFYkuner/p/3748495.html
2. 核心代碼修改
2.1. 分隔符DelimiterSet的修改
首先了解一下分隔符org.apache.sqoop.lib.DelimiterSet的定義。
第一個字段field就是 –fields-terminated-by 指定的字段分隔符。源文件中變量類型是char,只能支持單字節。因此,首先需要將其修改為String類型,以便支持多字節分隔。
同時,DelimiterSet類的成員變量fieldDelim也需要修改為String類型。
2.2. 從Oracle導入Hdfs的修改
Sqoop主要是使用org.apache.sqoop.orm.ClassWriter類來實現從Oralce導入數據到Hdfs的。ClassWriter通過generateClassForColumns函數自動生成對應的java代碼,這些被生成的代碼才是真正在hadoop集群上執行。
其中導出到hdfs文本文件這段代碼,具體是generateToString函數生成。
由於我們把DelimiterSet的fieldDelim字段類型從char改成了String,因此ClassWriter自動生成的java源碼,也需要對應的修改。所以,需要修改如下:
2.3. 從Hdfs導出到Oracle的修改
從Hdfs導出到Oracle數據表,主要改動在於對Hdfs文本記錄的解析,能夠識別多字節分隔符。文本解析是通過org.apache.sqoop.lib.RecordParser的parseRecord方法來實現的。
源碼是通過一個有限狀態機來解析文本。我這里用String的split方法做了個最簡單的處理來替換。
2.4. 解析--fields-terminated-by參數
sqoop通過命令行參數--fields-terminated-by設置分隔符。所以需要在org.apahe.sqoop.tool.BaseSqoopTool中設置Input和Output的分隔符,修改為字符串類型。修改如下:
2.5. 變更源碼文件一覽
由於修改了變量類型,以及相關函數參數,為了解決編譯錯誤,還需要修改一些其它java文件。所有被修改的java文件如下。
3. 編譯和打包
在ubuntu下使用ant編譯和打包。參考sqoop的README,我安裝的是jdk1.8.0_121和ant1.10.1。
== Compiling Sqoop
Compiling Sqoop requires the following tools:
* Apache ant (1.7.1) * Java JDK 1.6
Additionally, building the documentation requires these tools:
* asciidoc * make * python 2.5+ * xmlto * tar * gzip
To compile Sqoop, run +ant package+. There will be a fully self-hosted build provided in the +build/sqoop-(version)/+ directory. |
執行ant package命令即可。
在build下得到構建產物。
BTW:這里我指定了hadoop版本是2.6.1,只要在build.xml中修改版本號即可。
4. 測試
從Oracle導入Hdfs:
sqoop import --connect jdbc:oracle:thin:@ip:port --username username --password password --target-dir /hdfs/cluster/sqoop/dir --table oracletable --fields-terminated-by \\\\\|@\\\\\|
從Hdfs導出Oracle:
sqoop export --connect jdbc:oracle:thin:@ip:port --username username --password password --table oracletable --fields-terminated-by \\\\\|@\\\\\| --export-dir /hdfs/cluster/sqoop/dir
驗證發現hdfs文件中字段分隔符確實是“|@|”,測試通過。
最后拋出一個問題供思考:分隔符是“|@|”,但為什么測試命令中--fields-terminated-by 指定的卻是“\\\\\|@\\\\\|”呢?