開發筆記 – Spring Boot整合HBase
最近在重新整理搜書吧(一個做圖書比價的平臺)的系統架構,目前圖書產品數量超過了200萬條。各種資料加起來超過40G了,使用Mysql資料庫儲存伺服器吃不消,於是考慮使用HBase儲存大部分資料。
一、摘要
以前搜書吧的資料量比較小,使用資料庫+靜態檔案儲存的方式就可以搞定,主要有2個系統組成:網站前端+後臺服務。事先把圖書詳情等一些固定內容生成html靜態檔案和前端的其他靜態檔案打包部署,動態變化的資料使用js通過REST介面獲取。後臺服務系統主要處理業務邏輯以及提供REST介面呼叫(為節省資源,很多其他個人專案的後臺服務也執行在這個系統上)。現在圖書數量增加到了200多萬條,資料量比原來大很多,使用一臺伺服器不僅硬碟不足,Mysql儲存內容太多,記憶體資源也不夠用。於是想借鑑微服務的解決方案,使用Spring Boot+HBase搭建單獨的服務,作為一個小型的資料中心,可為不同的專案儲存資料。
二、軟體
- Ubuntu 16.04
- IntelliJ IDEA 2018.01
- JDK 1.8.0
- Hadoop 2.8.5
- HBase 2.1.0
- spring-data-hadoop 2.5.0
- hbase-client 1.4.4
三、HBase介紹
Hbase是一個高可靠性、高效能、面向列、可伸縮的分散式儲存系統,利用Hbase技術可在廉價PC Server上搭建起大規模叢集。它是一個可以隨機訪問的儲存和檢索資料的平臺,允許動態的靈活的資料模型。
HBase的伺服器體系結構遵從簡單的主從伺服器架構,它由HRegion伺服器(HRegion Server)和HMaster 伺服器組成。HMaster負責管理所有的HRegion伺服器,而HBase中的所有的伺服器都是通過zookeeper來進行協調並處理HBase伺服器執行期間可能遇到的錯誤。HBase Master並不儲存HBase中的任何資料.HBase邏輯上的表可能會被劃分成多個HRegion,然後儲存到HRegion伺服器中,HBase Master伺服器中儲存的是從資料到HRegion 伺服器的對映。
四、SSH/HOST等安裝配置
4.1 修改主機名
由於我使用的是阿里雲的ECS,主機名有點長,先修改主機名稱。輸入一下命令,把名稱修改自己想要的即可,比如我的修改為luoxudong02,修改完以後重啟系統。
vim /etc/hostname
4.2 修改host
vim /etc/hosts
增加一條主機對映記錄,其中前面為IP,後面為主機名稱。IP地址需要是主機內網IP(可以使用ifconfig檢視),阿里雲ECS有一個公網IP,有一個私有IP,需要填寫私有IP。
4.3 建立使用者
為了方便管理,建立一個hadoop使用者,如果是完全分散式的話要建立一個使用者組,因為master和slaves要求使用者和組要完全一樣。
建立hadoop使用者,並使用/bin/bash作為shell
sudo useradd -m hadoop -s /bin/bash
為hadoop使用者設定密碼
sudo passwd hadoop
為了後續操作方便,增加管理員許可權
sudo adduser hadoop sudo
後續的操作都切換到hadoop使用者下執行。
4.4 配置SSH免密登入
如果沒有安裝openssh-server則先安裝
sudo apt-get install openssh-server
先建立祕鑰
ssh-keygen -t rsa
一直按回車即可,完成以後把新建立的祕鑰追加到autorized_keys中,該檔案沒有的話會自動建立。
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
五、JDK安裝配置
5.1 下載JDK
下載JDK1.8,從官網下載對應環境的安裝包:https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html。
下載完成以後解壓到指定位置
tar -zxvf jdk-8u191-linux-x64.tar.gz -C ~/local
配置JDK環境變數,開啟~/.bashrc檔案,
vim ~/.bashrc
在檔案最後新增以下程式碼。
export JAVA_HOME=~/local/jdk1.8.0_191 export CLASSPAT=.:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib export PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH
使配置生效
source ~/.bashrc
六、Hadoop+HBase環境搭建
由於我只有一臺空閒的伺服器,所以目前我只是搭建了偽分散式環境,後續再根據需要擴充套件。
下載安裝包時要選擇對應的版本號,要不然會容易採坑。大家可以檢視官方文件,裡面有介紹詳細的配置要求。
6.1 環境要求
下面的表格是JDK版本的要求,其中JDK8是支援所有版本
以下是各版本的Hadoop和HBase對應表,從表格可以看出,支援的最新版本是Hadoop-2.83+和HBase-2.1.x。我選擇的是Hadoop2.8.5和HBase-2.1.0。
6.2 安裝配置Hadoop
6.2.1 下載安裝包
從官網下載Hadoop安裝包,我安裝的版本是2.8.5,下載地址:https://www-eu.apache.org/dist/hadoop/common/hadoop-2.8.5/hadoop-2.8.5.tar.gz。下載後解壓到指定目錄。
tar -zxvf hadoop-2.8.5.tar.gz -C ~/local
解壓以後Hadoop目錄名稱帶版本號,大家可以重新命名,去掉版本號,方便維護。
6.2.2 配置環境變數
跟JDK配置類似,開啟bashrc檔案,在後面新增一下程式碼
export HADOOP_HOME=~/local/hadoop export HADOOP_CONF_DIR=$HADOOP_HOME/etc/hadoop export YARN_CONF_DIR=$HADOOP_HOME/etc/hadoop export PATH=$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$PATH
6.2.3 設定Hadoop配置檔案
進入${HADOOP_HOME}/etc/hadoop目錄,修改一下幾個檔案
- hadoop-env.sh
- core-site.xml
- hdfs-site.xml
- yarn-site.xml
- mapred-site.xml(mapred-site.xml.template重新命名)
- slaves
1) hadoop-env.sh檔案
如果檔案中沒有配置JAVA_HOME,如果存在以下程式碼則不需要修改。
# The java implementation to use. export JAVA_HOME=${JAVA_HOME}
否則需要在檔案最後配置JDK路徑
export JAVA_HOME=~/local/jdk1.8.0_191(如果檔案中已經存在export JAVA_HOME=${JAVA_HOME}就可以不需要)
2) core-site.xml檔案
在configuration節點中加入以下程式碼:
<property> <name>hadoop.tmp.dir</name> <value>/home/hadoop/local/hadoop/tmp</value> <description>Abase for other temporary directories.</description> </property> <property> <name>fs.defaultFS</name> <value>hdfs://luoxudong02:9000</value> </property>
hadoop.tmp.dir是HDFS與本地磁碟的臨時檔案,是檔案系統依賴的基本配置,很多配置路徑都依賴它,它的預設位置在/tmp/{$user}下面。需要指定一個持久化路徑,否則系統tmp被自動清掉以後會出fs.defaultFS是預設檔案系統的名稱,通常是NameNode的hostname:port,其中luoxudong02是主機名稱,9000是預設埠號
3) hdfs-site.xml檔案
在configuration節點中加入以下程式碼:
<property> <name>dfs.replication</name> <value>1</value> </property> <property> <name>dfs.namenode.name.dir</name> <value>/home/hadoop/local/hadoop/dfsdata/name</value> </property> <property> <name>dfs.datanode.data.dir</name> <value>/home/hadoop/local/hadoop/dfsdata/data</value> </property> <property> <name>dfs.permissions</name> <value>false</value> </property>
dfs.replication 是指在檔案被下入的時候,每一塊將要被複制多少份,預設是3,單主機設定1就可以了
dfs.namenode.name.dir 是NameNode元資料存放位置,預設存放在${hadoop.tmp.dir}/dfs/name目錄。
dfs.datanode.data.dir 是DataNode在本地磁碟存放block的位置,可以使用逗號分隔的目錄列表,預設存放在${hadoop.tmp.dir}/dfs/data目錄。
dfs.permissions 標識是否要檢查許可權,預設是true,設定false則允許每個人都可以存取檔案。
4) yarn-site.xml檔案
在configuration節點中加入以下程式碼:
<property> <name>yarn.resourcemanager.hostname</name> <value>luoxudong02</value> </property> <property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property>
yarn.resourcemaneger.hostname 指定主機名稱
5) mapred-site.xml檔案
這個檔案本身是不存在,需要把目錄中的mapred-site.xml.template重新命名,在其中的configuration節點加入以下程式碼:
<property> <name>mapreduce.framework.name</name> <value>yarn</value> </property>
6) slaves檔案
把檔案內容改成主機名稱,如:luoxudong02
這樣配置基本就完成了,接下來啟動hadoop
第一次啟動之前需要格式化HDFS(只需要執行一次,後面啟動Hadoop伺服器不需要執行格式化命令)
bin/hdfs namenode -format
啟動服務
sbin/start-dfs.sh sbin/start-yarn.sh
然後輸入jps命令,如果啟動成功將會看到以下服務
6.3 安裝配置HBase
6.3.1 下載安裝包
從官網下載HBase安裝包,我安裝的是HBase-2.1.0,官網下載地址:http://archive.apache.org/dist/hbase/2.1.0/hbase-2.1.0-bin.tar.gz。下載完成後解壓到指定目錄
tar -zxvf hbase-2.1.0-bin.tar.gz -C ~/local
把解壓後的目錄名稱修改為HBase,去掉版本號。
6.3.2 配置環境變數
跟JDK配置類似,開啟bashrc檔案,在後面新增一下程式碼
export HBASE_HOME=~/local/hbase export PATH=$HBASE_HOME/bin:$PATH
重新整理JDK、Hadoop和HBase的環境變數後如下
export JAVA_HOME=~/local/jdk1.8.0_191 export CLASSPAT=.:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib export HADOOP_HOME=~/local/hadoop export HADOOP_CONF_DIR=$HADOOP_HOME/etc/hadoop export YARN_CONF_DIR=$HADOOP_HOME/etc/hadoop export HBASE_HOME=~/local/hbase export PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$HBASE_HOME/bin:$PATH
6.3.3 設定HBase配置檔案
HBase配置稍微簡單一些,只需要配置3個檔案
- hbase-env.sh
- hbase-site.xml
- regionservers
1) hbase-env.sh檔案
修改兩個地方
export JAVA_HOME=~/local/jdk1.8.0_191 export HBASE_MANAGES_ZK=true
第一行是關聯JDK路徑,第二個是指定使用HBase自帶的ZK。
2) hbase-site.xml檔案
在configuration節點中增加以下程式碼:
<property> <name>hbase.zookeeper.quorum</name> <value>luoxudong02</value> </property> <property> <name>hbase.zookeeper.property.dataDir</name> <value>/home/hadoop/local/hbase/zkdata</value> </property> <property> <name>hbase.tmp.dir</name> <value>/home/hadoop/local/hbase/tmp</value> </property> <property> <name>hbase.rootdir</name> <value>hdfs://luoxudong02:9000/hbase</value> </property> <property> <name>hbase.cluster.distributed</name> <value>true</value> </property>
hbase.zookeeper.quorum是叢集的地址列表,使用逗號分割開,由於我們使用的是偽分散式,只有一臺主機,設定成主機名稱就可以。
hbase.zookeeper.property.dataDir是快照的儲存位置
hbase.tmp.dir是本地檔案系統的臨時資料夾
hbase.rootdir是regionserver的共享目錄,用來持久化HBase
hbase.cluster.distributed指執行模式,false表示單機模式,true標識分散式模式
3) 修改regionservers檔案
把內容修改成主機名稱,如:luoxudong02
這樣基本配置完成,接下來啟動服務
bin/start-hbase.sh
這裡有一個小問題,啟動的時候提示slg4j有多個,那是因為hadoop安裝包下和hbase安裝包下都存在,網上有人說刪除hbase安裝包下的slf4j-log412檔案,我試了下刪除會包其他錯誤,導致hbase無法正常啟動,暫時沒有找到比較好的解決辦法。由於不影響使用,暫時不管。
Hadoop和HBase成功啟動後會有以下服務
大家在啟動後可能發現HMaster服務或者HRegionServer服務沒有。通過檢視log/hbase-hadoop-master-主機名.log中的日誌發下出現類似以下錯誤:
java.lang.NoClassDefFoundError: org/apache/htrace/SamplerBuilder at org.apache.hadoop.hdfs.DFSClient.<init>(DFSClient.java:635) at org.apache.hadoop.hdfs.DFSClient.<init>(DFSClient.java:619) at org.apache.hadoop.hdfs.DistributedFileSystem.initialize(DistributedFileSystem.java:149) at org.apache.hadoop.fs.FileSystem.createFileSystem(FileSystem.java:2669) at org.apache.hadoop.fs.FileSystem.access$200(FileSystem.java:94) at org.apache.hadoop.fs.FileSystem$Cache.getInternal(FileSystem.java:2703) at org.apache.hadoop.fs.FileSystem$Cache.get(FileSystem.java:2685) at org.apache.hadoop.fs.FileSystem.get(FileSystem.java:373) at org.apache.hadoop.fs.Path.getFileSystem(Path.java:295) at org.apache.hadoop.hbase.util.CommonFSUtils.getRootDir(CommonFSUtils.java:358) at org.apache.hadoop.hbase.util.CommonFSUtils.isValidWALRootDir(CommonFSUtils.java:407) at org.apache.hadoop.hbase.util.CommonFSUtils.getWALRootDir(CommonFSUtils.java:383) at org.apache.hadoop.hbase.regionserver.HRegionServer.initializeFileSystem(HRegionServer.java:691) at org.apache.hadoop.hbase.regionserver.HRegionServer.<init>(HRegionServer.java:600) at org.apache.hadoop.hbase.master.HMaster.<init>(HMaster.java:484) at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:423) at org.apache.hadoop.hbase.master.HMaster.constructMaster(HMaster.java:2965) at org.apache.hadoop.hbase.master.HMasterCommandLine.startMaster(HMasterCommandLine.java:236) at org.apache.hadoop.hbase.master.HMasterCommandLine.run(HMasterCommandLine.java:140) at org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:70) at org.apache.hadoop.hbase.util.ServerCommandLine.doMain(ServerCommandLine.java:149) at org.apache.hadoop.hbase.master.HMaster.main(HMaster.java:2983)
這時需要把HBase下的lib/client-facing-thirdparty/htrace-core-xxx.jar包拷貝到lib下
cp lib/client-facing-thirdparty/htrace-core-3.1.0-incubating.jar lib/
然後再重新啟動HBase就可以了,如果發現還是啟動失敗,可以檢視日誌,針對具體問題google一下。
啟動成功以後可以通過HBase shell命令測試一下。
Hadoop和HBase的配置就完成了,接下來通過Spring Boot建立一個Web服務來訪問HBase。
6.4 Spring Boot配置
Spring Boot配置比較簡單
6.4.1 HOST配置
開啟本地hosts檔案,新增HBase主機對映,跟Hadoop伺服器配置的host類似,有一點不同就是IP地址需要是Master主機的外網IP地址。
6.4.2 新增依賴包
我是使用maven來管理jar包,在pom.xml中增加以下依賴包
<dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-hadoop-boot</artifactId> <version>2.5.0.RELEASE</version> <exclusions> <exclusion> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-hadoop</artifactId> <version>2.5.0.RELEASE</version> <exclusions> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> </exclusion> <exclusion> <groupId>log4j</groupId> <artifactId>log4j</artifactId> </exclusion> <exclusion> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.hbase</groupId> <artifactId>hbase-client</artifactId> <version>1.4.4</version> <exclusions> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> </exclusion> <exclusion> <groupId>log4j</groupId> <artifactId>log4j</artifactId> </exclusion> <exclusion> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-1.2-api</artifactId> <version>2.11.0</version> </dependency>
6.4.3 新建HBaseProperties.java
package com.luoxudong.bigdata.config; import org.springframework.boot.context.properties.ConfigurationProperties; import java.util.Map; @ConfigurationProperties(prefix = "hbase") public class HBaseProperties { private Map<String, String> config; public Map<String, String> getConfig() { return config; } public void setConfig(Map<String, String> config) { this.config = config; } }
6.4.4 新建HBaseConfig.java
package com.luoxudong.bigdata.config; import java.util.Map; import java.util.Set; import org.apache.hadoop.hbase.HBaseConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.hadoop.hbase.HbaseTemplate; @Configuration @EnableConfigurationProperties(HBaseProperties.class) public class HBaseConfig { private final HBaseProperties properties; public HBaseConfig(HBaseProperties properties) { this.properties = properties; } @Bean public HbaseTemplate hbaseTemplate() { HbaseTemplate hbaseTemplate = new HbaseTemplate(); hbaseTemplate.setConfiguration(configuration()); hbaseTemplate.setAutoFlush(true); return hbaseTemplate; } public org.apache.hadoop.conf.Configuration configuration() { org.apache.hadoop.conf.Configuration configuration = HBaseConfiguration.create(); Map<String, String> config = properties.getConfig(); Set<String> keySet = config.keySet(); for (String key : keySet) { configuration.set(key, config.get(key)); } return configuration; } }
6.4.5 application.yml配置
hbase: config: hbase.zookeeper.quorum: luoxudong02 hbase.zookeeper.property.clientPort: 2181
6.4.6 使用
配置基本完成,然後再需要的地方應用HaseTemplate物件對hbase進行操作。
@Autowired private HbaseTemplate hbaseTemplate;
七、注意事項
也許按照上面的配置完成,你發現客戶端沒法訪問HBase。一般無法訪問是因為客戶端或者伺服器配置出錯,可以通過檢視前端控制檯日誌和HBase/log下的日誌可以發現錯誤原因。有一個在除錯過程中發現客戶端和後臺都沒有報錯,就是呼叫hbaseTemplate操作hbase時不繼續往下執行,找了半天才找到問題。因為我使用的是案例雲ECS,安全組預設是不允許22以外的埠訪問,預設情況下前端需要訪問HBase的2181和16020埠,需要把這兩個埠放開。如果服務端單獨配置了防火牆,也需要放開這兩個埠。以下是HBase涉及到的埠表格,大家可以根據具體情況放開相關埠。
八、其他
以上哪裡寫的不對或者有待改進,歡迎大家提意見,謝謝!
轉載請註明出處:http://www.luoxudong.com/?p=505