最近在重新整理搜書吧(一個做圖書比價的平臺)的系統架構,目前圖書產品數量超過了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