开发笔记 – 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

我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=3ny5vo90y3qcc

有28人对 “开发笔记 – Spring Boot集成HBase”留言了

  1. It’s actually a cool and useful piece of info. I’m satisfied that you shared this helpful information with us.
    Please stay us up to date like this. Thanks for sharing.

  2. Hi! Do you know if they make any plugins to protect against hackers?
    I’m kinda paranoid about losing everything I’ve worked
    hard on. Any suggestions?

  3. Howdy fantastic blog! Does running a blog like this
    require a massive amount work? I’ve virtually no
    knowledge of programming however I was hoping to start my own blog soon. Anyhow, should you have any suggestions or tips for new blog owners please share.
    I know this is off topic but I just wanted to ask. Many thanks!

  4. Howdy very nice web site!! Man .. Beautiful .. Superb ..
    I will bookmark your web site and take the feeds also? I am glad
    to find so many useful info right here in the post,
    we need work out more strategies in this regard, thanks for sharing.
    . . . . .

  5. Excellent goods from you, man. I’ve take into account your stuff previous to and you’re simply too excellent.
    I actually like what you have received right here, really like
    what you’re stating and the best way by which you are saying it.

    You’re making it enjoyable and you still care for to stay it wise.
    I can not wait to read far more from you. That is actually a wonderful site.

  6. Pretty nice post. I just stumbled upon your weblog and
    wished to say that I’ve really enjoyed surfing around your
    blog posts. In any case I’ll be subscribing to your rss feed and I hope you write again soon!

  7. Hiya! I know this is kinda off topic however , I’d figured I’d ask.

    Would you be interested in trading links or maybe guest writing a blog post or vice-versa?

    My blog addresses a lot of the same subjects as yours and I
    believe we could greatly benefit from each
    other. If you happen to be interested feel free to send me an e-mail.
    I look forward to hearing from you! Terrific blog by the way!

  8. Asking questions are genuinely fastidious thing if you
    are not understanding anything completely, except this paragraph provides
    nice understanding even.

  9. Wow, awesome blog layout! How long have you been blogging
    for? you make blogging look easy. The overall
    look of your website is fantastic, let alone the content!

  10. I have been surfing on-line more than three hours today, yet I never discovered any fascinating article like yours.
    It is beautiful price sufficient for me. In my view,
    if all website owners and bloggers made good content material as you probably did, the web will probably be a lot more helpful than ever before.

  11. You are so interesting! I do not suppose I’ve truly read through a single thing like that before.
    So wonderful to find another person with original thoughts on this subject matter.
    Really.. many thanks for starting this up. This site is one thing that is needed on the
    internet, someone with some originality!

  12. hello!,I love your writing so a lot! proportion we keep
    in touch extra approximately your post on AOL?
    I require a specialist on this area to resolve my problem.
    May be that’s you! Taking a look forward to look you.

  13. 不好意思跳贴留言,因为比较急,能否给一份SDK包名和对应名称 , 十分感谢,1172848710@qq.com

  14. Caused by: java.lang.ClassNotFoundException: org.apache.hadoop.hbase.client.HTableInterface

发表评论

邮箱地址不会被公开。