Flink读写系列之-读HBase并写入HBase

本文介绍如何使用Apache Flink通过两种方式读取和写入HBase数据,包括继承RichSourceFunction和实现OutputFormat接口的方法,适用于大数据处理场景。

这里读HBase提供两种方式,一种是继承RichSourceFunction,重写父类方法,一种是实现OutputFormat接口,具体代码如下:

方式一:继承RichSourceFunction

package com.my.flink.utils.streaming.hbase;

import com.my.flink.utils.config.ConfigKeys;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.functions.source.RichSourceFunction;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.Iterator;

/**
 * @Description hbase reader
 * @Author jiangxiaozhi
 * @Date 2018/10/17 10:05
 **/
public class HBaseReader extends RichSourceFunction<Tuple2<String, String>> {
    private static final Logger logger = LoggerFactory.getLogger(HBaseReader.class);

    private Connection conn = null;
    private Table table = null;
    private Scan scan = null;

    @Override
    public void open(Configuration parameters) throws Exception {
        super.open(parameters);
        conn = HBaseConnection.getHBaseConn();
        table = conn.getTable(TableName.valueOf(ConfigKeys.HBASE_SOURCE_TABLE()));
        scan = new Scan();
        scan.setStartRow(Bytes.toBytes("1001"));
        scan.setStopRow(Bytes.toBytes("1004"));
        scan.addFamily(Bytes.toBytes(ConfigKeys.HBASE_SOURCE_CF()));

    }

    @Override
    public void run(SourceContext<Tuple2<String, String>> ctx) throws Exception {
        ResultScanner rs = table.getScanner(scan);
        Iterator<Result> iterator = rs.iterator();
        while (iterator.hasNext()) {
            Result result = iterator.next();
            String rowkey = Bytes.toString(result.getRow());
            StringBuffer sb = new StringBuffer();
            for (Cell cell : result.listCells()) {
                String value = Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
                sb.append(value).append(",");
            }
            String valueString = sb.replace(sb.length() - 1, sb.length(), "").toString();
            Tuple2<String, String> tuple2 = new Tuple2<>();
            tuple2.setFields(rowkey, valueString);
            ctx.collect(tuple2);
        }

    }

    @Override
    public void cancel() {
        try {
            if (table != null) {
                table.close();
            }
            if (conn != null) {
                conn.close();
            }
        } catch (IOException e) {
            logger.error("Close HBase Exception:", e.toString());
        }

    }
}

代码和Flink读写系列之-读mysql并写入mysql类似,具体说明可前往查看, 

方式二:重写TableInputFormat方法

    env.createInput(new TableInputFormat[org.apache.flink.api.java.tuple.Tuple2[String, String]] {
      override def mapResultToTuple(r: Result): org.apache.flink.api.java.tuple.Tuple2[String, String] = {
        val rowkey = Bytes.toString(r.getRow)
        val sb = new StringBuffer()
        for (cell: Cell <- r.rawCells()) {
          val value = Bytes.toString(cell.getValueArray, cell.getValueOffset, cell.getValueLength)
          sb.append(value).append(",")
        }
        val valueString = sb.replace(sb.length() - 1, sb.length(), "").toString
        val tuple2 = new org.apache.flink.api.java.tuple.Tuple2[String, String]
        tuple2.setField(rowkey, 0)
        tuple2.setField(valueString, 1)
        tuple2
      }

      override def getTableName: String = HBASE_SOURCE_TABLE

      override def getScanner: Scan = {
        scan
      }

      override def configure(parameters: Configuration): Unit = {
        val conf = HBaseConfiguration.create();
        conf.set(HConstants.ZOOKEEPER_QUORUM, ZOOKEEPER_QUORUM)
        conf.set(HConstants.ZOOKEEPER_CLIENT_PORT, ZOOKEEPER_CLIENT_PORT)
        conn = ConnectionFactory.createConnection(conf)
        table = classOf[HTable].cast(conn.getTable(TableName.valueOf(HBASE_SOURCE_TABLE)))
        scan = new Scan() {
          setStartRow(Bytes.toBytes("1001"))
          setStopRow(Bytes.toBytes("1004"))
          addFamily(Bytes.toBytes(HBASE_SOURCE_CF))
        }
      }

      override def close() = {
        if (table != null) {
          table.close()
        }
        if (conn != null) {
          conn.close()
        }

      }
    })

上面的env是StreamExecutionEnvironment。

 

写入HBase也有两种方法,其中写入和写入mysql类似,这里重点说明实现OutputFormat接口进行写入:

package com.my.flink.utils.streaming.hbase;

import com.my.flink.utils.config.ConfigKeys;
import org.apache.flink.api.common.io.OutputFormat;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.configuration.Configuration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Table;
import org.apache.hadoop.hbase.util.Bytes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

/**
 * @Description HBaseOutputFormat
 * @Author jiangxiaozhi
 * @Date 2018/10/16 14:06
 **/
public class HBaseOutputFormat implements OutputFormat<Tuple2<String, String>> {
    private static final Logger logger = LoggerFactory.getLogger(HBaseOutputFormat.class);

    private org.apache.hadoop.conf.Configuration conf = null;
    private Connection conn = null;
    private Table table = null;

    @Override
    public void configure(Configuration parameters) {
    }

    @Override
    public void open(int taskNumber, int numTasks) throws IOException {
        conn = HBaseConnection.getHBaseConn();
        table = conn.getTable(TableName.valueOf(ConfigKeys.HBASE_SINK_TABLE()));
    }

    @Override
    public void writeRecord(org.apache.flink.api.java.tuple.Tuple2<String, String> record) throws IOException {
        Put put = new Put(Bytes.toBytes(record.f0));
        put.addColumn(Bytes.toBytes(ConfigKeys.HBASE_SINK_CF()), Bytes.toBytes("test1"), Bytes.toBytes(record.f1));
        table.put(put);
    }

    @Override
    public void close() throws IOException {
        if (table != null) {
            table.close();
        }
        if (conn != null) {
            conn.close();
        }
    }
}

在使用时:

读取HBase:
1.如果是HBaseReader
env.addSource(new HBaseReader())//产生DataStream
2.如果是TableInputFormat,产生DataStream见方式二,env.createInput.....

存储到HBase:
1.如果是HBaseWritter
env.addSource(new HBaseWriter())//HBaseWritter没有实现,可参看JdbcWriter
2.dataStream.writeUsingOutputFormat(new HBaseOutputFormat())

 

<think>好的,我需要帮助用户解决如何用Flink异步HBase写入HBase,同时处理多列族情况下的异常和重试机制的问题。首先,我需要回顾一下Flink的异步IO机制,以及如何与HBase集成。根据用户提供的引用,特别是引用[2]和[3],提到在异步IO中使用静态变量来维护HBase连接,这样可以避免分布式缓存的问题。另外,引用[4]指出同步访问外部数据库的效率问题,异步IO能有效提升吞吐量。 接下来,用户需要处理多列族的写入HBase的表结构通常包含多个列族,每个列族下的列可能不同。在写入时,需要确保每个Put操作正确地添加到对应的列族中。同时,异常处理和重试机制是关键,特别是在分布式环境中,网络波动或HBase集群不稳定可能导致写入失败。 关于异常处理,需要考虑连接异常、超时、重试次数等。Flink的异步IO支持超时处理,但重试机制需要自行实现,可能需要结合Flink的检查点机制或使用RetryUtils等工具。引用[1]提到控制flush频率的重要性,避免给HBase造成压力,所以在写入时可能需要批量处理或缓冲数据,但异步IO本身是逐条处理,需平衡两者。 现在需要示例代码,展示如何配置异步取和写入HBase,处理多列族,以及实现异常重试。根据引用[2]和[3],连接HBase应该在RichAsyncFunction的open方法中初始化,使用静态变量保存连接。对于多列族,构造Put对象时需要指定不同的列族,添加相应的列限定符。 在重试机制方面,可以在异步操作中捕获异常,记录失败次数,如果未达到最大重试次数,则重新提交操作。同时,需要考虑HBase客户端的自动重试配置,如设置重试次数和超时时间。此外,异步IO的超时处理可以配合withTimeout设置,避免长时间阻塞。 最后,要确保代码符合最佳实践,比如资源管理,在close方法中正确关闭连接,避免资源泄漏。引用[3]提到了关闭连接的示例,需要在异步函数的close方法中释放资源。 总结步骤: 1. 异步HBase:使用AsyncFunction,维护HBase连接,处理查询和结果。 2. 异步写入HBase:同样使用AsyncFunction,处理多个列族的数据插入。 3. 异常处理:捕获IO异常,实现重试逻辑,设置最大重试次数。 4. 资源管理:使用静态连接,确保连接在TaskManager生命周期内有效,正确关闭。 5. 配置优化:调整HBase客户端参数,如重试次数、超时时间,控制flush频率。 需要验证代码的正确性,比如是否正确处理多个列族,是否在异常时重试,以及连接管理是否有效。同时,参考用户提供的引用,确保解决方案符合已有的实践经验。</think>### Flink异步读写HBase多列族场景下的实现与异常处理 #### 一、异步HBase实现方案 1. **连接池初始化**:使用静态变量维护HBase连接 ```java public class HBaseAsyncReader extends RichAsyncFunction<String, Result> { private static Connection connection; @Override public void open(Configuration parameters) throws Exception { if (connection == null) { org.apache.hadoop.conf.Configuration conf = HBaseConfiguration.create(); conf.set("hbase.zookeeper.quorum", "zk1,zk2,zk3"); connection = ConnectionFactory.createConnection(conf); // [^2] } } @Override public void asyncInvoke(String rowKey, ResultFuture<Result> resultFuture) { try (Table table = connection.getTable(TableName.valueOf("table1"))) { Get get = new Get(Bytes.toBytes(rowKey)); get.addFamily(Bytes.toBytes("cf1")); // 多列族查询 get.addFamily(Bytes.toBytes("cf2")); Result result = table.get(get); resultFuture.complete(Collections.singleton(result)); } catch (IOException e) { resultFuture.completeExceptionally(e); } } } ``` #### 二、异步写入HBase多列族实现 ```java public class HBaseAsyncWriter extends RichAsyncFunction<DataItem, Void> { private static Connection connection; private static final int MAX_RETRIES = 3; @Override public void open(Configuration parameters) throws Exception { // 同连接初始化 } @Override public void asyncInvoke(DataItem item, ResultFuture<Void> resultFuture) { Table table = null; try { table = connection.getTable(TableName.valueOf("result_table")); Put put = new Put(Bytes.toBytes(item.rowKey)); // 多列族写入 put.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("col1"), Bytes.toBytes(item.value1)); put.addColumn(Bytes.toBytes("cf2"), Bytes.toBytes("col2"), Bytes.toBytes(item.value2)); table.put(put); resultFuture.complete(null); } catch (IOException e) { handleRetry(item, resultFuture, e, 0); // [^3] } finally { if (table != null) { try { table.close(); } catch (IOException ignored) {} } } } private void handleRetry(DataItem item, ResultFuture<Void> future, Exception e, int retryCount) { if (retryCount < MAX_RETRIES) { executorService.submit(() -> asyncInvoke(item, future)); } else { future.completeExceptionally(e); } } } ``` #### 三、异常处理与重试机制 1. **连接异常处理**:通过HBase客户端配置自动重试 ```java conf.set("hbase.client.retries.number", "3"); // 客户端自动重试 conf.set("hbase.rpc.timeout", "5000"); // RPC超时控制 ``` 2. **异步IO超时配置**: ```java AsyncDataStream.unorderedWait(stream, new HBaseAsyncWriter(), 1000, TimeUnit.MILLISECONDS, 100) .name("async-hbase-writer") .setParallelism(4); ``` 3. **批量写入优化**:通过BufferedMutator减少RPC调用 ```java BufferedMutatorParams params = new BufferedMutatorParams(TableName.valueOf("table")) .writeBufferSize(2 * 1024 * 1024); // 2MB缓冲[^1] BufferedMutator mutator = connection.getBufferedMutator(params); ``` #### 四、最佳实践建议 1. **连接管理**: - 使用静态连接避免每个请求创建新连接 - 在TaskManager生命周期内保持长连接 - 通过`Runtime.getRuntime().addShutdownHook`确保连接关闭 2. **资源控制**: ```java // 在close方法中释放资源 @Override public void close() throws Exception { if (connection != null && !connection.isClosed()) { logger.info("Closing HBase connection"); connection.close(); // } } ``` 3. **监控指标**: - 注册`MetricGroup`记录重试次数 - 监控队列积压情况(`AsyncWaitOperator`指标) - 跟踪HBase RegionServer的写入延迟 #### 五、完整调用示例 ```java DataStream<DataItem> stream = ...; // 异步取 DataStream<Result> hbaseReadStream = AsyncDataStream.unorderedWait( stream, new HBaseAsyncReader(), 1000, TimeUnit.MILLISECONDS, 100 ); // 异步写入 hbaseReadStream.map(result -> convertToDataItem(result)) .returns(TypeInformation.of(DataItem.class)) .keyBy(item -> item.getKey()) .process(new HBaseAsyncWriter()) .addSink(new LogSink()); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值