1. 程式人生 > >開發筆記 – Spring Boot整合HBase

開發筆記 – 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.5HBase-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