簡體   English   中英

在每個 Apache Spark 工作節點上創建 java hbase 客戶端實例

[英]Creating an instance of java hbase client on each Apache Spark worker node

使用 Spark 結構化流。

我正在編寫一個需要對數據進行大量查找的代碼。 查找非常復雜,只是不能很好地轉換為連接。

例如,在表 B 中查找字段 A 並獲取一個值,如果找到則在另一個表中查找該值。 如果未找到,請在表 D 中查找其他值 C,依此類推。

我設法使用 HBase 編寫了這些查找,它在功能上運行良好。 我為這些查找中的每一個都編寫了 udfs,例如一個非常簡單的可能是:

val someColFunc= udf( (code:String) =>
        {
            val value = HbaseObject.table.getRow("lookupTable", code, "cf", "value1")
            if (value != null)
                Bytes.toString(value)
            else
                null
        }
    )

由於 java hbase 客戶端是不可序列化的。 我像這樣通過 Hbase object

object HbaseObject {
 val table = new HbaseUtilities(zkUrl)
}

HbaseUtilities 是我為簡化查找而編寫的 class。 它只是創建了一個 java HBase 客戶端,並為我需要的那種 get 命令提供了一個包裝器。

這使我的代碼太慢了,這也沒關系。 令我困惑的是,增加或減少執行器或核心的數量對我的代碼速度沒有影響。 無論是 1 個執行程序還是 30 個執行程序,它都以完全相同的速度運行。 這讓我相信缺乏並行性。 所以我所有的員工都必須共享同一個 Hbase object。 他們是我可以在每個工作人員開始執行之前實例化一個這樣的 object 的方法嗎? 我已經嘗試過使用lazy val,它沒有任何效果

我什至嘗試創建一個 sharedSingleton ,如此處所示https://www.nicolaferraro.me/2016/02/22/using-non-serializable-objects-in-apache-spark/ ,它為我解決了一些問題,但沒有解決並行度的損失。

我知道可能有更好的方法來解決問題,並且非常歡迎所有建議,但現在我陷入了一些限制和緊迫的時間表。

您可以使用 HBase 項目主分支中的 HBase-Spark 連接器來完成您想要做的事情。 由於某種原因,連接器似乎沒有包含在任何官方 HBase 構建中,但您可以自己構建它並且它工作正常。 只需構建 jar 並將其包含在您的 pom.xml 中。

構建后,連接器將允許您通過 Worker class 內部的 HBase 連接 object,因此您不必擔心序列化連接或構建單例/等。

例如:

JavaSparkContext jSPContext ...; //Create Java Spark Context
HBaseConfiguration hbConf = HBaseConfiguration.create();
hbConf.set("hbase.zookeeper.quorum", zkQuorum);
hbConf.set("hbase.zookeeper.property.clientPort", PORT_NUM);
// this is your key link to HBase from Spark -- use it every time you need to access HBase inside the Spark parallelism:
JavaHBaseContext hBaseContext = new JavaHBaseContext(jSPContext, hbConf);   

// Create an RDD and parallelize it with HBase access:
JavaRDD<String> myRDD = ... //create your RDD
hBaseContext.foreachPartition(myRDD,  new SparkHBaseWorker());
// You can also do other usual Spark tasks, like mapPartitions, forEach, etc.

// The Spark worker class for foreachPartition use-case on RDD of type String would look something like this:
class SparkHBaseWorker implements VoidFunction<Tuple2<Iterator<String>, Connection>>
{
    private static final long serialVersionUID = 1L;
    
    public WorkerIngest()
    {
    }
    
// Put all your HBase logic into this function:
    @Override
    public void call(Tuple2<Iterator<String>, Connection> t) throws Exception
    {           
        // This is your HBase connection object:
        Connection conn = t._2();
        // Now you can do direct access to HBase from this Spark worker node:
        Table hbTable = conn.getTable(TableName.valueOf(MY_TABLE));
        // now do something with the table/etc.
    }
}

您需要在執行程序中創建所有不可序列化的對象。 您可以使用foreachPartitionmapPartitions在每個執行程序中創建連接。

與此類似的東西(我使用的是 hbase 客戶端 2.0.0):

 import org.apache.hadoop.conf.Configuration
 import org.apache.hadoop.hbase.client.{Connection, ConnectionFactory, Get, Put, Result}
 import org.apache.hadoop.hbase.util.Bytes
 import org.apache.hadoop.hbase.{HBaseConfiguration, TableName}


df.foreachPartition(
partition => {
  //foreach executor create the connection and the table
  val config: Configuration = HBaseConfiguration.create()
  config.set("hbase.zookeeper.quorum", "zk url")
  val connection: Connection = ConnectionFactory.createConnection(config)
  val table = connection.getTable(TableName.valueOf("tableName"))
  partition.map(
    record => {
      val byteKey = Bytes.toBytes(record.getString(0))
      val get = new Get(byteKey)
      val result = table.get(get)
      //DO YOUR LOGIC HERE FOR EACH RECORD
    }
  ).toList
  table.close()
  connection.close()
}
)

df是您要查找的每條記錄的 dataframe。

您可以為同一連接的每個執行程序創建所需數量的表。

當您在執行程序中創建所有對象時,您不需要處理不可序列化的問題。 您可以將它放在 class 中,就像您的HbaseUtilities一樣在那里使用,但您只需要在 foreach/map 分區內創建一個新實例

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM