Springboot 系列 (26) - Springboot+HBase 大数据存储(四)| Springboot 项目通过 HBase API 方式访问 HBase

发布时间 2023-04-01 12:19:31作者: 垄山小站


Apache HBase 是 Java 语言编写的一款 Apache 开源的 NoSQL 型数据库,不支持 SQL,不支持事务,不支持 Join 操作,没有表关系。Apache HBase 构建在 Apache Hadoop 和 Apache Zookeeper 之上。

Apache HBase: https://hbase.apache.org/

HBase 的安装配置,请参考 “Springboot 系列 (24) - Springboot+HBase 大数据存储(二)| 安装配置 Apache HBase 和 Apache Zookeeper”。

本文将创建一个 SpringBoot 项目,演示通过 HBase API 方式访问 HBase。

 

1. HBase 运行环境

    操作系统:Ubuntu 20.04
    Hadoop 版本:3.2.2
    Zookeeper 版本:3.6.3
    HBase 版本:2.4.4
    HBase 所在路径:~/apps/hbase-2.4.4/

    本文 HBase 在 HBase + Zookeeper (独立的) 模式下运行,Zookeeper 使用端口 2182。

 

2. 创建 Springboot 项目

    Windows版本:Windows 10 Home (20H2)   
    IntelliJ IDEA:Community Edition for Windows 2020.1.4
    Apache Maven:3.8.1

    注:Spring 开发环境的搭建,可以参考 “ Spring基础知识(1)- Spring简介、Spring体系结构和开发环境配置 ”。

    1) 运行 IDEA 创建项目
   
        点击菜单 New 创建 Project:
        
        New Project -> Project Type: Maven -> Project SDK: 1.8 -> Check "Create from archtype" -> select "org.apache.maven.archtypes:maven-archtype-quickstart" -> Next

            Name: SpringbootExample21
            GroupId: com.example
            ArtifactId: SpringbootExample21

        -> Finish

    2) 修改 pom.xml

        <?xml version="1.0" encoding="UTF-8"?>

        <project xmlns="http://maven.apache.org/POM/4.0.0"
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                                    http://maven.apache.org/xsd/maven-4.0.0.xsd">
            <modelVersion>4.0.0</modelVersion>

            <groupId>com.example</groupId>
            <artifactId>SpringbootExample21</artifactId>
            <version>1.0-SNAPSHOT</version>

            <name>SpringbootExample21</name>
            <!-- FIXME change it to the project's website -->
            <url>http://www.example.com</url>

            <properties>
                <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
                <maven.compiler.source>1.8</maven.compiler.source>
                <maven.compiler.target>1.8</maven.compiler.target>
            </properties>

            <parent>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-parent</artifactId>
                <version>2.6.6</version>
                <relativePath/> <!-- lookup parent from repository -->
            </parent>

            <dependencies>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter</artifactId>
                </dependency>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-test</artifactId>
                    <scope>test</scope>
                </dependency>
                <dependency>
                    <groupId>junit</groupId>
                    <artifactId>junit</artifactId>
                    <version>4.11</version>
                    <scope>test</scope>
                </dependency>
            </dependencies>
            
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-maven-plugin</artifactId>
                        <configuration>
                            <mainClass>com.example.App</mainClass>
                            <layout>JAR</layout>
                        </configuration>
                        <executions>
                            <execution>
                                <goals>
                                    <goal>repackage</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>

                <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
                    <plugins>
                        ...
                    </plugins>
                </pluginManagement>
            </build>
        </project>


        在IDE中项目列表 -> SpringbootExample21 -> 点击鼠标右键 -> Maven -> Reload Project

        本文选择了 spring-boot-starter-parent 2.6.6 相关依赖包,spring-boot-starter 和 spring-boot-starter-test 的版本由 spring-boot-starter-parent 控制。

    3) 修改 src/main/java/com/example/App.java 文件

        package com.example;

        import org.springframework.boot.SpringApplication;
        import org.springframework.boot.autoconfigure.SpringBootApplication;

        @SpringBootApplication
        public class App {
            public static void main(String[] args) {
                SpringApplication.run(App.class, args);
                System.out.println("Spring boot jar empty project");
            }
        }


    4) 添加 src/main/resources/application.properties 文件

        spring.main.banner-mode=off

        # database - hbase
        hbase.zookeeper.quorum=localhost
        hbase.zookeeper.property.clientPort=2182
        zookeeper.znode.parent=/hbase


    5) 运行

        Run -> Edit configurations -> Click "+" -> Select "Maven"

            Command line: clean spring-boot:run
            Name: SpringbootExample21 [clean,spring-boot:run]

        -> OK

        Run -> Run "SpringbootExample21 [clean,spring-boot:run]"
           
            Spring boot jar empty project

 

3. 添加 hbase-client 依赖

    1) 修改 pom.xml

        <project ... >

            ...

            <dependencies>

                ...

                <dependency>
                    <groupId>org.apache.hbase</groupId>
                    <artifactId>hbase-client</artifactId>
                    <version>2.4.4</version>
                    <exclusions>
                        <exclusion>
                            <groupId>org.slf4j</groupId>
                            <artifactId>slf4j-log4j12</artifactId>
                        </exclusion>
                     </exclusions>
                </dependency>
            </dependencies>

        </project>


        在IDE中项目列表 -> SpringbootExample21 -> 点击鼠标右键 -> Maven -> Reload Project

    2) 修改 src/main/resources/application.properties 文件

        spring.main.banner-mode=off

        hbase.zookeeper.quorum=localhost
        hbase.zookeeper.property.clientPort=2182
        zookeeper.znode.parent=/hbase


4. HBase API 示例

    HBase API: https://hbase.apache.org/book.html#hbase_apis
    API Reference : https://hbase.apache.org/apidocs/index.html

    1) 修改 src/main/java/com/example/App.java 文件

        package com.example;

        import java.io.IOException;
        import java.util.ArrayList;
        import java.util.List;

        import org.apache.commons.lang.StringUtils;
        import org.apache.hadoop.hbase.TableName;
        import org.apache.hadoop.hbase.Cell;
        import org.apache.hadoop.hbase.CompareOperator;
        import org.apache.hadoop.hbase.client.*;
        import org.apache.hadoop.hbase.filter.RowFilter;
        import org.apache.hadoop.hbase.filter.SubstringComparator;
        import org.apache.hadoop.hbase.util.Bytes;

        import org.springframework.boot.SpringApplication;
        import org.springframework.boot.autoconfigure.SpringBootApplication;

        @SpringBootApplication
        public class App {

            private static Connection hbaseConnection;
            private static Admin hbaseAdmin;

            public static void main(String[] args) {
                SpringApplication.run(App.class, args);

                //
                try {

                    org.apache.hadoop.conf.Configuration hbaseConf = new org.apache.hadoop.conf.Configuration();
                    hbaseConf.set("hbase.zookeeper.quorum", "localhost");
                    hbaseConf.set("hbase.zookeeper.property.clientPort", "2182");
                    //hbaseConf.setInt("hbase.hconnection.threads.max", 100);
                    //hbaseConf.setInt("hbase.hconnection.threads.core", 50);
                    //hbaseConf.setLong("hbase.hconnection.threads.keepalivetime", 1000);
                    hbaseConf.set("zookeeper.znode.parent", "/hbase");

                    hbaseConnection = ConnectionFactory.createConnection(hbaseConf);
                    hbaseAdmin = hbaseConnection.getAdmin();
                    System.out.println("App -> getAdmin(): hbaseAdmin = " + hbaseAdmin);

                    if (App.tableExists("user")) {
                        System.out.println("App -> tableExists('user') = true");
                    }

                    // Delete table if exists
                    System.out.println("App -> tableExists('user')");
                    if (App.tableExists("user")) {
                        System.out.println("App -> deleteTable('user')");
                        App.deleteTable("user");
                    }

                    // Create table
                    System.out.println("App -> createTable('user', ...)");
                    App.createTable("user", new String[]{"cf1", "cf2", "cf3"});

                    // Insert data
                    System.out.println("App -> insertRecords('user', ... )");
                    App.insertRecords("user", "row1", "cf1", new String[]{"name", "age"}, new String[] {"Tom", "12"});
                    App.insertRecords("user", "row2", "cf1", new String[]{"name", "age"}, new String[] {"Jerry", "9"});
                    App.insertRecord("user", "row1", "cf2", "job", "Student");
                    App.insertRecord("user", "row1", "cf3", "gender", "Male");
                    App.insertRecord("user", "row2", "cf2", "job", "Student2");
                    App.insertRecord("user", "row2", "cf3", "gender", "Female");

                    // Scan table
                    System.out.println("App -> scanTable('user')");
                    System.out.println(App.scanTable("user"));

                    // Get value
                    String value = App.getValue("user", "row1", "cf3", "gender");
                    System.out.println("App -> getValue('user', 'row1', 'cf3', 'gender'): value = " + value);

                    // Delete column
                    System.out.println("App -> deleteColumn('user', 'row1', 'cf3', 'gender')");
                    App.deleteColumn("user", "row1", "cf3", "gender");

                    // Get row
                    System.out.println("App -> getRow('user', 'row1')");
                    System.out.println(App.getRow("user", "row1"));

                    // Delete column family
                    System.out.println("App -> deleteColumnFamily('user', 'row1', 'cf2')");
                    App.deleteColumnFamily("user", "row1", "cf2");

                    // Scan table row
                    System.out.println("App -> scanRow('user', 'row1')");
                    System.out.println(App.scanRow("user", "row1"));

                    // Delete row
                    System.out.println("App -> deleteRow('user', 'row1')");
                    App.deleteRow("user", "row1");

                    // Scan table (2)
                    System.out.println("App -> scanTable('user')");
                    System.out.println(App.scanTable("user"));

                    hbaseAdmin.close();
                    hbaseConnection.close();

                } catch (IOException e) {

                }

            }

            public static boolean tableExists(String tableName) throws IOException {
                TableName[] tableNames = hbaseAdmin.listTableNames();
                if (tableNames != null && tableNames.length > 0) {
                    for (int i = 0; i < tableNames.length; i++) {
                        if (tableName.equals(tableNames[i].getNameAsString())) {
                            return true;
                        }
                    }
                }
                return false;
            }

            public static void createTable(String tableName, String[] columnFamilies) throws IOException {
                TableName name = TableName.valueOf(tableName);

                TableDescriptorBuilder descriptorBuilder = TableDescriptorBuilder.newBuilder(name);
                List<ColumnFamilyDescriptor> columnFamilyList = new ArrayList<>();
                for (String columnFamily : columnFamilies) {
                    ColumnFamilyDescriptor columnFamilyDescriptor = ColumnFamilyDescriptorBuilder.newBuilder(columnFamily.getBytes()).build();
                    columnFamilyList.add(columnFamilyDescriptor);
                }
                descriptorBuilder.setColumnFamilies(columnFamilyList);
                TableDescriptor tableDescriptor = descriptorBuilder.build();
                hbaseAdmin.createTable(tableDescriptor);
            }

            public static void deleteTable(String tableName) throws IOException {
                boolean isExists = App.tableExists(tableName);
                if (!isExists) {
                    return;
                }

                TableName name = TableName.valueOf(tableName);
                hbaseAdmin.disableTable(name);
                hbaseAdmin.deleteTable(name);
            }

            public static void insertRecord(String tableName, String row, String columnFamily, String column, String value) throws IOException {
                TableName name = TableName.valueOf(tableName);
                Table table = hbaseConnection.getTable(name);
                Put put = new Put(Bytes.toBytes(row));
                put.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(column), Bytes.toBytes(value));
                table.put(put);
                table.close();
            }

            public static void insertRecords(String tableName, String row, String columnFamily, String[] columns, String[] values) throws IOException {
                TableName name = TableName.valueOf(tableName);
                Table table = hbaseConnection.getTable(name);
                Put put = new Put(Bytes.toBytes(row));
                for (int i = 0; i < columns.length; i++) {
                    put.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(columns[i]), Bytes.toBytes(values[i]));
                    table.put(put);
                }
                table.close();
            }

            public static String getRow(String tableName, String rowKey) throws IOException {
                Table table = hbaseConnection.getTable(TableName.valueOf(tableName));
                Get g = new Get(rowKey.getBytes());
                Result rs = table.get(g);

                String str = "";
                for (Cell cell : rs.rawCells()) {
                    StringBuffer stringBuffer = new StringBuffer()
                            .append(Bytes.toString(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength())).append("\t")
                            .append(Bytes.toString(cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength())).append("\t")
                            .append(Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength())).append("\t")
                            .append(Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength())).append("\n");
                    str += stringBuffer.toString();
                }
                table.close();

                return str;
            }

            public static String getValue(String tableName, String rowKey, String family, String column) throws IOException {
                Table table = hbaseConnection.getTable(TableName.valueOf(tableName));

                String value = "";
                if (StringUtils.isBlank(tableName) || StringUtils.isBlank(family)
                        || StringUtils.isBlank(rowKey) || StringUtils.isBlank(column)) {
                    return null;
                }

                Get g = new Get(rowKey.getBytes());
                g.addColumn(family.getBytes(), column.getBytes());
                Result result = table.get(g);
                List<Cell> ceList = result.listCells();
                if (ceList != null && ceList.size() > 0) {
                    for (Cell cell : ceList) {
                        value = Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
                    }
                }
                table.close();

                return value;
            }

            public static String scanTable(String tableName) throws IOException {
                Table table = hbaseConnection.getTable(TableName.valueOf(tableName));
                Scan scan = new Scan();
                ResultScanner scanner = table.getScanner(scan);

                String str = "";
                for (Result result : scanner){
                    for (Cell cell : result.rawCells()) {
                        StringBuffer stringBuffer = new StringBuffer()
                                .append(Bytes.toString(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength())).append("\t")
                                .append(Bytes.toString(cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength())).append("\t")
                                .append(Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength())).append("\t")
                                .append(Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength())).append("\n");

                        str += stringBuffer.toString();
                    }
                }

                scanner.close();
                table.close();

                return str;
            }

            public static String scanRow(String tableName, String rowKeyword) throws IOException {
                ArrayList<Object> list = new ArrayList<Object>();

                Table table = hbaseConnection.getTable(TableName.valueOf(tableName));
                Scan scan = new Scan();

                RowFilter rowFilter = new RowFilter(CompareOperator.EQUAL, new SubstringComparator(rowKeyword));
                scan.setFilter(rowFilter);

                ResultScanner scanner = table.getScanner(scan);

                String str = "";
                for (Result result : scanner) {
                    for (Cell cell : result.rawCells()) {
                        StringBuffer stringBuffer = new StringBuffer()
                                .append(Bytes.toString(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength())).append("\t")
                                .append(Bytes.toString(cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength())).append("\t")
                                .append(Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength())).append("\t")
                                .append(Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength())).append("\n");

                        str += stringBuffer.toString();
                    }
                }

                scanner.close();
                table.close();

                return str;
            }

            public static void deleteRow(String tableName, String rowKey) throws IOException {
                Table table = hbaseConnection.getTable(TableName.valueOf(tableName));
                Delete delete = new Delete(rowKey.getBytes());
                table.delete(delete);
                table.close();
            }

            public static void deleteColumnFamily(String tableName, String rowKey, String columnFamily) throws IOException {
                Table table = hbaseConnection.getTable(TableName.valueOf(tableName));
                Delete delete = new Delete(rowKey.getBytes());
                delete.addFamily(Bytes.toBytes(columnFamily));
                table.delete(delete);
                table.close();
            }

            public static void deleteColumn(String tableName, String rowKey, String columnFamily, String column) throws IOException {
                Table table = hbaseConnection.getTable(TableName.valueOf(tableName));
                Delete delete = new Delete(rowKey.getBytes());
                delete.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(column));
                table.delete(delete);
                table.close();
            }

        }


    2) 打包 jar

        菜单 View -> Tool Windows -> Maven -> SpringbootExample21 -> Lifecycle -> Clean & Package

        jar 包生成在目录 target/ 里

            SpringbootExample21-1.0-SNAPSHOT.jar
            SpringbootExample21-1.0-SNAPSHOT.jar.original  

        SpringbootExample21.jar  包含依赖包,可以直接运行。 SpringbootExample21.jar.original 里不包含依赖的包(要手动配置依赖环境),运行前要把文件名上的 “.original” 去掉。

    3) 运行 jar

        本文 HBase 2.4.4 在 Ubuntu 20.04 主机的 ~/apps 目录。
        
        把 SpringbootExample21-1.0-SNAPSHOT.jar 文件复制到 ~/apps 目录下,运行如下命令:

            $ java -jar SpringbootExample21-1.0-SNAPSHOT.jar

                2023-03-30 12:05:59.993  INFO 7989 --- [           main] com.example.App                          : Starting App v1.0-SNAPSHOT using Java 11.0.18 on Test-Ubuntu20 with PID 7989 (/home/xxx/apps/SpringbootExample21-1.0-SNAPSHOT.jar started by xxx in /home/xxx/apps)
                2023-03-30 12:05:59.998  INFO 7989 --- [           main] com.example.App                          : No active profile set, falling back to 1 default profile: "default"
                2023-03-30 12:06:00.490  INFO 7989 --- [           main] com.example.App                          : Started App in 0.882 seconds (JVM running for 1.214)
                2023-03-30 12:06:00.584  WARN 7989 --- [           main] org.apache.hadoop.util.NativeCodeLoader  : Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
                WARNING: An illegal reflective access operation has occurred
                WARNING: Illegal reflective access by org.apache.hadoop.security.authentication.util.KerberosUtil (jar:file:/home/xxx/apps/SpringbootExample21-1.0-SNAPSHOT.jar!/BOOT-INF/lib/hadoop-auth-2.10.0.jar!/) to method sun.security.krb5.Config.getInstance()
                WARNING: Please consider reporting this to the maintainers of org.apache.hadoop.security.authentication.util.KerberosUtil
                WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
                WARNING: All illegal access operations will be denied in a future release
                2023-03-30 12:06:00.787  INFO 7989 --- [2182@0x55cb6996] org.apache.zookeeper.ZooKeeper           : Client environment:zookeeper.version=3.5.7-f0fdd52973d373ffd9c86b81d99842dc2c7f660e, built on 02/10/2020 11:30 GMT
                2023-03-30 12:06:00.788  INFO 7989 --- [2182@0x55cb6996] org.apache.zookeeper.ZooKeeper           : Client environment:host.name=Tom-Ubuntu20
                2023-03-30 12:06:00.788  INFO 7989 --- [2182@0x55cb6996] org.apache.zookeeper.ZooKeeper           : Client environment:java.version=11.0.18
                2023-03-30 12:06:00.788  INFO 7989 --- [2182@0x55cb6996] org.apache.zookeeper.ZooKeeper           : Client environment:java.vendor=Ubuntu
                2023-03-30 12:06:00.788  INFO 7989 --- [2182@0x55cb6996] org.apache.zookeeper.ZooKeeper           : Client environment:java.home=/usr/lib/jvm/java-11-openjdk-amd64
                2023-03-30 12:06:00.788  INFO 7989 --- [2182@0x55cb6996] org.apache.zookeeper.ZooKeeper           : Client environment:java.class.path=SpringbootExample21-1.0-SNAPSHOT.jar
                2023-03-30 12:06:00.788  INFO 7989 --- [2182@0x55cb6996] org.apache.zookeeper.ZooKeeper           : Client environment:java.library.path=/usr/java/packages/lib:/usr/lib/x86_64-linux-gnu/jni:/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu:/usr/lib/jni:/lib:/usr/lib
                2023-03-30 12:06:00.788  INFO 7989 --- [2182@0x55cb6996] org.apache.zookeeper.ZooKeeper           : Client environment:java.io.tmpdir=/tmp
                2023-03-30 12:06:00.788  INFO 7989 --- [2182@0x55cb6996] org.apache.zookeeper.ZooKeeper           : Client environment:java.compiler=<NA>
                2023-03-30 12:06:00.788  INFO 7989 --- [2182@0x55cb6996] org.apache.zookeeper.ZooKeeper           : Client environment:os.name=Linux
                2023-03-30 12:06:00.788  INFO 7989 --- [2182@0x55cb6996] org.apache.zookeeper.ZooKeeper           : Client environment:os.arch=amd64
                2023-03-30 12:06:00.788  INFO 7989 --- [2182@0x55cb6996] org.apache.zookeeper.ZooKeeper           : Client environment:os.version=5.15.0-69-generic
                2023-03-30 12:06:00.788  INFO 7989 --- [2182@0x55cb6996] org.apache.zookeeper.ZooKeeper           : Client environment:user.name=xxx
                2023-03-30 12:06:00.788  INFO 7989 --- [2182@0x55cb6996] org.apache.zookeeper.ZooKeeper           : Client environment:user.home=/home/xxx
                2023-03-30 12:06:00.788  INFO 7989 --- [2182@0x55cb6996] org.apache.zookeeper.ZooKeeper           : Client environment:user.dir=/home/xxx/apps
                2023-03-30 12:06:00.789  INFO 7989 --- [2182@0x55cb6996] org.apache.zookeeper.ZooKeeper           : Client environment:os.memory.free=37MB
                2023-03-30 12:06:00.789  INFO 7989 --- [2182@0x55cb6996] org.apache.zookeeper.ZooKeeper           : Client environment:os.memory.max=494MB
                2023-03-30 12:06:00.789  INFO 7989 --- [2182@0x55cb6996] org.apache.zookeeper.ZooKeeper           : Client environment:os.memory.total=57MB
                2023-03-30 12:06:00.792  INFO 7989 --- [2182@0x55cb6996] org.apache.zookeeper.ZooKeeper           : Initiating client connection, connectString=192.168.1.4:2182 sessionTimeout=90000 watcher=org.apache.hadoop.hbase.zookeeper.ReadOnlyZKClient$$Lambda$398/0x0000000840296440@40e9164d
                2023-03-30 12:06:00.798  INFO 7989 --- [2182@0x55cb6996] org.apache.zookeeper.common.X509Util     : Setting -D jdk.tls.rejectClientInitiatedRenegotiation=true to disable client-initiated TLS renegotiation
                2023-03-30 12:06:00.806  INFO 7989 --- [2182@0x55cb6996] org.apache.zookeeper.ClientCnxnSocket    : jute.maxbuffer value is 4194304 Bytes
                2023-03-30 12:06:00.819  INFO 7989 --- [2182@0x55cb6996] org.apache.zookeeper.ClientCnxn          : zookeeper.request.timeout value is 0. feature enabled=
                2023-03-30 12:06:00.853  INFO 7989 --- [2.168.1.4:2182)] org.apache.zookeeper.ClientCnxn          : Opening socket connection to server 192.168.1.4/192.168.1.4:2182. Will not attempt to authenticate using SASL (unknown error)
                2023-03-30 12:06:00.862  INFO 7989 --- [2.168.1.4:2182)] org.apache.zookeeper.ClientCnxn          : Socket connection established, initiating session, client: /192.168.1.4:43262, server: 192.168.1.4/192.168.1.4:2182
                2023-03-30 12:06:00.873  INFO 7989 --- [2.168.1.4:2182)] org.apache.zookeeper.ClientCnxn          : Session establishment complete on server 192.168.1.4/192.168.1.4:2182, sessionid = 0x10000053fd6000c, negotiated timeout = 40000

                App -> getAdmin(): hbaseAdmin = org.apache.hadoop.hbase.client.HBaseAdmin@3e2055d6
                App -> tableExists('user') = true
                App -> tableExists('user')
                App -> deleteTable('user')

                2023-03-30 12:06:01.343  INFO 7989 --- [           main] o.apache.hadoop.hbase.client.HBaseAdmin  : Started disable of user
                2023-03-30 12:06:01.736  INFO 7989 --- [           main] o.apache.hadoop.hbase.client.HBaseAdmin  : Operation: DISABLE, Table Name: default:user, procId: 1135 completed
                2023-03-30 12:06:02.077  INFO 7989 --- [           main] o.apache.hadoop.hbase.client.HBaseAdmin  : Operation: DELETE, Table Name: default:user, procId: 1138 completed
                App -> createTable('user', ...)
                2023-03-30 12:06:02.812  INFO 7989 --- [           main] o.apache.hadoop.hbase.client.HBaseAdmin  : Operation: CREATE, Table Name: default:user, procId: 1139 completed

                App -> insertRecords('user', ... )
                App -> scanTable('user')
                row1    cf1     age     12
                row1    cf1     name    Tom
                row1    cf2     job     Student
                row1    cf3     gender  Male
                row2    cf1     age     9
                row2    cf1     name    Jerry
                row2    cf2     job     Student2
                row2    cf3     gender  Female

                App -> getValue('user', 'row1', 'cf3', 'gender'): value = Male
                App -> deleteColumn('user', 'row1', 'cf3', 'gender')
                App -> getRow('user', 'row1')
                row1    cf1     age     12
                row1    cf1     name    Tom
                row1    cf2     job     Student

                App -> deleteColumnFamily('user', 'row1', 'cf2')
                App -> scanRow('user', 'row1')
                row1    cf1     age     12
                row1    cf1     name    Tom

                App -> deleteRow('user', 'row1')
                App -> scanTable('user')
                row2    cf1     age     9
                row2    cf1     name    Jerry
                row2    cf2     job     Student2
                row2    cf3     gender  Female

                2023-03-30 12:06:03.063  INFO 7989 --- [           main] o.a.h.h.client.ConnectionImplementation  : Closing master protocol: MasterService