寫在前面: 博主是一名軟件工程系大數據應用開發專業大二的學生,暱稱來源於《愛麗絲夢遊仙境》中的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個標籤😎
如果以上過程中出現了任何的紕漏錯誤,煩請大佬們指正😅
受益的朋友或對大數據技術感興趣的夥伴記得點贊關注支持一波🙏
希望我們都能在學習的道路上越走越遠😉