Docker下HBase学习,三部曲之三:java开发

前两章《Docker下HBase学习,三部曲之一:极速体验》《Docker下HBase学习,三部曲之二:集群HBase搭建》我们学习了HBase的单机和集群环境搭建,本章我们继续实战,学习在java应用中操作HBase;

完整的Demo源码

本次实战的完整的源码地址是:git@github.com:zq2599/blog_demos.git,里面有多个工程,本次Demo所在目录如下图红框所示:
这里写图片描述

网络规划

本次实战,会启动两个docker容器:hbase单机版和tomcat,java web应用部署在tomcat上,对hbase进行操作;

启动hbase容器

执行以下命令启动hbase单机版容器:

docker run --name=hbasestandalone -idt -p 60010:60010 bolingcavalry/centos7-hbase126-standalone:0.0.1

容器启动后要执行如下命令进入容器:

docker exec -it hbasestandalone /bin/bash

进入容器后,执行start-hbase.sh即可启动hbase服务,如下图:

这里写图片描述

启动tomcat容器

执行以下命令启动一个定制版的tomcat容器,该容器启动后,可以在线部署war包:

docker run --name=tomcat001 --link=hbasestandalone:hbaseserver -p 8080:8080 -idt bolingcavalry/online_deploy_tomcat:0.0.1

link参数表明了可以通过hbaseserver确定hbasestandalone的ip(/etc/hosts文件);

关于tomcat支持在线部署war包的更多信息,请参考文章《实战docker,编写Dockerfile定制tomcat镜像,实现web应用在线部署》

开发web应用:pom.xml

本次开发的java web应用是基于maven构建的,所以在pom中出了spring,sl4j等常用的库,还要加入hbase的依赖,如下:

<dependency>
      <groupId>org.apache.hadoop</groupId>
      <artifactId>hadoop-hdfs</artifactId>
      <version>2.7.4</version>
    </dependency>

    <dependency>
      <groupId>org.apache.hbase</groupId>
      <artifactId>hbase-client</artifactId>
      <version>1.2.6</version>
    </dependency>

关于Demo

本次实战的demo是围绕着学生类展开的,这个学生类只有三个字段:id、name、age,源码如下:

public class Student {
    /**
     * 学号
     */
    private long id;

    /**
     * 姓名
     */
    private String name;

    /**
     * 年龄
     */
    private int age;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

服务接口

本次我们只是简单的实战,只涉及到新增、删除、查找这些最基本的功能,服务接口定义如下:

public interface StudentService {

    /**
     * 新增
     * @param student
     * @return
     */
    String insert(Student student);

    /**
     * 根据学号删除
     * @param id
     * @return
     */
    String delete(long id);

    /**
     * 根据学号查找
     * @param id
     * @return
     */
    StudentDTO find(long id);
}

初始化、创建表

通过spring的@PostConstruct注解,我们可以定义一个实例在构造方法完成后被spring主动调用执行的方法,这里我们用这个方法来检查有没有student方法,如果没有就在此创建,以保证后续的操作时表是存在的,源码如下:

private Configuration configuration;

    private HBaseAdmin hBaseAdmin;

    @PostConstruct
    public void init(){
        LOGGER.info("start init");
        configuration = HBaseConfiguration.create();
        configuration.set("hbase.zookeeper.quorum", "hbaseserver");
        try {
            hBaseAdmin = new HBaseAdmin(configuration);

            //检查表是否存在,如果不存在就创建
            if (!hBaseAdmin.tableExists(TABLE_NAME)) {
                LOGGER.info("table is not exist");

                HTableDescriptor hTableDescriptor = new HTableDescriptor(TableName.valueOf(TABLE_NAME));
                for (String columnName : COLUMNS) {
                    HColumnDescriptor column = new HColumnDescriptor(columnName);
                    hTableDescriptor.addFamily(column);
                }

                hBaseAdmin.createTable(hTableDescriptor);
            }
        }catch (Exception e){
            LOGGER.error("init error", e);
            e.printStackTrace();
        }

        LOGGER.info("finish init");
    }

如上所示,通过@PostConstruct注释,在业务调用StudentService提供的服务之前,init方法就已经执行过了,里面对configuration、hBaseAdmin等变量都做了实例化,还会检查student表是否存在,如果不存在就主动去创建student表;

注意这段代码configuration.set(“hbase.zookeeper.quorum”, “hbaseserver”),"hbaseserver"是启动docker容器时候的link参数,这里可以用来代表hbase服务器的ip(被docker服务写入了tomcat001容器的/etc/hosts文件);

新增一条记录

student表有两个family:id和info,id是学号,info包含了两个qualifier:name和age,并且每一条记录的rowkey也用学号表示,所以新增一条记录的关键代码如下:

        //主键
        byte[] rowIdBytes = Bytes.toBytes(rowId);

        String errorStr = null;
        HTable hTable = htable();

        if(null==hTable){
            return ERROR_CREATE_HTABLE_FAIL;
        }

        Put p1 = new Put(rowIdBytes);

        Map<String, String> map = new HashMap<String, String>();
        map.put("id", id);
        map.put("info:name", student.getName());
        map.put("info:age", String.valueOf(student.getAge()));

        for (String columnName : map.keySet()) {
            byte[] value = Bytes.toBytes(map.get(columnName));
            String[] str = columnName.split(":");
            byte[] family = Bytes.toBytes(str[0]);
            byte[] qualifier = null;
            if (str.length > 1) {
                qualifier = Bytes.toBytes(str[1]);
            }
            p1.addColumn(family, qualifier, value);
        }

        try {
            hTable.put(p1);
        }catch(Exception e){
            LOGGER.error("insert error : " + e);
            e.printStackTrace();
        }

删除一条记录

根据rowkey删除记录的代码如下所示:

List<Delete> list = new ArrayList<Delete>();
        Delete delete = new Delete(Bytes.toBytes(String.valueOf(id)));
        list.add(delete);

        String errorStr = null;

        try {
            hTable.delete(list);
        }catch(Exception e){
            errorStr = "delete error : " + e;
            LOGGER.error("delete error");
        }

根据rowkey查询记录

根据rowkey查询记录的代码如下所示:

        String errorStr = null;
        Result result = null;
        Get get = new Get(Bytes.toBytes(String.valueOf(id)));

        try {
            result = hTable.get(get);
        }catch(Exception e){
            errorStr = "find error : " + e;
        }

        StudentDTO studentDTO = new StudentDTO();
        studentDTO.setErrorStr(errorStr);

        if(StringUtils.isBlank(errorStr) && null!=result){
            studentDTO.setStudent(buildStudent(result.rawCells()));
            LOGGER.info("after build : " + JSON.toJSONString(studentDTO));
        }

        return studentDTO;

查找到的结果被封装在result对象中,result.rawCells()方法返回的Cell数组中有所有查询结果数据,这里专门做了个buildStudent方法来处理Cell数组,转成Student对象:

        Student student = null;

        for(Cell cell : cellArray){
            String row = new String(CellUtil.cloneRow(cell));
            String family = new String(CellUtil.cloneFamily(cell));
            String qualifier = new String(CellUtil.cloneQualifier(cell));
            String value = new String(CellUtil.cloneValue(cell));

            LOGGER.info("row [{}], family [{}], qualifier [{}], value [{}]", row, family, qualifier, value);

            if(!StringUtils.isNumeric(row)){
                LOGGER.error("invalid row for build student");
                return null;
            }

            if(null==student){
                student = new Student();
                student.setId(Long.valueOf(row));
            }

            if("info".equals(family)){
                if("age".equals(qualifier)){
                    student.setAge(Integer.valueOf(value));
                } else if("name".equals(qualifier)){
                    student.setName(value);
                }
            }
        }

如上所示,从Cell中可以取出row、family、qualifier、value这些信息,综合起来就能凑够student所需的字段了;

Controller

做好了StudentService及其对应的实现,我们再实现一个Controller,以便从浏览器输入url来体验StudentService的服务:

@RequestMapping("/insert")
    public String insert(HttpServletRequest request, Model model) {
        String id = get(request, "id");
        String name = get(request, "name");
        String age = get(request, "age");

        Student student = new Student();
        student.setId(Long.valueOf(id));
        student.setName(name);
        student.setAge(Integer.valueOf(age));

        String errorStr = studentService.insert(student);

        LOGGER.info("student service response [{}]", errorStr);

        return StringUtils.isEmpty(errorStr) ? "success" : buileFilePageInfo(errorStr, model);
    }


    @RequestMapping("/delete")
    public String delete(HttpServletRequest request, Model model) {
        String id = get(request, "id");

        String errorStr = studentService.delete(Long.valueOf(id));

        LOGGER.info("student service response [{}]", errorStr);

        return StringUtils.isEmpty(errorStr) ? "success" : buileFilePageInfo(errorStr, model);
    }

    @RequestMapping("/find")
    @ResponseBody
    public String find(HttpServletRequest request, Model model) {
        String id = get(request, "id");

        StudentDTO studentDTO = studentService.find(Long.valueOf(id));

        LOGGER.info("find result : {}", JSON.toJSONString(studentDTO));

        return StringUtils.isEmpty(studentDTO.getErrorStr())
                ? JSON.toJSONString(studentDTO.getStudent())
                : studentDTO.getErrorStr();
    }

部署&验证

将应用打包成war,在线部署到tomcat001容器上,开始验证:

  1. 在浏览器输入“http://localhost:8080/hbasedemo/insert?id=9009&name=Tom&age=16”,验证新增一条记录,页面提示“操作成功”后,去hbase容器上执行hbase shell命令进入控制台,再执行scan 'student’查看student表的内容,如下图,已经新增了一条记录:
    这里写图片描述
  2. 在浏览器输入“http://localhost:8080/hbasedemo/find?id=9009”,根据学号查找记录,服务端直接返回json格式数据,如下图:
    这里写图片描述
  3. 在浏览器输入“http://localhost:8080/hbasedemo/delete?id=9009”,根据学号删除记录,操作成功后,不论是执行查询请求还是去hbase控制台执行scan 'student’查看student表的内容,都看不到9009这条记录了;

至此,在java应用中操作HBase的实战就结束了,完整的源码地址是:git@github.com:zq2599/blog_demos.git,里面有多个工程,本次Demo所在目录如下图红框所示:
这里写图片描述

欢迎关注我的公众号:程序员欣宸

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员欣宸

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值