用戶行為分析系統(tǒng)設(shè)計與實現(xiàn):從日志采集到畫像構(gòu)建)
1. 這個項目到底在解決什么問題先聊聊我為什么對一個“校園網(wǎng)用戶行為分析系統(tǒng)”這么感興趣。做了這么多年大數(shù)據(jù)相關(guān)的東西我越來越覺得校園網(wǎng)這種場景被嚴(yán)重低估了——它不像電商有海量交易也不像短視頻有超高并發(fā)但它有一個極其稀缺的東西全量、連續(xù)、帶真實身份映射的網(wǎng)絡(luò)行為數(shù)據(jù)。你想想全校幾千甚至幾萬學(xué)生從早上睜開眼刷手機到晚上熄燈斷網(wǎng)每一次HTTP請求、每一個DNS查詢、每一段TCP連接、每一次登錄認(rèn)證全都真實地記錄在校園網(wǎng)的核心設(shè)備和認(rèn)證服務(wù)器上。這些數(shù)據(jù)有多“干凈”它不像互聯(lián)網(wǎng)側(cè)用戶行為數(shù)據(jù)那樣充斥著爬蟲、機器人、廣告流量和惡意程序它對應(yīng)的是一個真實的人——學(xué)號、姓名、宿舍、院系、年級全都綁得死死的。這就是為什么“基于大數(shù)據(jù)的校園網(wǎng)用戶行為分析系統(tǒng)的設(shè)計與實現(xiàn)”值得做也值得認(rèn)真寫一篇完整的技術(shù)拆解。這個項目本質(zhì)上解決的是三類痛點第一類是網(wǎng)絡(luò)運維側(cè)網(wǎng)管人員想知道網(wǎng)絡(luò)到底卡在哪、誰在占帶寬、什么應(yīng)用在跑、什么時候是高峰但傳統(tǒng)SNMP流量監(jiān)控只能看到設(shè)備級的吞吐率看不到“人”和“行為”第二類是教學(xué)管理側(cè)學(xué)工部想知道學(xué)生是否沉迷游戲、是否存在深夜上網(wǎng)影響次日出勤的情況但人工抽查只能靠逮逮不到規(guī)律第三類是技術(shù)平臺側(cè)那么多網(wǎng)絡(luò)設(shè)備、認(rèn)證系統(tǒng)、日志平臺各自為政沒有一個統(tǒng)一視角把用戶、應(yīng)用、時間、位置串起來看。這個系統(tǒng)的核心價值就是把一堆無序的網(wǎng)絡(luò)日志變成有人物畫像、有時間軸、有行為標(biāo)簽的結(jié)構(gòu)化數(shù)據(jù)資產(chǎn)。你可以把它理解成一個“用戶全息行為雷達(dá)”每個上網(wǎng)的人都變成了一條連續(xù)的行為軌跡。所以這篇文章不是給你講“大數(shù)據(jù)概念”而是完完整整拆解這套系統(tǒng)怎么設(shè)計、怎么做技術(shù)選型、數(shù)據(jù)從哪來、清洗規(guī)則怎么定、后端架構(gòu)怎么搭、可視化怎么做、踩過哪些坑。無論你是做大數(shù)據(jù)畢業(yè)設(shè)計需要完整思路參考還是有真實的校園網(wǎng)運維背景想構(gòu)建一套分析平臺這篇文章都能讓你少走大量彎路。2. 系統(tǒng)整體設(shè)計與思路拆解2.1 從網(wǎng)絡(luò)日志到用戶行為的四層架構(gòu)很多剛開始接觸這類系統(tǒng)的人會陷入一個誤區(qū)一上來就翻Hadoop和Spark的文檔先搭一套大數(shù)據(jù)集群再說。但真正動過手的人都知道脫離了具體數(shù)據(jù)源和業(yè)務(wù)目標(biāo)的大數(shù)據(jù)平臺就是一臺昂貴的碎紙機——你喂進(jìn)去什么它攪碎什么根本產(chǎn)生不了洞見。我在設(shè)計這套系統(tǒng)時用的是四層邏輯架構(gòu)先把“數(shù)據(jù)怎么流動”這件事徹底想清楚再決定每一層用什么技術(shù)去承接。第一層是數(shù)據(jù)采集層。校園網(wǎng)環(huán)境里無非這幾類數(shù)據(jù)源核心交換機上的NetStream/sFlow流量采樣、認(rèn)證網(wǎng)關(guān)常見的有深瀾、銳捷、H3C等廠商的認(rèn)證計費系統(tǒng)產(chǎn)生的RADIUS日志、DNS服務(wù)器的解析日志、HTTP出口代理或上網(wǎng)行為管理設(shè)備的審計日志。每類數(shù)據(jù)的格式、粒度、時間基準(zhǔn)、編碼方式都完全不一樣。例如RADIUS日志記錄的是用戶上下線的起止時間、IP分配情況、MAC地址、NAS設(shè)備編號而NetStream記錄的只是一條條IP五元組的流量統(tǒng)計兩者必須要靠IP和時間的join才能把“誰”和“什么行為”關(guān)聯(lián)起來。第二層是數(shù)據(jù)存儲層。這里一定不能用“一套數(shù)據(jù)庫打天下”的思路。因為同時存在三種截然不同的數(shù)據(jù)類型原始日志適合放分布式文件系統(tǒng)或消息隊列做短期緩沖和離線歸檔清洗后的結(jié)構(gòu)化用戶行為記錄適合放在能支持多維聚合分析的OLAP引擎中那些需要實時看板展示的指標(biāo)比如當(dāng)前在線人數(shù)、總帶寬占用、熱門應(yīng)用排行則需要一套支持高并發(fā)點查的KV存儲或者時序數(shù)據(jù)庫。第三層是行為分析層。這是整個系統(tǒng)的靈魂。行為分析不能只停留在“統(tǒng)計了每個用戶用了多少流量”這種層面而要細(xì)化為“訪問時長、活躍時段、應(yīng)用偏好、異常行為、業(yè)務(wù)軌跡”等多個維度的聯(lián)合挖掘。比如某個用戶每天凌晨兩點到四點都有持續(xù)大流量下載行為這不能簡單定性為“熬夜”還需要結(jié)合目標(biāo)地址、端口、協(xié)議特征判斷是BT下載、視頻緩存還是正常的科研數(shù)據(jù)傳輸。分析層需要把機器學(xué)習(xí)中的聚類算法、時間序列分解、孤立森林異常檢測等技術(shù)真正落到校園網(wǎng)行為的語義上。第四層是可視化應(yīng)用層。分析結(jié)果最終要交付給三類不同的角色網(wǎng)絡(luò)運維人員關(guān)心的是“現(xiàn)在網(wǎng)絡(luò)是否健康、哪里需要擴(kuò)容”學(xué)工管理者關(guān)心的是“名冊上某個學(xué)生近期網(wǎng)絡(luò)生活是否規(guī)律、有沒有高風(fēng)險行為”校領(lǐng)導(dǎo)關(guān)心的是“整個校園網(wǎng)資源的利用效率和未來投入方向”。這些角色的關(guān)注點完全不同因此前端不能只做一套大屏必須按角色拆分視圖。這個四層架構(gòu)最核心的設(shè)計原則是“先建模后遷移先離線后實時”。就是說在項目落地初期不要盲目追求流式計算先用離線的全量數(shù)據(jù)把模型跑通、把指標(biāo)體系定義好等業(yè)務(wù)方認(rèn)可了輸出的結(jié)果再把鏈路改造成準(zhǔn)實時甚至實時。2.2 為什么不能用傳統(tǒng)關(guān)系型數(shù)據(jù)庫硬扛在這個項目的前期調(diào)研中我特地拿真實場景測過一輪MySQL/PostgreSQL等傳統(tǒng)關(guān)系型數(shù)據(jù)庫的承載能力。一個2萬在校生規(guī)模的高校假設(shè)平均每天活躍用戶1.2萬按每用戶每天產(chǎn)生8000條網(wǎng)絡(luò)訪問記錄算一天的原始行為記錄就是近1億條一個月的存儲量輕松突破25億行。關(guān)系型數(shù)據(jù)庫遇到這種量級有幾個繞不過去的坎。首先是寫入瓶頸單機MySQL在普通SSD上穩(wěn)定寫入也就每秒幾千到一萬行左右而校園網(wǎng)在晚高峰時每秒產(chǎn)生的日志量可以到兩萬條以上寫入必然排隊積壓。其次是聚合查詢效率比如“統(tǒng)計過去30天每天各院系的平均在線時長”這種在分析場景里極其常見的SQL對應(yīng)的是數(shù)十億行的范圍掃描加GROUP BY普通索引完全無用跑一個查詢能把數(shù)據(jù)庫鎖死十幾分鐘直接拖垮在線業(yè)務(wù)。第三是橫向擴(kuò)展的難度雖然MySQL集群和分庫分表能擴(kuò)展寫入能力但按用戶ID或者IP段去分片之后跨分片的聚合計算會變得極其痛苦運維復(fù)雜度也呈指數(shù)級上升。所以這套系統(tǒng)在設(shè)計上堅定地把“日志存儲與行為分析”放到了大數(shù)據(jù)技術(shù)棧上。離線存儲用HDFS或者云對象存儲來存原始日志數(shù)據(jù)倉庫層用Hive或者Doris這類組件做分區(qū)表和分桶表的統(tǒng)一管理查詢分析引擎則放到StarRocks或ClickHouse這類MPP數(shù)據(jù)庫上。我這么說不是勸你徹底拋棄關(guān)系型數(shù)據(jù)庫。實際上系統(tǒng)里仍然保留了MySQL它用來存用戶基礎(chǔ)信息、學(xué)號與IP的綁定關(guān)系、院系班級層級結(jié)構(gòu)、預(yù)警規(guī)則配置等元數(shù)據(jù)和維度數(shù)據(jù)。這套方案的本質(zhì)是“各司其職”MySQL管維度、管配置、管事務(wù)大數(shù)據(jù)組件管事實、管日志、管批量聚合。3. 核心細(xì)節(jié)解析與實操要點3.1 數(shù)據(jù)源接入校園網(wǎng)日志必須解決的對齊難題校園網(wǎng)日志接入是整個系統(tǒng)中最容易出現(xiàn)“Garbage in, garbage out”的環(huán)節(jié)也是拉開真實項目與虛構(gòu)設(shè)計之間差距的分水嶺。從真實環(huán)境看最麻煩的痛點是多源時間不同步。我踩過一個至今記憶猶新的坑出口防火墻的設(shè)備時鐘和認(rèn)證服務(wù)器的時間差了4分鐘起初覺得4分鐘誤差無傷大雅但在做“用戶斷線后是否仍有流量”的行為分析時這4分鐘的偏移會直接導(dǎo)致幾百個用戶被誤判為“離線后仍有異常流量”。排查了很久最后把所有網(wǎng)絡(luò)設(shè)備統(tǒng)一配置NTP時間同步并把數(shù)據(jù)接入層每個數(shù)據(jù)源的時鐘偏移做成可監(jiān)控的指標(biāo)——如果哪臺設(shè)備的時間偏移超過30秒立刻觸發(fā)告警。第二個痛點是IP地址的動態(tài)回收。校園網(wǎng)大量使用DHCP動態(tài)分配一個學(xué)生在一天內(nèi)可能經(jīng)歷好幾次IP變更。如果只是簡單聚合IP上的流量你統(tǒng)計到的“用戶行為”其實是多個人混在一起。解決辦法是把RADIUS認(rèn)證日志中每次上下線事件作為一個會話窗口在這個窗口期內(nèi)IP和用戶學(xué)號形成穩(wěn)定映射。后續(xù)所有流量數(shù)據(jù)的聚合都必須先執(zhí)行“會話關(guān)聯(lián)”通過IP和時間戳把流量日志映射回帳號維度。這個關(guān)聯(lián)邏輯可以用流式方式實現(xiàn)偽代碼表達(dá)大致是這個邏輯case class RadiusEvent(acctId: String, userId: String, userIp: String, onlineTime: Long, offlineTime: Long, nasPort: String) case class FlowRecord(userIp: String, timestamp: Long, destIp: String, destPort: Int, protocol: String, bytes: Long) def buildUserSession(radiusEvent: RadiusEvent, flowRecords: Dataset[FlowRecord]) : Dataset[UserBehaviorRecord] { flowRecords .where(col(userIp) radiusEvent.userIp) .where(col(timestamp).between(radiusEvent.onlineTime, radiusEvent.offlineTime)) .map { record UserBehaviorRecord( userId radiusEvent.userId, onlineSession radiusEvent.acctId, timestamp record.timestamp, destIp record.destIp, destPort record.destPort, protocol record.protocol, bytes record.bytes ) } }這段代碼的邏輯不復(fù)雜但真正實現(xiàn)的時候要考慮到一個現(xiàn)實離線批處理的方式處理每天的認(rèn)證記錄和日志文件拼接至少需要20分鐘無法滿足運營方的“想看今天上午實時情況”的需求。在實際架構(gòu)里我把實時產(chǎn)生的認(rèn)證事件推入Kafka用Flink做流式會話狀態(tài)維護(hù)流量日志則以一分鐘滾動窗口的方式從采集器推送進(jìn)來做流式關(guān)聯(lián)。這套方案實測定格在秒級延遲完全夠用。3.2 用戶行為畫像與關(guān)鍵標(biāo)簽體系設(shè)計有了“用戶ID 時間 目標(biāo)”的事實數(shù)據(jù)之后下一步就是構(gòu)建標(biāo)簽體系。這里需要強調(diào)一個認(rèn)知不要把畫像做成大而全的“人肉信息表”不要試圖去推斷學(xué)生的成績、性格、戀愛狀態(tài)這種跟網(wǎng)絡(luò)行為沒有直接因果關(guān)系的維度那是既不可行也沒有正當(dāng)性的。真正有業(yè)務(wù)價值且合規(guī)的標(biāo)簽應(yīng)當(dāng)集中在網(wǎng)絡(luò)使用模式這個范疇。我將標(biāo)簽體系設(shè)計為五個一級維度時間規(guī)律性標(biāo)簽早鳥型、夜貓子型、規(guī)律型、隨機型應(yīng)用偏好標(biāo)簽視頻文娛型、游戲競技型、學(xué)術(shù)科研型、社交溝通型、綜合均衡型流量消耗標(biāo)簽輕度用戶、中度用戶、重度下載用戶、異常突發(fā)用戶活躍區(qū)域標(biāo)簽宿舍區(qū)活躍、教學(xué)區(qū)活躍、圖書館活躍、跨區(qū)流動風(fēng)險行為標(biāo)簽連接數(shù)異常、短時高頻認(rèn)證失敗、訪問惡意域名、非業(yè)務(wù)時段大流量在具體實現(xiàn)上每個標(biāo)簽都是由底層行為統(tǒng)計指標(biāo)計算出來的。舉個例子“夜貓子型”的計算邏輯大致是提取用戶過去30天每天的按小時活躍度數(shù)據(jù)形成一個24維的行為向量用K-Means聚類算法把全體用戶聚類為若干個典型作息群體算法自動把凌晨活躍占比高的群體標(biāo)記為“夜貓子”再用一個連續(xù)7天的滑動窗口判斷用戶當(dāng)前作息類型是否發(fā)生偏移。這個方案比簡單設(shè)定“零點后流量超過30%就判夜貓子”要科學(xué)得多因為不同學(xué)校的熄燈時間、年級課表結(jié)構(gòu)差異太大了聚類可以做到數(shù)據(jù)自適應(yīng)。在設(shè)計畫像的時候要特別注意時間窗口的滑動與衰減。一次性的全量結(jié)算是沒有生命力的。用戶今天的行為只能微弱地影響他當(dāng)日的標(biāo)簽但過去三個月的長期活躍模式才是畫像的基石。我為每個標(biāo)簽配置了一個時間衰減權(quán)重表例如近7天行為權(quán)重為1.08到30天為0.631到90天為0.3超過90天的歷史行為權(quán)重僅為0.1。這樣一來每逢寒暑假結(jié)束后學(xué)生的標(biāo)簽會在大約兩周內(nèi)平滑地切換到新的學(xué)期行為模式而不是因為假期里某一天的突發(fā)下載行為而長期被打上“重度下載用戶”的錯誤標(biāo)簽。3.3 離線鏈路與實時鏈路的協(xié)同工作方式一套完整的“大數(shù)據(jù)”系統(tǒng)如果不區(qū)分離線與實時鏈路遲早會在吞吐和時效上翻車。我的做法是把整個系統(tǒng)做成了雙鏈路并行、結(jié)果在服務(wù)層統(tǒng)一出口的格局。離線鏈路使用Apache Hive或者Doris的External Table直接掛在HDFS上每天凌晨通過調(diào)度系統(tǒng)觸發(fā)全量回補計算和T1的指標(biāo)匯總產(chǎn)出的結(jié)果寫入StarRocks的明細(xì)表和聚合表供前端的“歷史趨勢看板”和“學(xué)生月度行為報告”查詢。離線計算的優(yōu)點是穩(wěn)定、容錯、可重算缺點是慢一天的數(shù)據(jù)要算到第二天的凌晨2點才能全部完成但這不重要因為任何一個學(xué)工老師都不會要求“實時看到昨天之前的每個歷史時刻總量”他們要的是準(zhǔn)確。實時鏈路則是獨立的一條Flink Streaming作業(yè)鏈。Kafka里面的認(rèn)證事件和五分鐘粒度的流量聚合日志經(jīng)過狀態(tài)計算后寫入Doris的主鍵模型表。這一路負(fù)責(zé)支撐的是“此刻校園網(wǎng)運行態(tài)勢大屏”和“實時異常行為預(yù)警”。實時鏈路的計算復(fù)雜度必須嚴(yán)格控制——只做單事件的規(guī)則判斷和短窗口內(nèi)聚合凡是需要超過30分鐘窗口的復(fù)雜行為模式分析一律交還給離線鏈路。兩張鏈路產(chǎn)出的數(shù)據(jù)在服務(wù)層還要做一次合并和沖突消解原則是“實時數(shù)據(jù)先行展示離線數(shù)據(jù)次日校準(zhǔn)”。例如大屏上顯示的“當(dāng)前全網(wǎng)在線人數(shù)”實時鏈路給的是即時的精確計數(shù)離線鏈路則會結(jié)合當(dāng)日認(rèn)證日志做一個校準(zhǔn)如果發(fā)現(xiàn)凌晨某個時段Kafka曾發(fā)生短暫積壓導(dǎo)致計數(shù)偏低就會在第二天的歷史曲線上把這段數(shù)據(jù)補準(zhǔn)。這樣既保證了數(shù)據(jù)新鮮度也維護(hù)了最終一致性。4. 技術(shù)選型解析與前后端落地4.1 大數(shù)據(jù)組件怎么選才不踩坑每次聊這種系統(tǒng)總有朋友問為什么不用Hadoop生態(tài)全家桶。我的建議非常直接能用輕量MPP數(shù)據(jù)庫解決的就不要為了“顯示技術(shù)棧完整”而盲目引入Spark、Hive、HBase、YARN那一整套重組件。原因很現(xiàn)實你首先得有人會運維這套集群其次這些組件跑起來的資源開銷很大最后它們各自都有各自的版本兼容陷阱很可能你折騰了三周還沒把Hive和Spark的告警噪音壓下去。針對校園網(wǎng)行為分析這個場景實際數(shù)據(jù)量和查詢模式是有明顯邊界的日均日志一億條上下、事實表每月幾十億行、維度表最高幾萬行、高并發(fā)查詢集中在最近一個月的匯總和明細(xì)。這個量級一套StarRocks就能輕松覆蓋而且StarRocks自帶列式存儲、自適應(yīng)索引、向量化執(zhí)行和非常完善的分區(qū)分桶機制在標(biāo)準(zhǔn)服務(wù)器上單表千億級別的聚合查詢都能做到秒級響應(yīng)。我用StarRocks做了兩個關(guān)鍵設(shè)計。一是明細(xì)表按天做分區(qū)每個分區(qū)內(nèi)按用戶ID哈希分桶分桶數(shù)設(shè)為集群BE節(jié)點數(shù)的3倍這樣既能保證數(shù)據(jù)分布均勻又能在查詢時做本地聚合減少網(wǎng)絡(luò)Shuffle。二是針對“實時更新用戶最新標(biāo)簽”的場景使用主鍵模型表主鍵就是用戶ID每次流入的畫像計算結(jié)果直接覆蓋更新。整個數(shù)據(jù)鏈路從采集到應(yīng)用我最終確定了這樣一套選型組合日志采集和解析Filebeat Logstash純?nèi)罩静杉洼p度清洗消息隊列Kafka 2.8承擔(dān)日志緩沖與解耦保存最近3天原始數(shù)據(jù)流式計算Flink 1.15統(tǒng)計實時在線、分鐘級流量聚合、異常告警觸發(fā)離線數(shù)據(jù)湖存儲HDFS保存全部原始日志保留6個月過期歸檔冷存儲OLAP查詢引擎StarRocks 3.0明細(xì)與匯總模型并存關(guān)系型元數(shù)據(jù)庫MySQL 8.0用戶基礎(chǔ)檔案與規(guī)則配置一張mysql實例即可可視化Vue 3 ECharts DataV對接后端Restful服務(wù)這套組合沒有引入任何偏門組件全是社區(qū)活躍度和資料豐富度最高的開源項目即使你的團(tuán)隊之前完全沒有大數(shù)據(jù)開發(fā)經(jīng)驗按照官方文檔也能在兩三周內(nèi)完成環(huán)境搭建。4.2 后端服務(wù)接口抽象與模塊化拆分后端業(yè)務(wù)服務(wù)的職責(zé)是“用自己的話解釋數(shù)倉里的數(shù)據(jù)”而不是把SQL查詢裸奔暴露給前端。我按業(yè)務(wù)域拆分了五個微服務(wù)雖然微服務(wù)在這類系統(tǒng)里顯得有些小題大做但考慮到后續(xù)可能的擴(kuò)展和維護(hù)模塊邊界清晰的好處值得買單。用戶畫像服務(wù)負(fù)責(zé)根據(jù)用戶ID、學(xué)號或院系維度查詢畫像標(biāo)簽和歷史行為軌跡。這個服務(wù)查詢的StarRocks主鍵模型表接口基本就是一次點查加若干維度屬性的拼裝。一個典型的接口返回長這樣{ userId: 202301012345, deptName: 計算機科學(xué)與技術(shù)學(xué)院, grade: 2023級, behaviorType: 夜貓子型, appPrefTags: [視頻文娛, 游戲競技], recent7DayAvgOnlineMinutes: 312, trends: [ { date: 2025-03-17, onlineMinutes: 180, activeHours: 8 }, { date: 2025-03-18, onlineMinutes: 425, activeHours: 12 } ] }運維監(jiān)控服務(wù)則要承擔(dān)更純粹的指標(biāo)查詢例如全網(wǎng)總帶寬、TopN應(yīng)用占比、各AP區(qū)域在線終端數(shù)、認(rèn)證成功率等。這些查詢在StarRocks上寫起來就是簡單的SUM和GROUP BY但為了不把StarRocks壓垮服務(wù)層必須內(nèi)置結(jié)果緩存熱點看板的SQL結(jié)果緩存時間為30秒到5分鐘不等。預(yù)警服務(wù)則需要一個獨立的規(guī)則引擎。我把預(yù)警規(guī)則的元數(shù)據(jù)結(jié)構(gòu)化存儲在MySQL里用Groovy腳本定義觸發(fā)條件運行時加載到內(nèi)存中匹配流入的實時指標(biāo)。規(guī)則引擎的好處是運維人員可以隨時新增規(guī)則而不用修改代碼、重新發(fā)布服務(wù)。以前這種系統(tǒng)最怕的就是業(yè)務(wù)方提個新需求就要走變更流程規(guī)則化配置后配置在后臺界面點點鼠標(biāo)就能下發(fā)。這套服務(wù)在技術(shù)實現(xiàn)上我用的Spring Boot 3.xJDK 17Spring Cloud Alibaba作為微服務(wù)基礎(chǔ)件。有人可能覺得這個系統(tǒng)用單體就夠了沒必要微服務(wù)化。坦白說如果只是校內(nèi)自用單體確實夠但考慮到后續(xù)可能對接學(xué)校統(tǒng)一身份認(rèn)證、一卡通數(shù)據(jù)、教務(wù)系統(tǒng)中的課表數(shù)據(jù)每個數(shù)據(jù)源的接入都是一個獨立的演進(jìn)方向用微服務(wù)把領(lǐng)域邊界隔離能在后續(xù)擴(kuò)展時少一點“牽一發(fā)而動全身”的恐懼。5. 核心功能模塊的詳細(xì)實現(xiàn)路徑5.1 認(rèn)證日志與流量日志的會話關(guān)聯(lián)實現(xiàn)這是整個項目最硬核的一段值得從頭到尾講清楚。校園網(wǎng)環(huán)境中的數(shù)據(jù)流分為兩類。一類是認(rèn)證計費系統(tǒng)記錄的用戶上下線報文它解決“用戶張三在某個時間段內(nèi)使用了某IP”這一問題。另一類是網(wǎng)絡(luò)出口的流量日志它只記錄IP和IP之間的通信特征完全不知道MAC和學(xué)號的存在。系統(tǒng)必須把這兩套數(shù)據(jù)按時間字段進(jìn)行拼接。會話關(guān)聯(lián)合并后原始的行為事實表結(jié)構(gòu)大致為字段用戶ID、學(xué)號、會話起始時間、會話結(jié)束時間、源IP、目標(biāo)IP、目標(biāo)端口、應(yīng)用協(xié)議、上行流量、下行流量、訪問域名。這里的難點有兩層一是RADIUS的離線報文并不總是及時到達(dá)經(jīng)常出現(xiàn)用戶下線了但離線包延遲二十分鐘才上報的情況導(dǎo)致關(guān)聯(lián)到該會話的流量記錄必須等都到齊后才能完整聚合。二是當(dāng)用戶跨AP漫游時IP可能保持不變但NAS設(shè)備和端口號會變?nèi)绻耆础癐P時間窗口”關(guān)聯(lián)可能把多個終端上的不同人合并為一個會話。我的實際應(yīng)對方案是引入“四元組會話切分”邏輯以用戶賬號、認(rèn)證NAS標(biāo)識、認(rèn)證時間、用戶IP為依據(jù)切分會話而非簡單地用IP起止時間。只有同一賬號在同一設(shè)備上使用同一IP的連續(xù)時間段才被判定為一個穩(wěn)定會話。這套邏輯在流式計算中實現(xiàn)時會用到Flink的KeyedState以用戶賬號和IP作為聯(lián)合主鍵維護(hù)每個活躍會話的當(dāng)前狀態(tài)并設(shè)置空閑超時時間為15分鐘——如果用戶超過15分鐘沒有新的流量就強制終將會話標(biāo)記為“待關(guān)閉”后續(xù)到達(dá)的流量則劃入新會話。這個設(shè)計經(jīng)得起實踐的檢驗。在試運行階段的一次抽樣驗證中把系統(tǒng)通過會話關(guān)聯(lián)計算出的用戶在線總時長與認(rèn)證計費系統(tǒng)自帶的停機時長報表做對比誤差被控制在了2%以內(nèi)。這2%的誤差主要來自跨零點會話被自然切分到兩個自然日的統(tǒng)計差異屬于可接受范圍。5.2 標(biāo)簽實時計算與孤立森林異常檢測標(biāo)簽計算分為批量和在線兩個通道。批量通道在每日凌晨離線跑使用用戶過去30天的數(shù)據(jù)做K-Means聚類和標(biāo)簽推斷結(jié)果寫入StarRocks主鍵模型實時通道則是對新流入的數(shù)據(jù)做增量計數(shù)一旦某個指標(biāo)超過規(guī)則閾值就會觸發(fā)標(biāo)簽的暫態(tài)更新。這么說可能有點抽象用一個例子說明某個白天的正常用戶在凌晨被檢測到持續(xù)FTP下載大文件實時通道會立刻在當(dāng)前畫像上附加一個“即時異常下載”標(biāo)記但這個標(biāo)記有個3小時有效期如果三小時內(nèi)沒有持續(xù)異常標(biāo)記自動過期畫像保持原來的“規(guī)律型”。異常行為檢測部分除了簡單的閾值規(guī)則以外我還用到了孤立森林算法來識別多維特征上的離群點。為什么要用孤立森林而不用傳統(tǒng)的Z-Score或者3Sigma因為校園網(wǎng)行為數(shù)據(jù)非常稀疏且分布極不規(guī)則。就拿“短時間DNS請求次數(shù)”來說正常用戶在正常瀏覽時的分布已經(jīng)是一個極大右偏分布少數(shù)用戶使用DNS隧道或者跑P2P資源發(fā)現(xiàn)時產(chǎn)生的請求量會高出幾個數(shù)量級但高值本身在分布里不一定屬于“對稱分布下的離群”。孤立森林的優(yōu)點在于它通過隨機切分特征空間能快速地將那些在多個維度上都顯得“隔離”的點標(biāo)記出來而不用假設(shè)數(shù)據(jù)服從正態(tài)分布。我在這個模塊提取了六個核心特征每分鐘新建連接數(shù)、每分鐘DNS請求數(shù)、每分鐘上行包數(shù)、每分鐘下行包數(shù)、訪問目標(biāo)IP的離散度、連接失敗比例。把每用戶的實時特征向量送入孤立森林模型模型輸出的異常分?jǐn)?shù)超過0.7時觸發(fā)預(yù)警。為了控制誤報率預(yù)警不會直接推送給學(xué)工老師而是先進(jìn)運維人員的人工復(fù)核隊列確認(rèn)后有價值再上報。這步設(shè)計是我在項目上線初期被誤報搞得差點失去信任之后加上的非常管用。5.3 可視化大屏和領(lǐng)導(dǎo)駕駛艙的搭建如果說前面的工作都是在凈化數(shù)據(jù)、沉淀數(shù)據(jù)那可視化的作用就是把數(shù)據(jù)“翻譯”成不同角色能秒懂的語言。運維視角的首頁我設(shè)計成了一張網(wǎng)絡(luò)運行態(tài)勢總覽大屏核心組件包括在線用戶數(shù)曲線、實時下行總帶寬、Top10熱門應(yīng)用分布、各出口鏈路健康度、認(rèn)證成功率趨勢、當(dāng)前告警列表。為了支撐這條大屏后端為每個組件單獨提供查詢接口ECharts通過WebSocket訂閱推送過來的增量數(shù)據(jù)繪圖環(huán)節(jié)基本不需要做復(fù)雜的前端計算。因為數(shù)據(jù)刷新頻率是秒級和傳統(tǒng)的離線報表有很大不同這里必須用WebSocket或SSE。我用Spring Boot內(nèi)置的WebSocket做服務(wù)端主動推送后端每5秒做一次微聚合把結(jié)果推送到前端連接上。為什么不在前端用ECharts的定時器輪詢因為一旦打開大屏的賬號多起來輪詢會無謂地放大查詢壓力。而采用服務(wù)端推送之后只有當(dāng)數(shù)據(jù)變化時才推送一次增量網(wǎng)絡(luò)開銷和數(shù)據(jù)庫壓力都明顯下降。領(lǐng)導(dǎo)駕駛艙則更加注重結(jié)論性而不是實時性。頁面上展示的是本月校園網(wǎng)運行摘要、各院系平均在線時長對比、帶寬資源利用趨勢、重大異常事件月度統(tǒng)計等。這個頁面完全不查消息隊列所有數(shù)據(jù)都從StarRocks的離線匯總表取數(shù)響應(yīng)時間目標(biāo)控制在800毫秒以內(nèi)。我還額外設(shè)計了一套“下鉆”權(quán)限校領(lǐng)導(dǎo)可以按住某個院系的柱狀圖向下逐層鉆取到班級但出于隱私保護(hù)原則下鉆到個人詳情頁面的入口只對學(xué)工系統(tǒng)中有明確授權(quán)賬號的人員開放。權(quán)限控制不是技術(shù)難事但它決定了這個系統(tǒng)能不能真正落地、會不會惹出合規(guī)麻煩所以我在設(shè)計之初就把它作為與功能同等重要的一環(huán)對待。6. 常見問題與排查技巧實錄6.1 Kafka消費積壓與背壓問題項目上線一個月后故障開始零星出現(xiàn)。最典型的就是Kafka消費者積壓每天早上八點學(xué)生集中起床使用網(wǎng)絡(luò)會出現(xiàn)一波持續(xù)約40分鐘的流量高峰實時鏈路如果某些時刻Flink作業(yè)的并行度不夠或者Sink端StarRocks寫入變慢消費位點就會開始拉大差距消費者延時會從幾秒鐘慢慢膨脹到二十分鐘以上。排查這類問題我第一件事是看Kafka的消費組Lag監(jiān)控判斷是哪個環(huán)節(jié)卡住。如果Lag只存在于某一個Flink作業(yè)上那大概率是這個作業(yè)里有大狀態(tài)或者某個算子成為瓶頸。有一回我發(fā)現(xiàn)Flink作業(yè)頻繁發(fā)生反壓定位到是StarRocks集群的導(dǎo)入配額打滿導(dǎo)致寫入變慢。解決辦法有兩個一是調(diào)大Stream Load的批量大小和并發(fā)數(shù)二是把StarRocks的BE節(jié)點擴(kuò)容。在真實的集群上純調(diào)優(yōu)只能解決問題的一部分流量持續(xù)增長之后該擴(kuò)容就得擴(kuò)容否則系統(tǒng)永遠(yuǎn)在“剛好夠用”的懸崖邊走路。另一個實用技巧是給關(guān)鍵Topic設(shè)置合理的分區(qū)數(shù)。我最初Kafka分區(qū)設(shè)置過小只有6個分區(qū)但Flink作業(yè)的并行度有12結(jié)果有6個空閑并行度毫無用處。后來把主題分區(qū)調(diào)整為與下游最大并行度對齊的12吞吐量甚至沒有增加任何一臺機器就直接翻倍。6.2 用戶隱私與數(shù)據(jù)脫敏的現(xiàn)實處理做這種系統(tǒng)最怕的不是技術(shù)完不成而是業(yè)務(wù)隱私的邊界沒守住。校園網(wǎng)用戶行為數(shù)據(jù)極其敏感哪怕你只是想分析“學(xué)生晚上一般幾點睡”背后牽涉到的都是在校學(xué)生的個人隱私。所以這個系統(tǒng)的隱私合規(guī)設(shè)計我從技術(shù)設(shè)計到上線推廣都繃著一根弦。數(shù)據(jù)接入層就把敏感字段做了分級處理。用戶明文學(xué)號和姓名只存在于MySQL元數(shù)據(jù)庫且這個庫只對后端必要的兩個服務(wù)開放網(wǎng)絡(luò)訪問其他服務(wù)一律不允許直接連接。進(jìn)入大數(shù)據(jù)平臺的行為事實表一律使用系統(tǒng)內(nèi)部生成的脫敏用戶ID作為主關(guān)聯(lián)鍵真實的學(xué)號絕不能出現(xiàn)在StarRocks、HDFS或Kafka的日志中。一旦出現(xiàn)數(shù)據(jù)采集端的校驗任務(wù)就會將當(dāng)批次數(shù)據(jù)丟棄并發(fā)送異常告警防止臟數(shù)據(jù)帶著敏感信息流入分析鏈路。對外查詢接口層面強制要求按照角色控制能查看的數(shù)據(jù)粒度和時間范圍。普通運維人員只能看到以“在線終端數(shù)”為單位的網(wǎng)絡(luò)狀態(tài)數(shù)據(jù)看不到任何個人標(biāo)簽輔導(dǎo)員能查看本學(xué)院學(xué)生的行為畫像摘要但無法下載明細(xì)數(shù)據(jù)校級管理員的所有查詢審計日志都要在后臺留存至少180天。這套權(quán)限體系是系統(tǒng)的準(zhǔn)生證什么時候權(quán)限管好了什么時候系統(tǒng)才算真正能走出運維部門走向全校各業(yè)務(wù)部門。6.3 StarRocks表模型選錯的代價我剛開始建表時為了圖省事把全部數(shù)據(jù)都放進(jìn)了StarRocks的明細(xì)模型結(jié)果在跑“計算每個用戶最近7天的活躍天數(shù)”時發(fā)現(xiàn)查詢經(jīng)常超時。后來經(jīng)過分析才意識到明細(xì)模型對這類多行歸一的聚合查詢是極其不利的因為每次查詢都需要掃描該用戶過去七天所有明細(xì)行進(jìn)行實時去重聚合。解決辦法分成兩步。第一步把日匯總數(shù)據(jù)放入聚合模型表比如每個用戶每天的總在線分鐘、總流量、總請求數(shù)通過模型內(nèi)置的AGGREGATE_KEY自動做同維度合并第二步實時標(biāo)簽類結(jié)果放入主鍵模型表保證同一個用戶的最新畫像只有一行記錄。這樣調(diào)整之后原本需要掃描幾百萬行的聚合查詢變成了掃描幾萬行的點查加小聚合查詢速度直接從8秒降低到150毫秒。星羅棋布的各種表模型其實不是設(shè)計上的花架子用對了才算是真正理解了OLAP場景。作為一個過來人的建議在StarRocks上建表之前先問自己一句——這張表要支撐的查詢到底是“看趨勢”還是“找明細(xì)”想清楚再動手能幫你少走太多彎路。7. 部署環(huán)境準(zhǔn)備與上線運維實戰(zhàn)7.1 硬件資源規(guī)劃與集群拓?fù)浣ㄗh很多初次搭建大數(shù)據(jù)平臺的人一看到“大數(shù)據(jù)”三個字就以為需要幾十臺高性能服務(wù)器組成集群這是一個極大的認(rèn)知誤區(qū)。針對2萬在校生規(guī)模的校園網(wǎng)行為分析場景我實測下壓測下來的硬件底線其實不高。以峰值并發(fā)在線1.5萬人、日均原始日志約1億條、保留180天原始數(shù)據(jù)來估算HDFS的存儲空間需求大致在25TB到30TB左右這個數(shù)字按當(dāng)前主流配置也就是4臺8TB數(shù)據(jù)盤服務(wù)器的裸容量。計算資源方面Flink的實時鏈路和StarRocks的查詢負(fù)載加一起讓我最終確認(rèn)了一個5節(jié)點起步的最小可行集群3臺數(shù)據(jù)節(jié)點兼計算節(jié)點同時運行StarRocks的BE進(jìn)程和HDFS的DataNode2臺獨立節(jié)點分別部署Flink的TaskManager管理節(jié)點則共用其中一臺低配虛擬機運行NameNode、Flink JobManager和StarRocks FE。操作系統(tǒng)按常規(guī)選擇CentOS 7.9或者Ubuntu 20.04 LTS都可以但有一個容易忽略的關(guān)鍵點就是內(nèi)核參數(shù)優(yōu)化。因為大數(shù)據(jù)組件普遍依賴網(wǎng)絡(luò)和文件句柄我統(tǒng)一把/etc/security/limits.conf中的nofile調(diào)到65535把vm.swappiness設(shè)為10以下并關(guān)閉了透明大頁。這些看似不起眼的參數(shù)直接影響著Flink做Checkpoint時是否會因為磁盤IO抖動導(dǎo)致超時。7.2 流量高峰期的性能調(diào)優(yōu)實戰(zhàn)性能調(diào)優(yōu)最有效的抓手不是盲目加機器而是看準(zhǔn)瓶頸在哪里。在我經(jīng)歷的高峰期優(yōu)化中有兩個調(diào)優(yōu)方向收益最顯著。第一個方向是調(diào)整Flink的Checkpoint間隔和狀態(tài)后端。校園網(wǎng)流量有明顯的波峰波谷晚上九點到十一點是全天最高峰該時段Flink作業(yè)處理的消息量能到白天低谷的8倍左右。如果把Checkpoint間隔設(shè)得太短高峰期每個Checkpoint都會消耗大量資源去做狀態(tài)快照和正常的消息處理搶帶寬。我把Checkpoint間隔從最初的30秒調(diào)整為90秒并開啟增量Checkpoint后高峰期的處理延遲直接下降了30%。第二個方向是StarRocks的查詢并發(fā)控制和緩存命中率。雖然StarRocks的并發(fā)能力很強但如果沒有查詢隊列限制幾個大查詢同時沖進(jìn)來會把CPU打滿反而拖累所有小查詢。我通過設(shè)置query_queue_concurrency_limit信號量參數(shù)限制同時執(zhí)行的查詢數(shù)量并對大屏常用查詢強制走結(jié)果緩存。經(jīng)過一輪壓測驗證線上大屏查詢P95響應(yīng)時間穩(wěn)定保持在800毫秒以內(nèi)這已經(jīng)是運維人員不需要再抱怨的水平了。8. 復(fù)盤心得與后續(xù)可擴(kuò)展的方向這套系統(tǒng)從需求梳理到上線穩(wěn)定運行前后花了接近五個月時間其中將近一半的時間其實不是花在寫代碼上而是花在“理解校園網(wǎng)的數(shù)據(jù)到底長什么樣”以及“把業(yè)務(wù)方的模糊描述翻譯成精確的技術(shù)規(guī)則”上面。如果說有什么值得后來者記住的血淚經(jīng)驗就是在項目動工之前務(wù)必找一個真正的在校學(xué)生把校園網(wǎng)的認(rèn)證流程、IP分配方式、出口鏈路拓?fù)浜土髁咳罩径纪暾孛宄@是整個系統(tǒng)所有上層建筑的地基。后續(xù)這個系統(tǒng)還有很大的擴(kuò)展空間。一個極具潛力的方向是結(jié)合一卡通刷卡和圖書館門禁數(shù)據(jù)構(gòu)建更加完整的校園行為軌跡比如通過比對“凌晨4點還在上網(wǎng)”與“上午8點食堂無刷卡記錄”兩個弱信號配合教務(wù)系統(tǒng)排查長期缺課風(fēng)險。另一個方向是將基于孤立森林的異常檢測擴(kuò)展為基于行為序列的深度模型從單點異常識別升級為行為軌跡異常的判別。不過這些擴(kuò)展開啟之前有一個前置問題必須思考清楚系統(tǒng)的分析結(jié)果到底如何使用才能既發(fā)揮數(shù)據(jù)價值又守住數(shù)據(jù)倫理邊界。我的原則向來是技術(shù)只能輔助判斷不能越界替代人的決策。最后再分享一個我自己在整個項目中最受益的小技巧堅持給每一張關(guān)鍵數(shù)據(jù)表都建立“血緣追蹤”從原始日志到明細(xì)寬表再到最終指標(biāo)每一次加工歷史都清晰可見。這樣做遇到數(shù)據(jù)對不上的問題能在一個小時內(nèi)準(zhǔn)確定位到是采集、清洗、關(guān)聯(lián)還是聚合哪一環(huán)出了問題。沒有這條血緣鏈路的大數(shù)據(jù)系統(tǒng)等到出了數(shù)據(jù)問題只能靠翻代碼加猜那排查的酸爽經(jīng)歷過的人都會懂。