在Spark Streaming

时间:2016-09-29 18:41:28

标签: apache-spark hbase spark-streaming

我正在编写一个项目来接收来自Kafka的数据并写入Hbase表。因为我想知道记录的差异,我需要首先在Hbase中使用相同的rowkey记录,然后使用接收记录进行减法,最后将新记录保存到HBase表中。

一开始,我尝试使用newAPIHadoop从hbase获取数据。这是我的尝试:

val conf = HBaseConfiguration.create()
conf.set("zookeeper.znode.parent", "/hbase-secure")
conf.set(TableOutputFormat.OUTPUT_TABLE, tableName)
conf.set("hbase.zookeeper.quorum", zkQuorum)
conf.set("hbase.master", masterAddr)
conf.set("hbase.zookeeper.property.clientPort", portNum)
conf.set(TableInputFormat.INPUT_TABLE, tableName)
conf.set(TableInputFormat.SCAN_COLUMNS, cfName + ":" + colName)

val HbaseRDD = ssc.sparkContext.newAPIHadoopRDD(conf, 
      classOf[TableInputFormat],
      classOf[org.apache.hadoop.hbase.io.ImmutableBytesWritable],
      classOf[org.apache.hadoop.hbase.client.Result])

通过这种方式,我可以获取具有特定列族和列名 ONLY ONCE 的记录值。通过说一次,我的意思是每次我启动我的火花流应用程序时,这段代码将被执行,我可以得到一个值,但它不会再执行了。因为每次收到Kafka的记录时,我想用cf和专栏从HBase读取我的记录,这对我不起作用。

要解决这个问题,我将逻辑移到foreachRDD(),但遗憾的是sparkContext似乎不可序列化。我收到了task is not serialzable的错误。

最后,我发现还有另一种方法可以使用hbase.clinet HTable从hbase读取数据。所以这是我的最后工作:

def transferToHBasePut(line: String): (ImmutableBytesWritable, Put) = {
    val conf = HBaseConfiguration.create()
    conf.set("zookeeper.znode.parent", "/hbase-secure")
    conf.set("hbase.zookeeper.quorum", "xxxxxx")
    conf.set("hbase.master", "xxxx")
    conf.set("hbase.zookeeper.property.clientPort", "xxx")
    conf.set(TableInputFormat.INPUT_TABLE, "xx")
    conf.set(TableInputFormat.SCAN_COLUMNS, "xxxxx")

    val testTable = new HTable(conf, "testTable")
    val scan = new Scan
    scan.addColumn("cf1".getBytes, "test".getBytes)
    val rs = testTable.getScanner(scan)

    var r = rs.next()
    val res = new StringBuilder
    while(r != null){
      val tmp = new String(r.getValue("cf1".getBytes, "test".getBytes))

      res.append(tmp)
      r= rs.next()
    }
val res = res.toString

//do the following manipulations and return object (ImmutableBytesWritable, Put)
         ..............................
         .......................
          }

在main方法中,我在foreachRDD中使用上述方法,并使用方法saveAsNewAPIHadoopDataset保存到HBase中

streamData.foreachRDD(stream => stream.map (transferToHBasePut).saveAsNewAPIHadoopDataset(job.getConfiguration))

这对我来说现在很好,但我对这个过程有疑问:

通过这种方式,我想,对于RDD的每个分区,都会创建与HBase的连接。我想知道是否有可能扩大我的应用程序。假如我在1秒钟内有超过1000条记录,看起来我的火花流媒体中将设置1000个连接。

这是从hbase进行数据读取的正确方法吗?在sparkStreaming中从HBase读取数据的最佳实践是什么?或者火花流不应该读取任何数据,它只是设计用于将流数据写入数据库。

提前致谢。

2 个答案:

答案 0 :(得分:3)

经过一番学习,我为RDD的每个分区创建了一个配置。在Spark Streaming official website检查foreachRDD的设计模式。实际配置不是连接,所以我不知道如何从现有连接池获取连接以获取和放置Hbase记录。

答案 1 :(得分:0)

foreachRDD在各个执行程序jvm进程上执行。至少你可以在transferToHBasePut方法中获得conf的单例实例(意味着在使用jvm进程的现有set conf或new conf之前进行空值检查)。因此,这将减少Hbase与Spark群集中生成的执行程序数量的连接数。

希望这会有所帮助......