大數據【企業級360°全方位用戶畫像】標籤開發代碼抽取

寫在前面: 博主是一名軟件工程系大數據應用開發專業大二的學生,暱稱來源於《愛麗絲夢遊仙境》中的Alice和自己的暱稱。作爲一名互聯網小白,寫博客一方面是爲了記錄自己的學習歷程,一方面是希望能夠幫助到很多和自己一樣處於起步階段的萌新。由於水平有限,博客中難免會有一些錯誤,有紕漏之處懇請各位大佬不吝賜教!個人小站:http://alices.ibilibili.xyz/ , 博客主頁:https://alice.blog.csdn.net/
儘管當前水平可能不及各位大佬,但我還是希望自己能夠做得更好,因爲一天的生活就是一生的縮影。我希望在最美的年華,做最好的自己

        在之前的幾篇關於標籤開發的博客中,博主已經不止一次地爲大家介紹了開發代碼書寫的流程。無論是匹配型標籤還是統計型標籤,都涉及到了大量的代碼重用問題。爲了解決這個問題,本篇博客,我們將開始將對代碼進行抽取,簡便我們的開發!

在這裏插入圖片描述


1、創建一個特質

        對於scala基礎語法不太熟悉的朋友們可能有疑惑了。什麼是特質呢?

        其實關於scala中特質的介紹,博主在前幾個月寫scala專欄的時候就科普過了。感興趣的朋友可以👉《scala快速入門系列【特質】》

        簡單來說就是,scala中沒有Java中的接口(interface),替代的概念是——特質。

        特質是scala中代碼複用的基礎單元,特質的定義和抽象類的定義很像,但它是使用trait關鍵字。

        我們先在IDEA中創建一個特質

在這裏插入圖片描述

        然後咱們就可以開始寫代碼。

        因爲在前面的幾篇具體講解標籤開發的博客中,博主已經將流程講了好幾遍,算得上是非常透徹了。所以本篇博客,博主在這直接貼上代碼,並不做過多的過程說明。每一步具體的含義,都已經體現在了代碼中,如果各位朋友們看了有任何的疑惑,可以私信我,也可以在評論區留言。

package com.czxy.base

import java.util.Properties
import com.czxy.bean.HBaseMeta
import com.typesafe.config.{Config, ConfigFactory}
import org.apache.spark.sql.expressions.UserDefinedFunction
import org.apache.spark.sql.{DataFrame, Dataset, Row, SparkSession}

/*
 * @Author: Alice菌
 * @Date: 2020/6/13 08:49
 * @Description: 

    此代碼用戶編寫用戶畫像項目可以重用的代碼
 */
trait BaseModel {
  // 所有重複的代碼(功能)都抽取到這裏


  // 設置任務的名稱
  def setAppName:String

  // 設置四級標籤id
  def setFourTagId:String

  /* 1. 初始化SparkSession對象  */
  private val spark:SparkSession = SparkSession.builder().appName(setAppName).master("local[*]").getOrCreate()

  //導入隱式轉換
  import org.apache.spark.sql.functions._
  import spark.implicits._

  /* 2. 連接MySQL  */
  // 讀取application.conf 內的配置
  private val config: Config = ConfigFactory.load()
  // 獲取url
  private val url : String = config.getString("jdbc.mysql.url")
  // 獲取tableName
  private val tableName : String = config.getString("jdbc.mysql.tablename")

  
  def getMySQLDF = {
    // 連接MySQL數據庫
    spark.read.jdbc(url,tableName,new Properties)
  }

  
  /* 3. 讀取MySQL數據庫的四級標籤  */

  def getFourTag (mysqlCoon: DataFrame): HBaseMeta ={
    //讀取HBase中的四級標籤
    val fourTagsDS: Dataset[Row] = mysqlCoon.select("id","rule").where("id="+setFourTagId)
    //切分rule
    val KVMap: Map[String, String] = fourTagsDS.map(row => {
      // 獲取到rule值
      val RuleValue: String = row.getAs("rule").toString
      // 使用“##”對數據進行切分
      val KVMaps: Array[(String, String)] = RuleValue.split("##").map(kv => {
        val arr: Array[String] = kv.split("=")
        (arr(0), arr(1))
      })
      KVMaps
    }).collectAsList().get(0).toMap       //封裝成map
    //   將Map 轉換成HbaseMeta樣例類
    val hbaseMeta: HBaseMeta = toHBaseMeta(KVMap)
    hbaseMeta
  }

  /* 4. 讀取五級標籤數據【單獨處理】*/
  def getFiveTagDF(mysqlConn:DataFrame)={

    mysqlConn.select("id","rule").where("pid="+setFourTagId).toDF()

  }

  /* 5. 讀取hbase中的數據,這裏將hbase作爲數據源進行讀取 */
  def getHbase(hbaseMeta: HBaseMeta)={
    val hbaseDatas: DataFrame = spark.read.format("com.czxy.tools.HBaseDataSource")
      // hbaseMeta.zkHosts 就是 192.168.10.20  和 下面是兩種不同的寫法
      .option("zkHosts",hbaseMeta.zkHosts)
      .option(HBaseMeta.ZKPORT, hbaseMeta.zkPort)
      .option(HBaseMeta.HBASETABLE, hbaseMeta.hbaseTable)
      .option(HBaseMeta.FAMILY, hbaseMeta.family)
      .option(HBaseMeta.SELECTFIELDS, hbaseMeta.selectFields)
      .load()

    println(hbaseMeta)

    hbaseDatas.show()

       hbaseDatas
  }

  /* 6. 五級數據與 HBase 數據進行打標籤【單獨處理】 */
  def getNewTag(spark: SparkSession,fiveTagDF:DataFrame,hbaseDF:DataFrame):DataFrame



  /**
    * 7.合併歷史數據
    * 將標籤寫入HBase
    *
    * @param newTags 新標籤
    * @return 返回最終標籤
    */
  def joinAllTags(newTags: DataFrame): DataFrame = {
    //讀取HBase 中的歷史數據
    val oldTags: DataFrame = spark.read.format("com.czxy.tools.HBaseDataSource")
      .option(HBaseMeta.ZKHOSTS, "192.168.10.20")
      .option(HBaseMeta.ZKPORT, "2181")
      .option(HBaseMeta.HBASETABLE, "test")
      .option(HBaseMeta.FAMILY, "detail")
      .option(HBaseMeta.SELECTFIELDS, "userId,tagsId")
      .load()

    //使用join將新數據和舊數據的tagsId合併到一起
    val allTags: DataFrame = oldTags.join(newTags, oldTags("userId") === newTags("userId"))


    //  創建一個新的udf函數,用來拼接 tagsId
    val getAllTags: UserDefinedFunction = udf((oldTagsId: String, newTagsId: String) => {
      if (oldTagsId == "" && newTagsId != "") {
        newTagsId
      } else if (oldTagsId != "" && newTagsId == "") {
        oldTagsId
      } else if (oldTagsId == "" && newTagsId == "") {
        ""
      } else {
        val str: String = oldTagsId + "," + newTagsId
        str.split(",").distinct.mkString(",")
      }
    })

    //獲取最終結果
    allTags.select(
      when(oldTags("userId").isNotNull, oldTags("userId"))
        .when(newTags("userId").isNotNull, newTags("userId"))
        .as("userId"),
      getAllTags(oldTags("tagsId"), newTags("tagsId"))
        .as("tagsId")
    )


  }


  /**
    * 8. 新建一個方法,用於保存數據  save
    * @param allTags   最終的結果
    */
  def save(allTags: DataFrame): Unit = {
    //把最終結果保存到HBase
    allTags.write.format("com.czxy.tools.HBaseDataSource")
      .option(HBaseMeta.ZKHOSTS, "192.168.10.20")
      .option(HBaseMeta.ZKPORT, "2181")
      .option(HBaseMeta.HBASETABLE, "test")
      .option(HBaseMeta.FAMILY, "detail")
      .option(HBaseMeta.SELECTFIELDS, "userId,tagsId")
      .save()

    println("結果保存完畢!!!")
  }


  /* 9. 斷開連接 */
  def close(): Unit = {
    spark.close()
  }
   
  //將mysql中的四級標籤的rule  封裝成HBaseMeta
  //方便後續使用的時候方便調用
  def toHBaseMeta(KVMap: Map[String, String]): HBaseMeta = {
    //開始封裝
    HBaseMeta(KVMap.getOrElse("inType",""),
      KVMap.getOrElse(HBaseMeta.ZKHOSTS,""),
      KVMap.getOrElse(HBaseMeta.ZKPORT,""),
      KVMap.getOrElse(HBaseMeta.HBASETABLE,""),
      KVMap.getOrElse(HBaseMeta.FAMILY,""),
      KVMap.getOrElse(HBaseMeta.SELECTFIELDS,""),
      KVMap.getOrElse(HBaseMeta.ROWKEY,"")
    )
  }


  /**
    * 按照先後順序, 連接mysql數據庫, 讀取四級,五級HBase數據
    * 打標籤,最終寫入
    */
  def exec(): Unit = {
    //1.設置日誌級別
    spark.sparkContext.setLogLevel("WARN")
    //2.連接mysql
    val mysqlConnection: DataFrame = getMySQLDF
    //3. 讀取mysql數據庫中的四級標籤
    val fourTags: HBaseMeta = getFourTag(mysqlConnection)
    //4. 讀取mysql數據庫中的五級標籤
    val fiveTags: Dataset[Row] = getFiveTagDF(mysqlConnection)
    //讀取HBase 中的數據
    val hBaseMea: DataFrame = getHbase(fourTags)
    //讀取新獲取的數據
    val newTags: DataFrame = getNewTag(spark,fiveTags, hBaseMea)
    newTags.show()

    //獲取最終結果
    val allTags: DataFrame = joinAllTags(newTags)
    allTags.show()

    //保存到HBase
    save(allTags)
    //斷開連接
    close()
    
  }
  
  
}

2、調用特質

        既然特質我們已經寫好了,那麼現在我們想要基於用戶的工作進行統計型標籤開發,那麼我們就可以像下面的示例一樣。

import com.czxy.base.BaseModel
import com.czxy.bean.{HBaseMeta, TagRule}
import org.apache.spark.sql.{DataFrame, Dataset, Row, SparkSession}
import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.expressions.UserDefinedFunction

/*
 * @Author: Alice菌
 * @Date: 2020/6/13 09:48
 * @Description:

     基於用戶的Job標籤,做測試使用
 */
object Test extends BaseModel{

  override def setAppName: String = "Job"

  override def setFourTagId: String = "65"

  // 重寫Hbase數據與MySQL五級標籤數據處理的方法
  override def getNewTag(spark: SparkSession, fiveTagDF: DataFrame, hbaseDF: DataFrame): DataFrame = {

    // 引入隱式轉換
    import spark.implicits._
    //引入java 和scala相互轉換
    import scala.collection.JavaConverters._
    //引入sparkSQL的內置函數
    import org.apache.spark.sql.functions._

    // 對5級標籤的數據進行處理
    val fiveTageList: List[TagRule] = fiveTagDF.map(row => {
      // row 是一條數據
      // 獲取出id 和 rule
      val id: Int = row.getAs("id").toString.toInt
      val rule: String = row.getAs("rule").toString

      // 封裝樣例類
      TagRule(id,rule)
    }).collectAsList()   // 將DataSet轉換成util.List[TagRule]   這個類型遍歷時無法獲取id,rule數據
      .asScala.toList    // 將util.List轉換成list   需要隱式轉換    import scala.collection.JavaConverters._

    // 需要自定義UDF函數
    val getUserTags: UserDefinedFunction = udf((rule: String) => {

      // 設置標籤的默認值
      var tagId: Int = 0
      // 遍歷每一個五級標籤的rule
      for (tagRule <- fiveTageList) {

        if (tagRule.rule == rule) {
          tagId = tagRule.id
        }
      }
      tagId
    })

    val jobNewTags : DataFrame = hbaseDF.select('id.as ("userId"),getUserTags('job).as("tagsId"))
    jobNewTags.show(5)

    jobNewTags

  }
  def main(args: Array[String]): Unit = {

    exec()

  }

}

        我們可以發現,現在開發一個標籤,我們只需要實現第一步寫好的特質,然後在具體的類中設置任務的名稱AppName和四級標籤的id,以及重寫Hbase數據與MySQL五級標籤數據處理的方法。然後在程序的主入口main函數中,調用特質中的exec方法即可。

        這大大的減少了我們的工作量。不知道各位朋友感受到了沒有呢?
在這裏插入圖片描述

結語

        博主在經過了幾個小時的開發後,目前已經成功了開發了15個標籤,分別是7個匹配型和8個統計型標籤。等過段時間學習了挖掘算法後,再開發幾個挖掘型的標籤後,有希望完成超過20個標籤😎
在這裏插入圖片描述

        如果以上過程中出現了任何的紕漏錯誤,煩請大佬們指正😅

        受益的朋友或對大數據技術感興趣的夥伴記得點贊關注支持一波🙏

        希望我們都能在學習的道路上越走越遠😉
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章