HBase API

我们之后的实际开发中不可能在服务器那边直接使用shell命令一直敲的,一般都是通过API进行操作的。

环境准备

新建Maven项目,导入Maven依赖

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

1、创建连接

        HBase 的客户端连接由 ConnectionFactory 类来创建(工厂模式直接创建),我们使用完之后需要手动关闭连接。同时连接 是一个重量级的,推荐一个进程使用一个连接,对 HBase 的命令通过连接中的两个属性 Admin 和 Table 来实现。其中 Admin 主要是针对元数据-表格的创建修改进行操作, Table 则是针对表格中数据的增加修改进行操作。

1.1、单线程创建连接

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.client.AsyncConnection;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.CompletableFuture;

public class HBaseConnection {
    public static void main(String[] args) throws IOException {

        // 1. 创建连接配置参数
        Configuration conf = new Configuration();
        //对应我们 hbase-site.xml 中的配置信息的<name>和<value>的值
        conf.set("hbase.zookeeper.quorum","hadoop102,hadoop103,hadoop104");

        // 2. 创建连接
        // 默认使用同步连接
        Connection connection = ConnectionFactory.createConnection(conf);

        // 3. 使用连接
        System.out.println(connection);

        // 4. 关闭连接
        connection.close();
    }
}

1.2、多线程创建连接

我们真正开发中首先不会把配置参数写到代码中的,我们是通过Maven项目下的resources目录来读取配置文件来设置配置参数的,我们可以看源码:

Connection connection = ConnectionFactory.createConnection();

我们调用了工厂模式的 ConnectionFactory 的 createConnection 方法来创建连接,这里我们。

没有配置参数,因为HBase默认其实会自动帮我们添加配置参数:

我们可以看到当调用ConnectionFactory 的 createConnection 方法的时候,其实又调用了HBaseConfiguration 的 create 方法,

 

 该方法内部帮我们添加了配置参数:

可以看到,它其实是去读取我们Maven项目下的resources目录下的文件,所以我们需要将我们的配置参数写到resources目录下,最好使用 "hbase-ste.xml" 来命名,至于这个文件,我们直接复制我们hbase集群中conf目录下的hbase-site.xml 。 

其中,我们只需要留下关于我们zookeeper服务器连接地址的配置信息即可,别的全部删除,因为我们是客户端,我们不能设置服务端的配置,那些即使写了也不会生效。

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<property>
        <name>hbase.zookeeper.quorum</name>
        <value>hadoop102,hadoop103,hadoop104</value>
        <description>The directory shared by RegionServers.</description>
    </property>
</configuration>

 使用类单例模式确保只使用一个连接,可以同时用于多个线程。

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.client.AsyncConnection;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.CompletableFuture;

public class HBaseConnection {

    // 声明一个静态属性
    public static Connection connection = null;

    static {
        // 1. 创建连接
        // 默认使用同步连接
        try {
            //使用读取本地配置文件的方式来添加参数
            connection = ConnectionFactory.createConnection();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void closeConnection() throws IOException {
        // 判断连接是否为 null
        if (connection != null){
            connection.close();
        }
    }

    public static void main(String[] args) throws IOException {

        //使用多线程连接 直接使用创建好的连接 不再main线程单独创建
        System.out.println(HBaseConnection.connection);

        //在main线程的最后记得关闭连接
        HBaseConnection.closeConnection();
    }

}

2、DDL

创建 HBaseDDL类,添加HBaseConnection的静态属性作为我们的连接对象,确保单例模式。

2.1、创建命名空间

我们上面说了,HBase中的 DDL 语句被封装到了 Admin中,所以我们需要先获取 Admin。

Admin admin = connection.getAdmin();

注意:在coding的过程中遇到异常不要老想着直接在方法名之后直接 throws ,这样虽然是简洁了一些,但是如果第一行抛出了一个IOException,之后几行再出现异常我们就察觉不到了,所以尽量在我们核心代码处try-catch,方便了解异常信息。

然后我们直接通过方法来创建 namespace ,这里的namespace是一个对象,这样做的原因是因为我们 HBase 的shell命令中创建namespace的时候就是不止一种方法,所以这里单纯字符串来创建namespace肯定不行,对象具有更完整属性。

第二种创建命名空间的方式中,我们可以看到有一个 键值对参数,这就需要设置我们对象的属性了。 

import org.apache.hadoop.hbase.NamespaceDescriptor;
import org.apache.hadoop.hbase.client.Admin;
import org.apache.hadoop.hbase.client.Connection;

import java.io.IOException;

public class HBaseDDL {

    public static Connection connection = HBaseConnection.connection;

    /**
     * 创建命名空间
     * @param namespace 命名空间的名称
     */
    public static void createNamespace(String namespace) throws IOException {

        // 1. 获取admin
        //admin是轻量级的 并且不是线程安全的 不推荐池化或者缓存这个连接
        //也就是说 用的时候再去获取 不用就把它关闭掉
        Admin admin = connection.getAdmin();

        // 2. 调用方法创建 namespace
        // 代码比shell更加底层 所以shell能实现的功能代码 一定也可以
        // 所以代码实现时 需要更完整的命名空间描述

        // 2.1 获取一个命名空间的建造者 => 设计师
        NamespaceDescriptor.Builder builder = NamespaceDescriptor.create(namespace);

        // 2.2 给命名空间添加属性
        // 给namespace添加键值对属性其实并没有什么意义 只是给人注释一样
        builder.addConfiguration("user","lyh");

        // 2.3 使用builder构造出namespace对象
        // 创建命名空间造成的问题 属于方法本身的问题 不应该抛出
        try {
            admin.createNamespace(builder.build());
        } catch (IOException e) {
            System.out.println("该命名空间已经存在!");
            e.printStackTrace();
        }

        // 3. 关闭资源
        admin.close();

    }
    public static void main(String[] args) throws IOException {
        //测试创建马命名空间
        createNamespace("lyh");

        //记得关闭HBase连接
        HBaseConnection.closeConnection();
    }
}

运行结果 

 

2.2、判断表格是否存在

    /**
     * 判断表格是否存在
     * @param namespace 命名空间
     * @param tableName 表名
     * @return true-存在 false-不存在
     */
    public static boolean isTableExists(String namespace,String tableName) throws IOException {
        // 1. 获取admin
        Admin admin = connection.getAdmin();

        // 2. 使用方法判断表格是否存在
        boolean b = false;
        try {
            b = admin.tableExists(TableName.valueOf(namespace, tableName));
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // 3. 关闭admin
        admin.close();
        
        // 4.返回结果
        return b;
    }

2.3、创建表

    /**
     * 创建表格
     * @param namespace 命名空间
     * @param tableName 表格名称
     * @param columnFamilies 列族名称 可以有多个
     */
    public static void createTable(String namespace,String tableName,String... columnFamilies) throws IOException {
        
        // 判断是否有至少一个列族
        if (columnFamilies.length == 0){
            System.out.println("创建表格至少应该有一个列族");
            return;
        }

        // 判断表格是否已经存在
        if (isTableExists(namespace,tableName)){
            System.out.println("表格已经存在");
            return;
        }


        // 1. 获取admin
        Admin admin = connection.getAdmin();

        // 2. 调用方法创建表格
        // 2.1 获取表格的建造者
        TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(TableName.valueOf(namespace,tableName));

        // 2.2 添加参数
        for (String columnFamily : columnFamilies) {
            // 2.3 获取列族建造者
            ColumnFamilyDescriptorBuilder columnFamilyDescriptorBuilder = ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes(columnFamily));

            // 2.4 通过建造者创建对应列族描述
            // 添加版本参数-维护的版本数
            columnFamilyDescriptorBuilder.setMaxVersions(5);

            // 2.5 创建添加完参数的列族描述
            builder.setColumnFamily(columnFamilyDescriptorBuilder.build());
        }
        // 2.3 创建表格描述
        try {
            admin.createTable(builder.build());
        } catch (IOException e) {
            //System.out.println("表格已经存在");
            e.printStackTrace();
        }

        // 2.4 关闭admin
        admin.close();
    }

2.4、修改表

这里需要注意的比较多:

我们这里修改表格的列族版本,首先就需要获取表格描述和列族描述,但是我们不能重新通过newBuilder创建这两种描述,而是应该使用旧的描述。

对于旧的表格描述来说,我们可以通过admin的getDescriptor()来获取旧的描述。

对于旧的列族描述来说,我们可以通过表格描述对象的getColumnFamily()方法来获取。

    /**
     * 修改表格中一个列族的版本
     * @param namespace 命名空间
     * @param tableName 表名
     * @param columnFamily 列族
     * @param version 维护的版本
     */
    public static void modifyTable(String namespace,String tableName,String columnFamily,int version) throws IOException {

        // 判断表格是否存在
        if (!isTableExists(namespace,tableName)){
            System.out.println("表格不存在");
            return;
        }

        // 1. 获取admin
        Admin admin = connection.getAdmin();

        // 2. 调用方法修改表格
        // 2.0 获取之前的表格描述
        TableDescriptor tableDescriptor = null;
        try {
            tableDescriptor = admin.getDescriptor(TableName.valueOf(namespace, tableName));
        } catch (IOException e) {
            System.out.println("表格不存在");
            e.printStackTrace();
        }

        // 2.1 创建一个表格描述建造者
        // 如果使用填写 tableName 的方法 相当于创建了一个新的表格描述 没有之前的信息
        // 如果想要修改表格的信息 必须调用方法填写一个旧的表格描述
        TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(tableDescriptor);

        // 2.2 对应建造者进行表格数据的修改
        // 获取旧的列族描述
        ColumnFamilyDescriptor columnFamily1 = tableDescriptor.getColumnFamily(Bytes.toBytes(columnFamily));

        // 创建列族描述建造者
        ColumnFamilyDescriptorBuilder columnFamilyDescriptorBuilder = ColumnFamilyDescriptorBuilder.newBuilder(columnFamily1);

        // 修改对应的版本
        columnFamilyDescriptorBuilder.setMaxVersions(version);

        // 在这里修改的时候 如果填写的是新创建的列族描述 那么我们表格之前的其它属性会被初始化 所以要使用旧的列族描述
        builder.modifyColumnFamily(columnFamilyDescriptorBuilder.build());

        try {
            admin.modifyTable(builder.build());
        } catch (IOException e) {

            e.printStackTrace();
        }

        // 3. 关闭admin
        admin.close();
    }

2.5、删除表

需要注意HBase中删除表前必须标记表为不可用!

/**
     * 删除表格
     * @param namespace 命名空间
     * @param tableName 表名
     * @return true-删除成功
     */
    public static boolean deleteTable(String namespace,String tableName) throws IOException {
        // 1. 判断表格是否存在
        if (!isTableExists(namespace,tableName)){
            System.out.println("表格不存在 无法删除");
            return false;
        }

        // 2. 获取admin
        Admin admin = connection.getAdmin();

        // 3. 调用相关的方法删除表格
        try {
            // hbase 删除表格前必须标记标记表格为不可用才能删除
            admin.disableTable(TableName.valueOf(namespace,tableName));
            admin.deleteTable(TableName.valueOf(namespace,tableName));
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 4. 关闭admin
        admin.close();

        return true;
    }

3、DML

3.1、插入数据

我们可以看到,插入数据的put方法中要求参数必须为Byte类型,这也应证了我们之前第一篇博客说的-HBase的Cell的数据都是以Byte字节类型存储的。

public class HBaseDML {

    //静态属性
    public static Connection connection = HBaseConnection.connection;

    /**
     * 插入数据
     * @param namespace 命名空间
     * @param tableName 表名
     * @param rowKey 行键
     * @param columnFamily 列族
     * @param columnName 列名
     * @param value 值
     */
    public static void putCell(String namespace,String tableName,String rowKey,String columnFamily,String columnName,String value) throws IOException {

        // 1. 获取 Table
        Table table = connection.getTable(TableName.valueOf(namespace,tableName));

        // 2. 调用相关方法实现数据插入
        // 2.1 创建 put 对象
        Put put = new Put(Bytes.toBytes(rowKey));

        // 2.2 给 put 对象添加属性
        put.addColumn(Bytes.toBytes(columnFamily),Bytes.toBytes(columnName),Bytes.toBytes(value));

        // 2.3 将对象写入对应的方法
        try {
            table.put(put);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 3. 关闭table
        table.close();
    }

    public static void main(String[] args) throws IOException {

        // 测试插入数据
        putCell("bigdata","student","1005","info","name","hbase");

        System.out.println("其他代码");

        // 关闭连接
        HBaseConnection.closeConnection();
    }
}

3.2、查询数据

    /**
     读取数据 读取对应的一行中的某一列
     * @param namespace 命名空间
     * @param tableName 表名
     * @param rowKey 行键
     * @param columnFamily 列族
     * @param columnName 列名
     * @return 返回最小单位集合 Cells
     */
    public static Cell[] getCells(String namespace,String tableName,String rowKey,String columnFamily,String columnName) throws IOException {

        // 1. 获取Table
        Table table = connection.getTable(TableName.valueOf(namespace,tableName));

        // 2. 创建get对象
        Get get = new Get(Bytes.toBytes(rowKey));

        // 3. 读取数据
        // 如果直接调用get方法读取数据 读到的是一整行数据
        // 如果想读取某一列的数据 需要添加对应的参数
        get.addColumn(Bytes.toBytes(columnFamily),Bytes.toBytes(columnName));

         // 设置读取数据的版本所有版本
         get.readAllVersions();

         // 读取数据 得到result对象
        Result result = null;
        try {
            result = table.get(get);

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            table.close();
        }

        //返回结果
        if (result!=null)
            return result.rawCells();
        else
            return null;
    }


   /**
     * 打印Cell的值
     * @param cells Cell数组
     */
    public static void printCells(Cell[] cells){
        for (Cell cell : cells) {
            // cell 存储数据比较底层
            String rowKey = new String((CellUtil.cloneRow(cell)));
            String columnFamily = new String(CellUtil.cloneFamily(cell));
            String columnName = new String(CellUtil.cloneQualifier(cell));
            String value = new String(CellUtil.cloneValue(cell));

            System.out.print(rowKey + "-" + columnFamily + "-" + columnName + "-" + value + "\t");
        }
    }

3.3、扫描数据

我们打印的范围是 [startRow,stopRow) 的,是不包含 stopRow 的,如果我们要打印出最后一位的话,stopRow+1 越界也是没有问题的,比如下面我们一共有5行数据,我们设置终止行键为 6 就是没问题的不会报错。 

/**
     * 打印Cell的值
     * @param cells Cell数组
     */
    public static void printCells(Cell[] cells){
        for (Cell cell : cells) {
            // cell 存储数据比较底层
            String rowKey = new String((CellUtil.cloneRow(cell)));
            String columnFamily = new String(CellUtil.cloneFamily(cell));
            String columnName = new String(CellUtil.cloneQualifier(cell));
            String value = new String(CellUtil.cloneValue(cell));

            System.out.print(rowKey + "-" + columnFamily + "-" + columnName + "-" + value + "\t");
        }
    }

    /**
     * 扫描数据 [起始行健,终止行键)
     * @param namespace 命名空间
     * @param tableName 表名
     * @param startRow 起始行健
     * @param stopRow 终止行键
     */
    public static void scan(String namespace,String tableName,String startRow,String stopRow) throws IOException {

        // 1. 获取table
        Table table = connection.getTable(TableName.valueOf(namespace, tableName));

        // 2. 创建Scan对象
        Scan scan = new Scan();
        // 添加参数 来限制扫描的范围 否则扫描整张表
        scan.withStartRow(Bytes.toBytes(startRow));
        scan.withStopRow(Bytes.toBytes(stopRow));

        try {
            // 读取多行数据 获得scanner
            ResultScanner scanner = table.getScanner(scan);

            // result 来记录一行数据 本质是一个 Cell 数组
            // resultScanner 来记录多行数据 本质是一个 result 数组
            for (Result result : scanner) {
                printCells(result.rawCells());
                System.out.println();   //打印完一个行键对应的行后换行
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

        // 3. 关闭table
        table.close();
    }

3.4、带过率扫描

带过滤扫描不仅可以扫描最新的数据,还可以扫描到该列族维护的最大版本范围内的历史数据。

    /**
     * 单列带过滤扫描数据 [起始行健,终止行键)
     * @param namespace 命名空间
     * @param tableName 表名
     * @param startRow 起始行键
     * @param stopRow 终止行键
     * @param columnFamily 列族
     * @param columnName 列名
     * @param value value值
     * @throws IOException IO异常
     */
    public static void filterScan(String namespace,String tableName,String startRow,String stopRow,String columnFamily,String columnName,String value) throws IOException {

        // 1. 获取table
        Table table = connection.getTable(TableName.valueOf(namespace, tableName));

        // 2. 创建Scan对象
        Scan scan = new Scan();
        // 添加参数 来限制扫描的范围 否则扫描整张表
        scan.withStartRow(Bytes.toBytes(startRow));
        scan.withStopRow(Bytes.toBytes(stopRow));

        // 可以添加多个过滤
        FilterList filterList = new FilterList();

        // 创建过滤器
        // (1) 结果只保留当前列的数据
        ColumnValueFilter columnValueFilter = new ColumnValueFilter(
                Bytes.toBytes(columnFamily),
                Bytes.toBytes(columnName),
                // 比较关系
                CompareOperator.EQUAL,
                Bytes.toBytes(value));
        filterList.addFilter(columnValueFilter);
        // 添加过滤
        scan.setFilter(filterList);

        try {
            // 读取多行数据 获得scanner
            ResultScanner scanner = table.getScanner(scan);

            // result 来记录一行数据 本质是一个 Cell 数组
            // resultScanner 来记录多行数据 本质是一个 result 数组
            for (Result result : scanner) {
                printCells(result.rawCells());
                System.out.println();   //打印完一个行键对应的行后换行
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

        // 3. 关闭table
        table.close();
    }

整行过滤就是结果集不只是单单我们搜索的那一列数据而是整行数据(是整行数据,包括所有列族所有列,而不是一个Cell),需要使用 SingleColumnVlaueFilter 就好,需要注意的是,如果一行数据的某一列为空,而那一列的值恰好是我们过滤的关键字,那么这一行数据也会被添加到结果集。

// (2) 结果保留整行数据 包含其他列
        // 结果会保留没有当前列的数据 比如我们要info:name=张三的整行数据 但是有这么一行数据的info列族下name是空的 这种数据也会被获取到结果集中去
        SingleColumnValueFilter singleColumnValueFilter = new SingleColumnValueFilter(
                Bytes.toBytes(columnFamily),
                Bytes.toBytes(columnName),
                CompareOperator.EQUAL,
                Bytes.toBytes(value)
        );
        filterList.addFilter(singleColumnValueFilter);

3.5、删除数据

        HBase使用版本控制来管理数据的多个版本,当最新版本的数据被删除后,HBase会使用次新版本的数据填充原来的位置。所以如果一个Cell的某一列有多个版本,当我们仅仅删除最新版之后,会有旧版本来填充被删除的位置。

/**
     * 删除一行中的一列数据
     * @param namespace 命名空间
     * @param tableName 表格名称
     * @param rowKey 行键
     * @param columnFamily 列族
     * @param columnName 列名
     */
    public static void deleteColumn(String namespace,String tableName,String rowKey,String columnFamily,String columnName) throws IOException {

        // 1. 获取table
        Table table = connection.getTable(TableName.valueOf(namespace,tableName));

        // 2. 创建delete对象
        Delete delete = new Delete(Bytes.toBytes(rowKey));

        // 添加列信息
        // addColumn 删除最新版本
        // addColumns 删除所有版本

        delete.addColumn(Bytes.toBytes(columnFamily),Bytes.toBytes(columnName));

//        delete.addColumns(Bytes.toBytes(columnFamily),Bytes.toBytes(columnName));

        // 删除数据
        try {
            table.delete(delete);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 关闭table
        table.close();
    }

删除前,我们的历史版本有3个,最新的是 ls

删除后,name变为了 zs

 


至此,HBase的基本使用已经完毕,还是比较简单的,解下来就是深入了解一下HBase的底层,毕竟没有学习难度的能力就没有竞争力!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/71598.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java中创建线程三种方式

继承Thread类创建线程实现Runnable接口创建线程使用Callable和Future创建线程 继承Thread类 /*** 使用集成Thread的方式实现多线程*/ public class Match1 {public static void main(String[] args) {Runner liuxiang new Runner();//创建一个新的线程liuxiang.setName(&quo…

当执行汇编指令MOV [0001H] 01H时,CPU都做了什么?

今天和几位单位大佬聊天时&#xff0c;讨论到一个非常有趣的问题-当程序执行MOV [0001H], 01H计算机实际上都做了哪些工作&#xff1f;乍一看这个问题平平无奇&#xff0c;CPU只是把立即数01H放在了地址为0001的内存里&#xff0c;但仔细想想这个问题远没有那么简单&#xff0c…

matlab解常微分方程常用数值解法1:前向欧拉法和改进的欧拉法

总结和记录一下matlab求解常微分方程常用的数值解法&#xff0c;本文先从欧拉法和改进的欧拉法讲起。 d x d t f ( x , t ) , x ( t 0 ) x 0 \frac{d x}{d t}f(x, t), \quad x\left(t_{0}\right)x_{0} dtdx​f(x,t),x(t0​)x0​ 1. 前向欧拉法 前向欧拉法使用了泰勒展开的第…

基于grpc从零开始搭建一个准生产分布式应用(2) - 工程构建

开始本章之前默认读者已经配置好了以下环境&#xff1a;Intellij IDEA 2022.1.2、JDK 1.8.0_144、Maven 3&#xff0c;另外也建议大家在一些免费代码托管平台开个帐号&#xff0c;这样就可以免费使用git做版本处理了&#xff0c;笔者自己私人使用的是阿里云的云效平台。因为此专…

Docker安装ElasticSearch/ES 7.4.0

目录 前言安装ElasticSearch/ES安装步骤1&#xff1a;准备1. 安装docker2. 搜索可以使用的镜像。3. 也可从docker hub上搜索镜像。4. 选择合适的redis镜像。 安装步骤2&#xff1a;拉取ElasticSearch镜像1 拉取镜像2 查看已拉取的镜像 安装步骤3&#xff1a;创建容器创建容器方…

ESP8266(RTOS SDK)内嵌网页以实现WEB配网以及数据交互

【本文发布于https://blog.csdn.net/Stack_/article/details/131997098&#xff0c;未经允许不得转载&#xff0c;转载须注明出处】 1、执行make menuconfig&#xff0c;将http头由512改为更大的值&#xff0c;否则用电脑浏览器访问正常&#xff0c;但用手机浏览器访问会因为ht…

idea双击启动无效,idea卡顿问题

idea双击启动无效&#xff1a;大概率是关机时没有正确关闭idea&#xff0c;再次开机导致无法正常启动idea 1.通过任务管理器杀死idea进程后重启idea 2.需要修改配置 打开 &#xff08;以各自电脑实际为准&#xff09;C:\Program Files\JetBrains\IntelliJ IDEA 2020.3.1\bin&am…

ECS服务器安装docker

​ 为了安装并配置 Docker &#xff0c;你的系统必须满足下列最低要求&#xff1a; 64 位 Linux 或 Windows 系统 如果使用 Linux &#xff0c;内核版本必须不低于 3.10 能够使用 sudo 权限的用户 在你系统 BIOS 上启用了 VT&#xff08;虚拟化技术&#xff09;支持 on your s…

StarRocks企业级数据库

第1章 StarRocks简介 1.1 StarRocks介绍 StarRocks是新一代极速全场景MPP数据库 StraRocks充分吸收关系型OLAP数据库和分布式存储系统在大数据时代的优秀研究成果&#xff0c;在业界实践的基础上&#xff0c;进一步改进优化、升级架构&#xff0c;并增添了众多全新功能&…

进程间通信(IPC)的几种方式

进程间通信&#xff08;IPC&#xff09; 1.常见的通信方式2.低级IPC方法文件 3.常用于本机的IPC机制3.1无名管道pipe3.2命名管道FIFO3.3消息队列MessageQueue3.4共享内存SharedMemory3.5信号量Semaphore3.6信号Signal3.7unix域套接字 4.不同计算机上的IPC机制5.IPC机制的数据拷…

用友-NC-Cloud远程代码执行漏洞[2023-HW]

用友-NC-Cloud远程代码执行漏洞[2023-HW] 一、漏洞介绍二、资产搜索三、漏洞复现PoC小龙POC检测脚本: 四、修复建议 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#…

网络防御之SSL VPN

1. SSL工作过程是什么&#xff1f; 第一阶段&#xff1a; 客户端发送client hello消息到服务端&#xff0c;服务端收到client hello消息后&#xff0c;再发送server hello消息到客户端。 第二阶段&#xff1a; 服务器的证书&#xff0c;用于客户端给客户端发送信息时加密 serv…

最新智能AI系统+ChatGPT源码搭建部署详细教程+知识库+附程序源码

近期有网友问宝塔如何搭建部署AI创作ChatGPT&#xff0c;小编这里写一个详细图文教程吧。 使用Nestjs和Vue3框架技术&#xff0c;持续集成AI能力到AIGC系统&#xff01; 增加手机端签到功能、优化后台总计绘画数量逻辑&#xff01;新增 MJ 官方图片重新生成指令功能同步官方 …

Apollo让自动驾驶如此简单

前言&#xff1a; 最近被新能源的电价闹的不行&#xff0c;买了电车的直呼上当了、不香了。但电车吸引人不只是公里油耗低&#xff0c;还有良好的驾车使用感。比如辅助驾驶、甚至是自动驾驶。今天来介绍一个头部自动驾驶平台Apollo&#xff0c;Apollo是一个开源的、自动驾驶的软…

mac安装vscode 配置git

1、安装vscode 官网地址 下载mac稳定版安装很慢的解决办法 (转自) mac电脑如何解决下载vscode慢的问题 选择谷歌浏览器右上角的3个点&#xff0c;选择下载内容&#xff0c;右键选择复制链接地址&#xff0c;在新窗口粘贴地址&#xff0c; 把地址中的一段替换成下面的vscode.cd…

06_Hudi案例实战

本文来自"黑马程序员"hudi课程 6.第六章 Hudi案例实战 6.1 案例架构 6.2 业务数据 6.2.1 消息数据格式 6.2.2 数据生成 6.3 七陌数据采集 6.3.1 Apache Flume 是什么 6.3.2 Apache Flume 运行机制 6.3.3 Apache Flume 安装部署 6.3.4 Apache Flume 入门程序 6.3.5 七…

Linux 终端命令之文件浏览(3) less

Linux 文件浏览命令 cat, more, less, head, tail&#xff0c;此五个文件浏览类的命令皆为外部命令。 hannHannYang:~$ which cat /usr/bin/cat hannHannYang:~$ which more /usr/bin/more hannHannYang:~$ which less /usr/bin/less hannHannYang:~$ which head /usr/bin/he…

linux I/O性能优化

Linux 文件系统 磁盘和文件系统的关系&#xff1a; 磁盘为系统提供了最基本的持久化存储。 文件系统则在磁盘的基础上&#xff0c;提供了一个用来管理文件的树状结构。 文件系统工作原理 索引节点和目录项 文件系统&#xff0c;本身是对存储设备上的文件&#xff0c;进行组织…

cesium学习记录07-实体(Entity)

在学习记录05中&#xff0c;我们将了如何在 Cesium 中加载各种数据&#xff0c;包括矢量数据、影像图层、地形和 3D 模型。这些数据为我们构建了一个基础的场景和背景。特别是在加载 3D 模型时&#xff0c;我们采用了 viewer.scene.primitives.add 方法将模型作为一个原始对象添…

chatGPT小白快速入门-002:一文看懂什么是chatGPT

一、前言 本文是《chatGPT小白快速入门培训课程》的第002篇文章&#xff1a;一文看懂什么是chatGPT&#xff0c;全部内容采用chatGPT和chatGPT开源平替软件生成。完整内容大纲详见&#xff1a;《chatGPT小白快速入门课程大纲》。 本系列文章&#xff0c;参与&#xff1a; AIGC…