郭慶
(京華信息科技股份有限公司)
企業(yè)級(jí)應(yīng)用指的是支持企業(yè)、事業(yè)單位或者政府等機(jī)構(gòu)各項(xiàng)業(yè)務(wù)運(yùn)作的軟件系統(tǒng)[1]。企業(yè)級(jí)應(yīng)用主要負(fù)責(zé)顯示、操作和存儲(chǔ)大量復(fù)雜數(shù)據(jù),并對(duì)這些數(shù)據(jù)進(jìn)行支持和自動(dòng)化[2]。隨著社會(huì)的進(jìn)步和網(wǎng)絡(luò)技術(shù)的發(fā)展,眾多企業(yè)和組織為了使業(yè)務(wù)能夠與供應(yīng)商、合作伙伴乃至最終用戶更緊密地聯(lián)系在一起,紛紛將企業(yè)級(jí)應(yīng)用推向互聯(lián)網(wǎng)。但面對(duì)海量的業(yè)務(wù)數(shù)據(jù)處理時(shí),傳統(tǒng)企業(yè)級(jí)應(yīng)用架構(gòu)在擴(kuò)展性上遇到了瓶頸。近年迅速發(fā)展成熟起來的Hadoop平臺(tái)以其高效和易擴(kuò)展等特性受到高度關(guān)注,將企業(yè)級(jí)應(yīng)用遷移到Hadoop平臺(tái)成為應(yīng)對(duì)海量數(shù)據(jù)處理技術(shù)挑戰(zhàn)的新思路。本文圍繞Java EE企業(yè)級(jí)應(yīng)用遷移到Hadoop平臺(tái)展開研究,分析比較各種技術(shù)方法,并根據(jù)實(shí)際應(yīng)用情況提出相應(yīng)的遷移策略。
Java平臺(tái)企業(yè)版(Java Platform Enterprise Edition,Java EE),是Sun公司為企業(yè)級(jí)應(yīng)用推出的標(biāo)準(zhǔn)平臺(tái),現(xiàn)屬于Oracle公司[3]。Java EE自1998年發(fā)布以來得到了迅速地發(fā)展,已成為企業(yè)級(jí)應(yīng)用的主流技術(shù)平臺(tái)之一。以Java Pet Store[4]為代表,典型的Java EE企業(yè)級(jí)應(yīng)用最主要的功能是圍繞業(yè)務(wù)數(shù)據(jù)進(jìn)行存儲(chǔ)、操作和顯示。整個(gè)系統(tǒng)一般可分為Web層、業(yè)務(wù)邏輯層和企業(yè)信息系統(tǒng)層(Enterprise Information Systems Tier)。應(yīng)用運(yùn)行在Java EE平臺(tái)提供的容器中,容器為應(yīng)用的每個(gè)層次提供了相應(yīng)的功能支持,見表1。
表1 Java EE技術(shù)與系統(tǒng)層次的對(duì)應(yīng)關(guān)系
容器提供的 API和運(yùn)行環(huán)境的支持使得應(yīng)用的代碼實(shí)現(xiàn)更統(tǒng)一,為應(yīng)用遷移提供了較清晰的切入點(diǎn)。
Hadoop[5]是由 Apache開源組織的一個(gè)分布式計(jì)算框架,設(shè)計(jì)用于在大量(廉價(jià)的)硬件設(shè)備上構(gòu)建一個(gè)具有高可靠性和良好擴(kuò)展性的集群系統(tǒng),以分布式處理大數(shù)據(jù)集。Hadoop2.0包含以下模塊:
1) Hadoop通用模塊:支持其它Hadoop模塊的通用工具集;
2) Hadoop分布式文件系統(tǒng)(HDFS):支持對(duì)應(yīng)用數(shù)據(jù)高吞吐量訪問的分布式文件系統(tǒng);
3) Hadoop YARN:用于作業(yè)調(diào)度和集群資源管理的框架;
4) Hadoop MapReduce:基于YARN的大數(shù)據(jù)并行處理系統(tǒng)。
除了Hadoop本身,還有一大批以Hadoop為基礎(chǔ)發(fā)展起來的開源項(xiàng)目,它們是Hadoop平臺(tái)功能的補(bǔ)充和擴(kuò)展,集合在一起形成了Hadoop生態(tài)系統(tǒng)[6]。
從前面的敘述可見,Hadoop平臺(tái)的功能是針對(duì)大數(shù)據(jù)集存儲(chǔ)和操作而設(shè)計(jì)的,因此本文研究的遷移是指在保持Java EE企業(yè)級(jí)應(yīng)用業(yè)務(wù)功能不變的前提下,將其業(yè)務(wù)數(shù)據(jù)轉(zhuǎn)移到Hadoop中存儲(chǔ),并使應(yīng)用通過Hadoop實(shí)現(xiàn)對(duì)業(yè)務(wù)數(shù)據(jù)的訪問和操作。
對(duì)于典型的Java EE企業(yè)級(jí)應(yīng)用,業(yè)務(wù)數(shù)據(jù)的存儲(chǔ)方式主要有2種:存放在關(guān)系型數(shù)據(jù)庫(kù)中;以文件的形式存放在文件系統(tǒng)中。
3.1.1 關(guān)系型數(shù)據(jù)庫(kù)遷移
考慮到遷移后數(shù)據(jù)查詢和操作的便捷性及性能,對(duì)關(guān)系型數(shù)據(jù)庫(kù)的數(shù)據(jù)遷移一般不直接存放到HDFS中,而是遷移到Hbase[7]、Canssandra[8]等分布式數(shù)據(jù)庫(kù)中。以Hbase為例,從關(guān)系型數(shù)據(jù)庫(kù)遷入數(shù)據(jù)有以下幾類方法:
1) 直接調(diào)用Hbase的API
Hbase客戶端包已提供了必要的方法,可編寫Java程序進(jìn)行調(diào)用,程序邏輯如下:
① 通過JDBC讀出關(guān)系型數(shù)據(jù)庫(kù)中的數(shù)據(jù)。
② 假設(shè)Hbase里已建立了對(duì)應(yīng)的表。創(chuàng)建org.apache.hadoop.hbase.client.HTable類的實(shí)例,并連接到Hbase:
HTable table = new HTable(org.apache.hadoop.hbase.HBaseConfiguration.create(), 〈表名〉);
③ 創(chuàng)建org.apache.hadoop.hbase.client.Put類的實(shí)例,并將要寫入Hbase的數(shù)據(jù)放入實(shí)例內(nèi):
Put p = new Put(〈主鍵值〉);
p.add(〈列族名〉, 〈列名〉,〈版本號(hào)〉, 〈值〉);
④ 將數(shù)據(jù)寫入Hbase:
table.put(p);
使用這種方法遷移數(shù)據(jù)可以完成遷移過程,滿足各種特殊需求。但是,當(dāng)遷移的數(shù)據(jù)量大時(shí),該方法的性能較低,且有一定的編碼工作量。
2) 使用“Hadoop生態(tài)系統(tǒng)”中各項(xiàng)目提供的工具
Sqoop[9]是一個(gè)用于在Hadoop和關(guān)系型數(shù)據(jù)庫(kù)之間傳輸數(shù)據(jù)的工具,對(duì)主流的關(guān)系型數(shù)據(jù)庫(kù)如MySQL、Oracle等均能支持。使用Sqoop進(jìn)行數(shù)據(jù)遷移的過程如下:
① 安裝Sqoop,并在sqoop-env.sh中加入Hadoop集群的配置參數(shù):
HADOOP_COMMON_HOME=〈Hadoop 安裝路徑〉
HADOOP_MAPRED_HOME=〈Hadoop MapReduce安裝路徑〉
HBASE_HOME=〈Hbase安裝路徑〉
② 復(fù)制hadoop的 core包以及關(guān)系型數(shù)據(jù)庫(kù)的JDBC驅(qū)動(dòng)包到sqoop-lib目錄下。
③ 使用 sqoop-import命令進(jìn)行數(shù)據(jù)遷移。命令格式如下:
./sqoop-import --connect 〈JDBC Url〉 --table 〈源表名〉 --hbase-create-table --hbase-table 〈Hbase表名〉--column-family 〈列族名〉 --hbase-row-key 〈鍵字段名〉 --split-by 〈字段名〉 --username 〈數(shù)據(jù)庫(kù)用戶名〉--P 〈數(shù)據(jù)庫(kù)密碼〉 --compression-codec 〈壓縮方式〉–z
其中:
--connect 〈JDBC Url〉:指定要遷移數(shù)據(jù)的來源數(shù)據(jù)庫(kù);
--table 〈源表名〉:指定源數(shù)據(jù)庫(kù)中要遷移數(shù)據(jù)的表;--hbase-create-table:表示要在HBase中建立表;--hbase-table 〈Hbase表名〉:指定數(shù)據(jù)要復(fù)制到Hbase中的目標(biāo)表名稱;
--column-family 〈列族名〉:指定要在目標(biāo)表中建立的列族名稱;
--hbase-row-key 〈鍵字段名〉:指定源數(shù)據(jù)表的哪個(gè)字段作為目標(biāo)表的鍵;
--split-by 〈字段名〉:指定根據(jù)源數(shù)據(jù)表的哪個(gè)字段對(duì)源數(shù)據(jù)進(jìn)行區(qū)域切分;
--compression-codec 〈壓縮方式〉:指定壓縮方式;
-z:表示采用壓縮方式。
采用該方法遷移數(shù)據(jù),其遷移過程使用MapReduce實(shí)現(xiàn)了分布并行,當(dāng)遷移數(shù)據(jù)量較大時(shí)有較好的性能表現(xiàn)。
3) 其它方法
一些軟件產(chǎn)品基于分布式數(shù)據(jù)庫(kù)作了進(jìn)一步封裝,可將數(shù)據(jù)遷移到這些產(chǎn)品上,間接實(shí)現(xiàn)數(shù)據(jù)到分布式數(shù)據(jù)庫(kù)的遷移。
例如開源項(xiàng)目Honeycomb[10],它是一個(gè)以Hbase為后端的MySQL存儲(chǔ)引擎,能夠使MySQL用Hbase存儲(chǔ)數(shù)據(jù),用戶可以通過MySQL用SQL語言訪問數(shù)據(jù)。使用MySQL+Honeycomb進(jìn)行數(shù)據(jù)遷移的方法如下:
① 安裝MySQL+Honeycomb。
② 在Hbase的配置文件hbase-site.xml中增加以下配置:
③ 修改Honeycomb的配置文件honeycomb.xml,將hbase.zookeeper.quorum節(jié)點(diǎn)的內(nèi)容改為與Hbase相同的配置。
④ 啟動(dòng) MySQL+Honeycomb,并在 MySQL中建立與源數(shù)據(jù)表相同結(jié)構(gòu)的表:
create table 〈表名〉 (……表結(jié)構(gòu)……) character set utf8 collate utf8_bin engine=honeycomb;
⑤ 使用MySQL遷移工具或其它ETL工具將源數(shù)據(jù)庫(kù)的數(shù)據(jù)復(fù)制到 MySQL+Honeycomb中對(duì)應(yīng)的表里。
3.1.2 文件遷移
Hadoop客戶端包里的 org.apache.hadoop.fs.FileSystem類提供了必要的方法,可調(diào)用于編寫Java程序,程序邏輯如下:
1) 獲取 org.apache.hadoop.fs.FileSystem 類的實(shí)例:
FileSystem fs = FileSystem.get(〈HDFS URI〉,new org.apache.hadoop.conf.Configuration ());
2) 獲取輸出流:
fs.create(new org.apache.hadoop.fs.Path(〈HDFS 路徑〉), true);
3) 打開源文件獲取輸入流,讀取數(shù)據(jù)并寫入到輸出流中。
遷移時(shí)要記錄好遷移前后文件路徑的對(duì)應(yīng)關(guān)系,以便修改應(yīng)用進(jìn)行訪問。
應(yīng)用數(shù)據(jù)遷移到Hadoop平臺(tái)后,訪問方式也發(fā)生了改變,需要相應(yīng)修改應(yīng)用對(duì)數(shù)據(jù)的訪問邏輯。
3.2.1 關(guān)系型數(shù)據(jù)訪問
“Hadoop生態(tài)系統(tǒng)”中的一些開源項(xiàng)目為用戶提供了類似 SQL語言的數(shù)據(jù)訪問方式,降低了應(yīng)用遷移的難度。下面列出幾個(gè)項(xiàng)目和 SQL語言的主要差異以及遷移要點(diǎn):
1) Hive[11]
通過Hive訪問數(shù)據(jù)有2個(gè)途徑:一是通過Hive提供的 JDBC驅(qū)動(dòng)類 org.apache.hadoop.hive.jdbc.HiveDriver進(jìn)行訪問;二是通過 Hive客戶端類org.apache.hadoop.hive.service.HiveClient進(jìn)行訪問。
將SQL語句修改為Hive提供的查詢語言HiveQL需要:
① 查詢語句中對(duì)列取別名必須加上關(guān)鍵字“as”;② 表內(nèi)連接查詢必須用關(guān)鍵字“join”;
③ “in”子查詢改為使用“l(fā)eft semi join”。例如:
④ 插入語句按以下語法修改:
⑤ Hive暫不支持事務(wù),需要把事務(wù)相關(guān)的代碼去除。
2) Phoenix[12]
應(yīng)用可通過 Phoenix提供的 JDBC驅(qū)動(dòng)類com.salesforce.phoenix.jdbc.PhoenixDriver對(duì)數(shù)據(jù)進(jìn)行訪問。應(yīng)用需要調(diào)整的地方包括:
① Phoenix暫不支持表連接查詢和嵌套子查詢,需將SQL拆成多條分別執(zhí)行;
② Update和Insert合并成Upsert,語法如下:Upsert into 〈表名〉 (〈列名 1〉,〈列名 2〉,……)values (〈值 1〉,〈值 2〉,……);
③ Phoenix暫不支持事務(wù),需要把事務(wù)相關(guān)的代碼去除;
3) Honeycomb
應(yīng)用可通過MySQL提供的JDBC驅(qū)動(dòng)類com.mysql.jdbc.Driver對(duì)數(shù)據(jù)進(jìn)行訪問。應(yīng)用需要調(diào)整的地方包括:
① Honeycomb目前只支持UTF-8編碼,應(yīng)用需要相應(yīng)更改;
② Honeycomb對(duì)數(shù)據(jù)表名大小寫敏感,應(yīng)用需要相應(yīng)更改;
③ Honeycomb暫不支持事務(wù),需要把事務(wù)相關(guān)的代碼去除。
3.2.2 文件訪問
Hadoop客戶端包里的 org.apache.hadoop.fs.FileSystem類為用戶訪問HDFS中的文件提供了必要的方法,修改應(yīng)用程序:
1) 將調(diào)用 java.io.File類的地方改為調(diào)用org.apache.hadoop.fs.FileSystem類相應(yīng)的方法,涉及的文件路徑改為HDFS中的路徑;
2) 將使用 java.io.FileInputStream 的地方改為使用org.apache.hadoop.fs.FileSystem的open()方法獲取輸入流;
3) 將使用java.io.FileOutputStream的地方改為使用 org.apache.hadoop.fs.FileSystem 的 create()或append()方法獲取輸出流。
在實(shí)際遷移過程中,為降低代碼修改的工作量,可以編寫代理類分別繼承于 java.io.File、java.io.FileInputStream和java.io.FileOutputStream,由代理類完成對(duì)Hadoop客戶端包相關(guān)類的調(diào)用,然后通過動(dòng)態(tài)代理或通過 ASM[13]框架修改字節(jié)碼,替換掉應(yīng)用中對(duì) java.io.File、java.io.FileInputStream 和 java.io.FileOutputStream類的調(diào)用。
通過前面敘述的各種技術(shù)方法能夠?qū)崿F(xiàn)將典型的Java EE企業(yè)級(jí)應(yīng)用功能遷移到Hadoop平臺(tái)上,但實(shí)施成本、約束條件和遷移效果等有所不同。各方法的簡(jiǎn)要對(duì)比如表2、表3所示。
表2 關(guān)系型數(shù)據(jù)庫(kù)遷移技術(shù)對(duì)比
表3 關(guān)系型數(shù)據(jù)訪問邏輯遷移技術(shù)對(duì)比
對(duì)于文件遷移的方法,一般都是通過調(diào)用Hadoop客戶端包里提供的org.apache.hadoop.fs.FileSystem等類實(shí)現(xiàn),在此不作展開比較。
在遷移時(shí),需結(jié)合應(yīng)用本身的實(shí)際情況以及各種遷移方法的特點(diǎn),確定合理的遷移策略,力圖使遷移達(dá)到代價(jià)最小化和收益最大化。遷移策略應(yīng)遵循以下幾個(gè)步驟制定:
1) 評(píng)估應(yīng)用的數(shù)據(jù)量
Hadoop平臺(tái)進(jìn)行分布式計(jì)算時(shí)要產(chǎn)生跨多機(jī)數(shù)據(jù)流處理的開銷,當(dāng)處理的數(shù)據(jù)量較小時(shí),這種開銷的占比是不可接受的。因此,對(duì)數(shù)據(jù)量未達(dá)到TB級(jí)的功能應(yīng)考慮不作遷移,以獲得更佳的性能。對(duì)數(shù)據(jù)量較大的應(yīng)用繼續(xù)進(jìn)行下一步判斷。
2) 確定是否擁有應(yīng)用系統(tǒng)源代碼
是否擁有應(yīng)用系統(tǒng)源代碼決定了遷移工作開展的深度。若不具有應(yīng)用系統(tǒng)源代碼,應(yīng)選擇不需要大量修改應(yīng)用系統(tǒng)代碼的遷移技術(shù)方案:對(duì)結(jié)構(gòu)化數(shù)據(jù)采用 Honeycomb進(jìn)行遷移,對(duì)文件數(shù)據(jù)采用動(dòng)態(tài)代理替換應(yīng)用原有的訪問邏輯。對(duì)于擁有源代碼的應(yīng)用系統(tǒng)繼續(xù)進(jìn)行下一步判斷。
3) 平衡應(yīng)用性能要求與遷移成本
在數(shù)據(jù)量極大的場(chǎng)景下,各種遷移技術(shù)的性能表現(xiàn)不一,總的來說遷移工作量較小的技術(shù)其性能也相對(duì)較差。針對(duì)不同的性能成本比要求,采用的遷移策略如表4所示。
以某OA系統(tǒng)為例子驗(yàn)證Java EE企業(yè)級(jí)應(yīng)用遷移到Hadoop平臺(tái)的可行性。進(jìn)行遷移前已獲得該系統(tǒng)的源代碼,分析其數(shù)據(jù)訪問邏輯結(jié)果如下:
1) 對(duì)結(jié)構(gòu)化數(shù)據(jù)的訪問兼容MySQL;
2) 文件數(shù)據(jù)分為業(yè)務(wù)數(shù)據(jù)和配置文件2類;
3) 系統(tǒng)存在用戶上傳文件的功能,該功能使用Apache的commons-fileupload.jar實(shí)現(xiàn)。
針對(duì)應(yīng)用的特點(diǎn)采用如下遷移方法:
表4 性能成本比與遷移策略
1) 對(duì)結(jié)構(gòu)化數(shù)據(jù)采用MySQL+Honeycomb進(jìn)行遷移;
2) 調(diào)用Hadoop客戶端包里的org.apache.hadoop.fs.FileSystem類將文件數(shù)據(jù)中的業(yè)務(wù)數(shù)據(jù)復(fù)制到HDFS中,并記錄復(fù)制前后的目錄對(duì)應(yīng)關(guān)系;
3) 編寫代理類,分別繼承于java.io.File、java.io.FileInputStream、java.io.FileOutputStream、java.io.FileReader和java.io.FileWriter,覆蓋其中的公有方法,將對(duì)文件系統(tǒng)的訪問改為對(duì) org.apache.hadoop.fs.FileSystem類相應(yīng)方法的調(diào)用;
4) 對(duì)文件數(shù)據(jù)中的業(yè)務(wù)數(shù)據(jù)的訪問邏輯通過ASM框架修改字節(jié)碼替換為對(duì)代理類的訪問;
5) 對(duì)文件數(shù)據(jù)中的配置文件的訪問邏輯不作更改;
6) 對(duì)Apache的commons-fileupload.jar中讀寫臨時(shí)文件的邏輯不作更改,僅將 write方法中對(duì)java.io.FileOutputStream類的調(diào)用通過ASM框架修改字節(jié)碼替換為對(duì)代理類的訪問。
修改完成后運(yùn)行系統(tǒng),確認(rèn)結(jié)構(gòu)化數(shù)據(jù)均存放在Hbase中,業(yè)務(wù)數(shù)據(jù)文件均正確存放在HDFS中,應(yīng)用系統(tǒng)各功能運(yùn)行正常,說明Java EE企業(yè)級(jí)應(yīng)用遷移到Hadoop平臺(tái)是可行的。
Hadoop平臺(tái)及相關(guān)的開源項(xiàng)目為應(yīng)用遷移提供了豐富的支持,借助本文列舉的各種技術(shù)方法能夠?qū)崿F(xiàn)將典型的Java EE企業(yè)級(jí)應(yīng)用遷移到Hadoop平臺(tái)上。但同時(shí)應(yīng)注意到各種遷移技術(shù)均有其優(yōu)缺點(diǎn),在實(shí)際進(jìn)行應(yīng)用遷移時(shí),要充分考慮應(yīng)用數(shù)據(jù)量、性能要求和遷移工作量等因素,選擇合適的技術(shù)方案,保障遷移的成功,使 Java EE企業(yè)級(jí)應(yīng)用真正享受到
Hadoop平臺(tái)的強(qiáng)大功能。
[1]Gartner Group: IT Glossary - enterprise application software[EB/OL]. [2014-02-13]. http://www.gartner.com/it-glossary/enterprise-application-software.
[2]Martin Fowler: Patterns of Enterprise Application Architecture[M]Addison-Wesley Educational Publishers Inc, 2002.
[3]Java Community Process: JSR-000342 JavaTM Platform,Enterprise Edition 7 (Final Release) [EB/OL]. [2014-02-13].https://www.jcp.org/aboutJava/communityprocess/final/jsr342/index.html.
[4]Oracle Corporation: The Java Pet Store Referance Application[EB/OL].[2014-02-13]. http://www.oracle.com/technetwork/java/index-136650.html.
[5]Apache Foundation: Apache Hadoop [EB/OL]. [2014-02-13].http://hadoop.apache.org.
[6]Kai W?hner: Spoilt for Choice – How to choose the right Big Data / Hadoop Platform [EB/OL]. 2013.9 [2014-02-13].http://www.infoq.com/articles/BigDataPlatform.
[7]Apache Foundation: Apache Hbase [EB/OL]. [2014-02-13].http://hbase.apache.org.
[8]Apache Foundation: Apache Cassandra [EB/OL]. [2014-02-13].http://cassandra.apache.org.
[9]Apache Foundation: Apache Sqoop [EB/OL]. [2014-02-13].http://sqoop.apache.org.
[10]Altamira Corp: Honycomb [EB/OL]. [2014-02-13].https://github.com/altamiracorp/honeycomb.
[11]Apache Foundation: Apache Hive [EB/OL]. [2014-02-13].http://hive.apache.org.
[12]Apache Foundation: Apache Phoenix [EB/OL]. [2014-02-13].http://phoenix.incubator.apache.org.
[13]E. Kuleshov: Using the ASM Framework to Implement Common Java Bytecode Transformation Patterns:proceedings of the Sixth International Conference on Aspect-Oriented Software Development, Vancouver, Canada,March 2007[C/OL]. [2014-02-13]. http://asm.ow2.org/current/asm-transformations.pdf.