基於flink的電商使用者行為資料分析【3】| 實時流量統計
前言
在上一期內容中,菌哥已經為大家介紹了實時熱門商品統計模組的功能開發的過程(?基於flink的電商使用者行為資料分析【2】| 實時熱門商品統計)。本期文章,我們要學習的是實時流量統計模組的開發過程。
模組建立和資料準備
在UserBehaviorAnalysis下新建一個 maven module作為子專案,命名為NetworkFlowAnalysis
。在這個子模組中,我們同樣並沒有引入更多的依賴,所以也不需要改動pom檔案。
在src/main/目錄下,將預設原始檔目錄java改名為scala。將apache伺服器的日誌檔案apache.log
複製到資原始檔目錄src/main/resources
下,我們將從這裡讀取資料。
程式碼實現
我們現在要實現的模組是 “實時流量統計”。對於一個電商平臺而言,使用者登入的入口流量、不同頁面的訪問流量都是值得分析的重要資料,而這些資料,可以簡單地從web伺服器的日誌中提取出來。我們在這裡實現最基本的“頁面瀏覽數”的統計,也就是讀取伺服器日誌中的每一行log,統計在一段時間內使用者訪問url的次數。
具體做法為:每隔5秒,輸出最近10分鐘內訪問量最多的前N個URL。可以看出,這個需求與之前“實時熱門商品統計”非常類似,所以我們完全可以借鑑此前的程式碼。
具體分析如下:
熱門頁面
- 基本需求
– 從 web 伺服器的日誌中,統計實時的熱門訪問頁面
– 統計每分鐘的ip訪問量,取出訪問量最大的5個地址,每5秒更新一次- 解決思路
– 將 apache 伺服器日誌中的時間,轉換為時間戳,作為 Event Time
– 構建滑動視窗,視窗長度為1分鐘,滑動距離為5秒PV 和 UV
- 基本需求
– 從埋點日誌中,統計實時的 PV 和 UV
– 統計每小時的訪問量(PV),並且對使用者進行去重(UV)- 解決思路
– 統計埋點日誌中的 pv 行為,利用 Set 資料結構進行去重
– 對於超大規模的資料,可以考慮用布隆過濾器進行去重
在src/main/scala下建立NetworkFlow.scala檔案,新建一個單例物件。定義樣例類ApacheLogEvent
,這是輸入的日誌資料流;另外還有UrlViewCount
,這是視窗操作統計的輸出資料型別。在main函式中建立StreamExecutionEnvironment 並做配置,然後從apache.log檔案中讀取資料,幷包裝成ApacheLogEvent型別。
// 輸入 log 資料樣例類
case class ApacheLogEvent(ip: String, userId: String, eventTime: Long, method: String, url: String)
// 中間統計結果樣例類
case class UrlViewCount(url: String, windowEnd: Long, count: Long)
需要注意的是,原始日誌中的時間是“dd/MM/yyyy:HH:mm:ss”
的形式,需要定義一個DateTimeFormat
將其轉換為我們需要的時間戳格式:
.map(line => {
val linearray = line.split(" ")
val sdf = new SimpleDateFormat("dd/MM/yyyy:HH:mm:ss")
val timestamp = sdf.parse(linearray(3)).getTime
ApacheLogEvent(linearray(0), linearray(2), timestamp,
linearray(5), linearray(6))
})
因為後面部分的邏輯可以說與實時商品統計部分的邏輯是一樣的,所以這裡小菌就不再帶著大家一步步去分析了,完整程式碼如下:
import java.sql.Timestamp
import java.text.SimpleDateFormat
import java.util
import org.apache.flink.api.common.functions.AggregateFunction
import org.apache.flink.api.common.state.{ListState, ListStateDescriptor}
import org.apache.flink.streaming.api.TimeCharacteristic
import org.apache.flink.streaming.api.functions.KeyedProcessFunction
import org.apache.flink.streaming.api.functions.timestamps.BoundedOutOfOrdernessTimestampExtractor
import org.apache.flink.streaming.api.scala.{DataStream, StreamExecutionEnvironment}
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.scala.function.WindowFunction
import org.apache.flink.streaming.api.windowing.time.Time
import org.apache.flink.streaming.api.windowing.windows.TimeWindow
import org.apache.flink.util.Collector
import scala.collection.mutable.ListBuffer
/*
* @Author: Alice菌
* @Date: 2020/11/23 14:16
* @Description:
電商使用者行為資料分析:實時流量統計
<每隔5秒,輸出最近10分鐘內訪問量最多的前N個URL>
*/
object NetworkFlow {
// 輸入 log 資料樣例類
case class ApacheLogEvent(ip: String, userId: String, eventTime: Long, method: String, url: String)
// 中間統計結果樣例類
case class UrlViewCount(url: String, windowEnd: Long, count: Long)
def main(args: Array[String]): Unit = {
// 建立 流處理的 環境
val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
// 設定時間語義為 eventTime -- 事件建立的時間
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
// 設定任務並行度
env.setParallelism(1)
// 讀取檔案資料
val stream: DataStream[String] = env.readTextFile("G:\\idea arc\\BIGDATA\\project\\src\\main\\resources\\apache.log")
// 對 stream 資料進行處理
stream.map(data => {
val dataArray: Array[String] = data.split(" ")
// 因為日誌檔案中的資料格式是 17/05/2015:10:05:03
// 所以我們這裡用DataFormat對時間進行轉換
val simpleDateFormat: SimpleDateFormat = new SimpleDateFormat("dd/MM/yyyy:HH:mm:ss")
val timestamp: Long = simpleDateFormat.parse(dataArray(3).trim).getTime
// 將解析的資料存放至我們定義好的樣例類中
ApacheLogEvent(dataArray(0).trim, dataArray(1).trim, timestamp, dataArray(5).trim, dataArray(6).trim)
})
.assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor[ApacheLogEvent](Time.seconds(60)) {
override def extractTimestamp(element: ApacheLogEvent): Long = element.eventTime
})
// 因為我們需要統計出每種url的出現的次數,故這裡將 url 作為 key 進行分組
.keyBy(_.url)
// 滑動視窗聚合 -- 每隔5秒,輸出最近10分鐘內訪問量最多的前N個URL
.timeWindow(Time.minutes(10), Time.seconds(5))
// 預計算,統計出每個 URL 的訪問量
.aggregate(new CountAgg(),new WindowResult())
// 根據視窗結束時間進行分組
.keyBy(_.windowEnd)
// 輸出每個視窗中訪問量最多的前5個URL
.process(new TopNHotUrls(5)) //
.print()
// 執行程式
env.execute("network flow job")
}
// 自定義的預聚合函式
class CountAgg() extends AggregateFunction[ApacheLogEvent, Long, Long] {
override def createAccumulator(): Long = 0L
override def add(value: ApacheLogEvent, accumulator: Long): Long = accumulator + 1
override def getResult(accumulator: Long): Long = accumulator
override def merge(a: Long, b: Long): Long = a + b
}
// 自定義的視窗處理函式
class WindowResult() extends WindowFunction[Long, UrlViewCount, String, TimeWindow] {
override def apply(url: String, window: TimeWindow, input: Iterable[Long], out: Collector[UrlViewCount]): Unit = {
// 輸出結果
out.collect(UrlViewCount(url, window.getEnd, input.iterator.next()))
}
}
// 自定義 process function,實現排序輸出
class TopNHotUrls(nSize: Int) extends KeyedProcessFunction[Long, UrlViewCount, String] {
// 定義一個狀態列表,儲存結果
lazy val urlState: ListState[UrlViewCount] = getRuntimeContext.getListState( new ListStateDescriptor[UrlViewCount]( "urlState", classOf[UrlViewCount] ) )
override def processElement(value: UrlViewCount, ctx: KeyedProcessFunction[Long, UrlViewCount, String]#Context, collector: Collector[String]): Unit = {
// 將資料新增至 狀態 列表中
urlState.add(value)
// 根據視窗結束時間windowEnd,設定定時器
ctx.timerService().registerEventTimeTimer(value.windowEnd + 1)
}
override def onTimer(timestamp: Long, ctx: KeyedProcessFunction[Long, UrlViewCount, String]#OnTimerContext, out: Collector[String]): Unit = {
// 新建一個ListBuffer,用於存放狀態列表中的資料
val allUrlViews: ListBuffer[UrlViewCount] = new ListBuffer[UrlViewCount]()
// 獲取到狀態列表
val iter: util.Iterator[UrlViewCount] = urlState.get().iterator()
while ( iter.hasNext ) {
allUrlViews += iter.next()
}
// 清除狀態
urlState.clear()
// 按照 count 大小排序
val sortedUrlViews: ListBuffer[UrlViewCount] = allUrlViews.sortWith(_.count > _.count).take(nSize)
// 格式化成String列印輸出
val result: StringBuilder = new StringBuilder()
result.append("=========================================\n")
// 觸發定時器時,我們設定了一個延遲時間,這裡我們減去延遲
result.append("時間: ").append(new Timestamp(timestamp - 1)).append("\n")
for ( i <- sortedUrlViews.indices){
val currentUrlView: UrlViewCount = sortedUrlViews(i)
// 拼接列印結果
result.append("No").append(i+1).append(":")
.append(" URL=").append(currentUrlView.url).append(" ")
.append(" 流量=").append(currentUrlView.count).append("\n")
}
result.append("=========================================\n")
// 設定休眠時間
Thread.sleep(1000)
// 輸出結果
out.collect(result.toString())
}
}
執行效果
為了讓小夥伴們更好理解,菌哥基本每行程式碼都寫上了註釋,就衝這波細節,還不給安排一波三連?開個玩笑,回到主題上,我們再來討論一個問題。
實際生產環境中,我們的資料流往往是從Kafka獲取到的。如果要讓程式碼更貼近生產實際,我們只需將source更換為Kafka即可:
val properties = new Properties()
properties.setProperty("bootstrap.servers", "localhost:9092")
properties.setProperty("group.id", "consumer-group")
properties.setProperty("key.deserializer",
"org.apache.kafka.common.serialization.StringDeserializer")
properties.setProperty("value.deserializer",
"org.apache.kafka.common.serialization.StringDeserializer")
properties.setProperty("auto.offset.reset", "latest")
當然,根據實際的需要,我們還可以將Sink指定為Kafka、ES、Redis或其它儲存,這裡就不一一展開實現了。
參考
https://www.bilibili.com/video/BV1y54y127h2?from=search&seid=5631307517601819264
小結
本期內容主要為大家分享瞭如何基於flink在電商使用者行為分析專案中對實時流量統計模組進行開發的過程,這個跟上一期介紹的實時熱門商品統計功能非常類似,對本期內容不太理解的小夥伴可以多研究上一期的精彩內容~下一期我們會介紹專案中惡意登入監控的功能開發,敬請期待!你知道的越多,你不知道的也越多,我是Alice,我們下一期見!
受益的朋友記得三連支援小菌!
文章持續更新,可以微信搜一搜「 猿人菌 」第一時間閱讀,思維導圖,大資料書籍,大資料高頻面試題,海量一線大廠面經…期待您的關注!
相關文章
- 基於flink的電商使用者行為資料分析【2】| 實時熱門商品統計
- Flink-電商使用者行為分析(實時對賬)
- 基於flink的電商使用者行為資料分析【4】| 惡意登入監控
- Flink SQL結合Kafka、Elasticsearch、Kibana實時分析電商使用者行為SQLKafkaElasticsearch
- B站基於Flink的海量使用者行為實時ETL實踐
- 【Flink】基於 Flink 的流式資料實時去重
- 基於 Flink CDC 的實時同步系統
- 基於 Flink 的小米資料整合實踐
- OLAP引擎:基於Druid元件進行資料統計分析UI元件
- 基於大資料的使用者行為預測大資料
- Airwallex 基於 Flink 打造實時風控系統AI
- 基於 Flink CDC 打造企業級實時資料整合方案
- 基於 Flink 流計算實現的股票交易實時資產應用
- 基於雲原生的大資料實時分析方案實踐大資料
- 基於 Flink CDC 的現代資料棧實踐
- 基於實時計算(Flink)與高斯模型構建實時異常檢測系統模型
- 基於 Flink 的實時資料消費應用 / 功能質量保障方法
- B站基於ClickHouse的海量使用者行為分析應用實踐
- 基於 Flink 的實時數倉生產實踐
- 汽車之家基於 Apache Flink 的跨資料庫實時物化檢視探索Apache資料庫
- 基於 Apache Flink 的實時計算資料流業務引擎在京東零售的實踐和落地Apache
- 基於 Spark 的資料分析實踐Spark
- 攜程基於Flink的實時特徵平臺特徵
- 以Lgwr Worker為例,基於Strace 分析 Oracle 資料庫行為的方法Oracle資料庫
- DT時代,如何成為一名合格的電商資料分析師?
- 伴魚基於 Flink 構建資料整合平臺的設計與實現
- 基於Hive的大資料分析系統Hive大資料
- 錢大媽基於 Flink 的實時風控實踐
- 大資料實戰:電商該如何利用大資料獲取流量?大資料
- 基於Apache Hudi + Flink的億級資料入湖實踐Apache
- 基於Spark對消費者行為資料進行資料分析開發案例Spark
- 電商系統商品資料表設計分析與總結
- 基於CarbonData的電信時空大資料探索大資料
- 日處理資料量超10億:友信金服基於Flink構建實時使用者畫像系統的實踐
- GaussDB(DWS)基於Flink的實時數倉構建
- 基於flink和drools的實時日誌處理
- 快手基於 Apache Flink 的實時數倉建設實踐Apache
- 大資料“重磅炸彈”:實時計算框架 Flink大資料框架