HBase2實戰:HBase Flink和Kafka整合

哥不是小蘿莉發表於2019-01-09

1.概述

Apache官方釋出HBase2已經有一段時間了,HBase2中包含了許多個Features,從官方JIRA來看,大約有4500+個ISSUES(檢視地址),從版本上來看是一個非常大的版本了。本篇部落格將為大家介紹HBase2的新特性,以及如何在實戰中與Flink、Kafka等元件進行整合。

2.內容

HBase2有哪些新特性值得我們去關注,這裡給大家列舉部分特定。

2.1 部分新特性預覽

2.1.1 Region分配優化

在HBase中遇到比較頻繁的問題就是RIT問題,而在新特性中,對於Region的管理和分配有了新的調整。AssignmentManager基於ProcedureV2實現,可以快速的分配Region,另外維護Region的State儲存不再依賴Zookeeper,能夠更好的面對Region長時間的RIT問題。

具體參考JIRA單:[HBASE-14614]、[HBASE-17844]、[HBASE-14350]

2.1.2 Offheap優化

在HBase2中減少了對Heap記憶體的使用,改為Offheap記憶體,減少垃圾的產生,以及減少GC的停頓時間。

參考JIRA單:[HBASE-11425]

2.1.3 Compaction優化

在HBase2中,引入了MemStore新的實現類CompactingMemstore,這個類和預設的DefaultMemStore類的區別在於實現了在記憶體中進行Compaction。

CompactingMemstore中,資料是通過Segment作為單位進行組織的,一個MemStore中包含多個Segment。資料最開始寫入時會進入到一個處理Active狀態的Segment中,這個Segment是可以被修改的。當該Active狀態的Segment中的資料達到閥值後,不是直接Flush到HDFS的HFile檔案中,而是先Flush到記憶體中的一個不可修改的Segment中。CompactingMemstore會在後臺將多個不可修改的Segment合併為一個更大、更緊湊的Segment。

如果RegionServer需要把MemStore中的資料Flush到磁碟,會先選擇其他型別的MemStore,然後在選擇CompactingMemstore。這是由於CompactingMemstore對記憶體的管理更加高效,所以延長CompactingMemstore的生命週期可以減少總的I/O。當CompactingMemstore被Flush到磁碟時,不可修改的Segment會被移到一個快照中進行合併,然後寫入HFile。

參考JIRA單:[HBASE-15991]

2.1.4 RegionServer Group

在引入RegionServer Group之前,HBase預設使用StochasticLoadBalancer策略將表的Region移到到RegionServer裡面。在HBase2中,可以將RegionServer劃分到多個邏輯組中,這樣可以提供多租戶的能力。

參考JIRA單:[HBASE-6721]、[HBASE-16430]、[HBASE-17589]、[HBASE-17350]、[HBASE-17349]

2.1.5 Add new AsyncRpcClient

在HBase2中,客戶端請求改為非同步RPC機制,不再是同步Wait,這樣能大大有效的提高客戶端請求的併發量,有效的提高資源利用率。

參考JIRA單:[HBASE-13784]、[HBASE-12684]

3.實戰整合

瞭解了HBase2的一些新特性之後,如何將HBase2運用到實際專案中去,下面將為大家介紹如何將HBase整合到Flink和Kafka中。資料流向如下圖所示:

 

3.1 基礎環境

整合環境如下所示:

  • JDK1.8
  • HBase-2.1.1
  • Flink-1.7.1
  • Kafka-2.1.0

3.1.1 依賴JAR

整合實戰專案,需要依賴的JAR資訊如下:

<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-connector-kafka-0.11_2.12</artifactId>
    <version>1.7.1</version>
</dependency>
<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-streaming-java_2.12</artifactId>
    <version>1.7.1</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-hbase_2.12</artifactId>
    <version>1.7.1</version>
</dependency>
<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-core</artifactId>
    <version>1.7.1</version>
</dependency>
<dependency>
    <groupId>org.apache.hadoop</groupId>
    <artifactId>hadoop-common</artifactId>
    <version>2.7.4</version>
</dependency>

建議使用Maven來管理,可以很方便的將上述依賴資訊配置到pom.xml檔案中。

3.2 資料準備

準備資料來源,將資料寫入到Kafka叢集,通過Flink進行消費,進行業務邏輯處理,然後將處理後的結果寫入到HBase進行落地。資料準備的實現程式碼如下:

public class JProducer extends Thread {

    public static void main(String[] args) {
        JProducer jproducer = new JProducer();
        jproducer.start();
    }
    
    @Override
    public void run() {
        producer();
    }

    private void producer() {
        Properties props = config();
        Producer<String, String> producer = new KafkaProducer<>(props);
        for (int i = 0; i < 10; i++) {
            String json = "{\"id\":" + i + ",\"ip\":\"192.168.0." + i + "\",\"date\":" + new Date().toString() + "}";
            String k = "key" + i;
            producer.send(new ProducerRecord<String, String>("flink_topic", k, json));
        }
        producer.close();
    }

    private Properties config() {
        Properties props = new Properties();
        props.put("bootstrap.servers", "dn1:9092,dn2:9092,dn3:9092");
        props.put("acks", "1");
        props.put("retries", 0);
        props.put("batch.size", 16384);
        props.put("linger.ms", 1);
        props.put("buffer.memory", 33554432);
        props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        props.put("partitioner.class", "org.smartloli.kafka.connector.flink.producer.TestSimplePartitioner");
        return props;
    }
}

通過上述應用程式,將生產的訊息資料寫入到Kafka的Topic中,準備好資料來源。

3.3 處理資料並落地到HBase

使用Flink消費Kafka叢集中剛剛準備好的資料來源,然後進行邏輯處理後,將結果寫入到HBase叢集進行儲存,具體實現程式碼如下:

public class FlinkHBase {

    private static String zkServer = "dn1,dn2,dn3";
    private static String port = "2181";
    private static TableName tableName = TableName.valueOf("testflink");
    private static final String cf = "ke";
    private static final String topic = "flink_topic";

    public static void main(String[] args) {
        final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.enableCheckpointing(1000);
        env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);

        DataStream<String> transction = env.addSource(new FlinkKafkaConsumer010<String>(topic, new SimpleStringSchema(), configByKafka()));
        transction.rebalance().map(new MapFunction<String, Object>() {
            private static final long serialVersionUID = 1L;

            public String map(String value) throws IOException {
                write2HBase(value);
                return value;
            }
        }).print();
        try {
            env.execute();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public static Properties configByKafka() {
        Properties props = new Properties();
        props.put("bootstrap.servers", "dn1:9092,dn2:9092,dn3:9092");
        props.put("group.id", "kv_flink");
        props.put("enable.auto.commit", "true");
        props.put("auto.commit.interval.ms", "1000");
        props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        return props;
    }

    public static void write2HBase(String value) throws IOException {
        Configuration config = HBaseConfiguration.create();

        config.set("hbase.zookeeper.quorum", zkServer);
        config.set("hbase.zookeeper.property.clientPort", port);
        config.setInt("hbase.rpc.timeout", 30000);
        config.setInt("hbase.client.operation.timeout", 30000);
        config.setInt("hbase.client.scanner.timeout.period", 30000);

        Connection connect = ConnectionFactory.createConnection(config);
        Admin admin = connect.getAdmin();
        if (!admin.tableExists(tableName)) {
            admin.createTable(new HTableDescriptor(tableName).addFamily(new HColumnDescriptor(cf)));
        }
        Table table = connect.getTable(tableName);
        TimeStamp ts = new TimeStamp(new Date());
        Date date = ts.getDate();
        Put put = new Put(Bytes.toBytes(date.getTime()));
        put.addColumn(Bytes.toBytes(cf), Bytes.toBytes("test"), Bytes.toBytes(value));
        table.put(put);
        table.close();
        connect.close();
    }
}

將該應用程式提交到Flink叢集,通過Flink消費Kafka叢集中的資料,成功執行該應用程式後,可以到HBase叢集進行驗證,看資料是否有寫入成功。

3.4 資料驗證

進入到HBase叢集,執行hbase shell命令進入到Console介面,然後執行如下命令檢視資料是否有寫入成功:

hbase(main):009:0> scan 'testflink',LIMIT=>2

執行上述命令,結果如下所示:

4.總結

HBase2釋出的新特性很有必要去研究和剖析,對於優化HBase叢集或多或少有些許幫助。通過研究這些新特性,來幫助我們有效的應用到實戰專案中。

5.結束語

這篇部落格就和大家分享到這裡,如果大家在研究學習的過程當中有什麼問題,可以加群進行討論或傳送郵件給我,我會盡我所能為您解答,與君共勉!

另外,博主出書了《Kafka並不難學》,喜歡的朋友或同學, 可以在公告欄那裡點選購買連結購買博主的書進行學習,在此感謝大家的支援。

相關文章