欢迎光临
我们一直在努力

天气爬虫到Kafka Flume HBase大数据实时处理链路实战

简介:围绕天气数据采集到存储的完整链路,资源包内整理了一个可运行示例项目,适合正在学习大数据实时处理、希望打通数据采集与入库流程的初中级开发者。内容覆盖天气爬虫编写、Kafka消息队列分发、Flume消费并导入HBase,之后还通过Hive建立外部映射并用Superset做可视化分析,相当于一套小型大数据平台实践参考。

包体共17个文件,核心是11个Java源码文件,对应爬虫、Kafka生产者/消费者、Flume配置等主体逻辑;另含1个XML配置、1个Markdown说明文档和3张流程图/效果图,可辅助理解整体架构与运行结果;压缩包整体约985KB,体积小、便于下载后直接阅读。当前已有152人学习,适合作为课堂项目或毕业设计的起步模板。

读者可借此明确从数据源到存储、分析展示的实现路径,获得Kafka与Flume组合使用的关键配置思路,以及Hive与HBase映射操作和Superset对接的实际写法;相比零散教程,包内文件把主干代码、配置和说明集中在一起,能显著节省环境搭建和代码调试时间。

1. 天氣爬蟲採集,Kafka 實時分發,Flume 收集數據導入 HBase:這條鏈路能落地嗎

很多人第一眼看到「天氣爬蟲採集,kafka實時分發,flume 收集數據導入到 Hbase」這個資源包,以為重點是爬蟲代碼。其實真正難的,是讓一條從 HTTP 請求發出的天氣數據,穩定地流進 Kafka、寫入 HBase、再被 Hive 查出來、最後在 Superset 上畫成圖。這份 weather-mrs-master 項目恰好把整條鏈路串起來了:爬蟲抓數據,Kafka 做實時中轉,Flume 消費 Kafka 並寫入 HBase,Hive 與 HBase 建映射,Superset 做分析展示。你想在本地復現一個大數據實時處理項目,拿它當骨架是比較順手的選擇。適合正在學 Kafka、Flume、HBase 的開發者,也適合給簡歷補一個完整項目經驗的從業者。

2. 爬蟲採集模塊拆解:從 HTTP 請求到 Kafka 生產者,先解決數據從哪來

2.1 先看項目的 Maven 骨架:採集、配置、啟動三塊怎麼分工

打開資源包,根部是 pom.xml 、 README.md 、 src/main 。這說明整個項目用 Maven 管理,主體代碼在 Java 裡,不是 Python 腳本拼出來的。我一般會先看 pom.xml 裡依賴了哪些庫,能快速判斷作者意圖:有 httpclient 或 okhttp,說明採集用 HTTP 客戶端;有 jackson-databind,說明 JSON 解析走 Jackson;有 kafka-clients,說明生產者和消費者都走原聲 Kafka API。 src/main/java 下通常會拆成 collector 、 producer 、 config 、 util 幾個包, resources 裡放接口配置、topic 名稱這些可變參數。這個拆法比較樸素,但對於一個工程項目來說夠用。

天氣數據的來源,常見的是各地天氣服務商提供的 JSON 接口,比如按城市名查詢實時天氣,返回溫度、濕度、風向、天氣現象。有的免費接口每天有調用次數限制,有的需要申請 key。項目代碼一般把接口地址和 key 放在配置裡,不建議寫死在類裡,否則換數據源時要改代碼重新編譯。我在復現時會先在 README 裡確認作者默認用的哪個接口,再看 resources 下的配置文件。如果原項目沒有給具體接口,自己換一個能訪問的天氣 API 就行,爬蟲模塊的核心邏輯不受影響。

選數據源有一個原則:優先選返回結構簡單、字段穩定的 JSON 接口,不要選那種返回 HTML 還需要正則清洗的頁面。原因是後面鏈路很長,只要採集端多一個解析依賴,整個數據管道就多一個脆弱點。天氣接口的字段不多,溫度、濕度、天氣描述、更新時間,足以構成 HBase 一張表的列。如果要採集多個城市,還需要一個城市列表配置,代碼裡循環調用接口即可。項目的入口類一般就是一個帶 main 方法的啟動類,定時或手動觸發全量採集。

2.2 爬蟲抓取代碼:HttpClient 拉數據,Jackson 解析 JSON

採集模塊的核心是一個 HTTP 客戶端加一個 JSON 解析。用 HttpClient 發 GET 請求,拿到字符串後用 Jackson 轉成 JsonNode ,再逐字段取值。下面是一段我在類似項目裡常用的寫法,讀者可以直接對照資源裡的 WeatherFetcher 類看。

package com.weather.collector;

import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.jetbrains.annotations.NotNull;

import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;

public class WeatherFetcher {

private final CloseableHttpClient httpClient;

public WeatherFetcher() {
this.httpClient = HttpClients.custom()
.setConnectionTimeToLive(30, TimeUnit.SECONDS)
.setMaxConnPerRoute(20)
.build();
}

public String fetch(@NotNull String apiUrl) throws Exception {
HttpGet request = new HttpGet(apiUrl);
request.setHeader("User-Agent", "Mozilla/5.0 (weather-collector)");
request.setHeader("Accept", "application/json");
// 接口通常需要 1~3 秒響應,超時設太短會被對方誤判為攻擊
request.setConfig(RequestConfig.custom()
.setConnectTimeout(5000)
.setSocketTimeout(5000)
.build());
try (CloseableHttpResponse response = httpClient.execute(request)) {
int code = response.getStatusLine().getStatusCode();
if (code != 200) {
throw new RuntimeException("HTTP request failed, code=" + code);
}
return EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
}
}
}

這段代碼裡有兩個容易忽略的點:一個是 CloseableHttpClient 要複用,不要在每個請求裡 new 一個實例,否則連接池等於沒用,高頻採集時會出現大量 TIME_WAIT 連接,最終把本地端口耗盡。另一個是超時設置,天氣接口在早晚高峰時會抖動,5 秒超時比較合適。如果設置成 1 秒,稍微一慢就報異常,整個循環直接中斷;如果設置成 30 秒,一個接口掛了會把後續城市全部阻塞。這個「超時設多少」本身就是個坑,後面避坑章節會再提。

拿到 JSON 字符串後,用 Jackson 解析時要養成用 path() 而不是 get() 的習慣。 get() 遇到字段不存在會拋異常, path() 則返回一個空節點,這樣某個字段偶爾缺失不至於讓整個採集程序崩掉。

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.HashMap;
import java.util.Map;

public class WeatherParser {

private final ObjectMapper mapper = new ObjectMapper();

public Map<String, String> parse(String json, String city) throws Exception {
JsonNode root = mapper.readTree(json);
Map<String, String> record = new HashMap<>();
record.put("city", city);
// path 取值,缺失時返回 null,不會拋異常
record.put("temperature", root.path("now").path("temp").asText(""));
record.put("humidity", root.path("now").path("humidity").asText(""));
record.put("weather", root.path("now").path("text").asText(""));
record.put("wind", root.path("now").path("windDir").asText(""));
record.put("updateTime", root.path("updateTime").asText(""));
return record;
}
}

ObjectMapper 也要複用,它是線程安全的,每次 new 會增加無謂的開銷。 parse 方法把 JSON 轉成 Map<String, String> ,這樣後面無論是發 Kafka 還是寫 HBase,字段結構都統一了。這裡沒有把數據直接寫到文件或庫裡,因為這套項目的設計思路是「採集只負責產出數據」,後續交給 Kafka 和 Flume。這樣做的好處是採集模塊變得很純粹,換接口、加城市都不會影響下游鏈路。

2.3 發送 Kafka 的生產者參數:acks、batch.size、linger.ms 背後的取捨

採集到數據後,下一步是發給 Kafka。天氣數據量不算誇張,一個城市幾百字節,但如果你採 300 個城市、每分鐘一輪,壓力還是有的。生產者的參數能不能調好,直接決定 Kafka 會不會成為鏈路瓶頸。下面是一份生產者配置,參考的是 KafkaProducer 的標準寫法。

import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.serialization.StringSerializer;

import java.util.Properties;

public class WeatherProducer {

private final KafkaProducer<String, String> producer;

public WeatherProducer(String bootstrapServers) {
Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
// 可靠性參數:acks=1 表示 leader 寫成功就返回,想要更高的可靠性用 all
props.put(ProducerConfig.ACKS_CONFIG, "1");
props.put(ProducerConfig.RETRIES_CONFIG, 3);
// 吞吐參數:積累 16KB 或等待 5ms 再批量發送
props.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);
props.put(ProducerConfig.LINGER_MS_CONFIG, 5);
props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 33554432L);
producer = new KafkaProducer<>(props);
}

public void send(String topic, String city, String json) {
producer.send(new ProducerRecord<>(topic, city, json), (metadata, e) -> {
if (e != null) {
// 異步回調裡必須留日誌,不然發送失敗完全無感知
System.err.println("send failed, topic=" + topic + ", city=" + city, e);
}
});
}
}

參數要理解著調,不要照抄。 acks=1 是性能和可靠性的折中,Kafka 的 leader 把消息寫進日誌就返回給生產者,不需要等所有副本。如果數據不能丟,改成 all ,但延遲會增加。 retries 設 3 是防止網絡抖動,但重試可能導致消息亂序,所以最好配合 enable.idempotence=true 使用,這裡沒開,因為天氣數據對順序要求不高。 batch.size 和 linger.ms 是成對出現的,前者表示批次大小,後者表示等待時間,目的都是攢一批再發。對於這種小字段 JSON,5ms 延遲完全可接受。

這裡要提一個常見誤區:很多人把發送動作寫成同步阻塞, producer.send(…).get() ,這樣每請求一次就等一次網絡往返,採集 300 個城市會明顯變慢。正確做法是用異步回調,讓生產者內部自己批量發送。上面的 send 方法就是異步的,回調裡只打日誌。對天氣採集這種實時性要求不極端的場景,異步加 5ms 積累,吞吐完全夠用。項目裡如果把生產者封裝成單例,記得在程序退出時調用 producer.close() ,否則緩衝區裡的數據可能沒發完就被強制結束,這也算一個低級但常見的翻車點。

3. Kafka 實時分發鏈路:主題、分區與消費者 offset 的設計

3.1 Kafka 集群與主題創建:分區數和副本數怎麼定

數據從生產者出來,第一站是 Kafka topic。資源包裡一般不會帶集群環境,需要自己有一套 Kafka 可用。這一步我建議先在單機或三節點虛擬機上把 Kafka 起起來,然後用命令行工具創建主題。下面是最基礎的命令。

kafka-topics.sh –create \\
–bootstrap-server node01:9092,node02:9092,node03:9092 \\
–topic weather-topic \\
–partitions 6 \\
–replication-factor 1 \\
–config retention.ms=86400000

分區數設 6,是考慮到後面 Flume 可能開多個 agent 併發消費。如果只開一個 Flume agent,分區設 3 或 6 差別不大;如果打算橫向擴展多個 Consumer,分區數就是並發上限。副本數在生產環境至少 2 或 3,本地單機只能設 1,設大了會報 Replication factor: 2 larger than available brokers: 1 ,這是新手最容易撞到的報錯。 retention.ms=86400000 表示保留一天,天氣數據的時效性很強,沒必要留七天,保留時間太長反而佔磁盤。

創建完主題後,可以用 kafka-topics.sh –describe 檢查分區和副本狀態。確認 Isr 沒有異常,Leader 沒有傾斜,再繼續往下走。很多時候後面的 Flume 消費不到數據,不是 Flume 配錯,而是 topic 本身沒建好,或者生產者發到了另一個集群。先用命令把叢集狀況摸清,能省掉大量排查時間。

3.2 生產者與消費者的配置對照:別讓數據卡在上游

Kafka 的吞吐瓶頸往往不在 broker,而在生產者和消費者的配置不對。生產者要看 batch.size 、 linger.ms 、 buffer.memory ,消費者要看 fetch.min.bytes 、 max.poll.records 、 session.timeout.ms 。我把這個項目裡最值得調整的幾個參數列成對照,方便配置時參考。

環節 參數 推薦值 作用
生產者 acks 1 或 all 控制消息確認級別,天氣數據可容忍少量重試
生產者 linger.ms 5 等待 5ms 攢批,減少請求次數
生產者 batch.size 16384 批次大小,字節為單位
生產者 buffer.memory 33554432 生產者緩衝區總大小,爆了會阻塞 send
消費者 fetch.min.bytes 1 消費者拉取的最小數據量,調大可減少請求
消費者 max.poll.records 500 單次 poll 最大記錄數,調太大容易觸發 rebalance
消費者 session.timeout.ms 10000 消費者失聯判定時間,太短容易誤判
消費者 enable.auto.commit false 關閉自動提交,手動確認處理完再提交 offset

這個表裡最關鍵的是 enable.auto.commit 。很多項目默認開自動提交,處理邏輯還沒跑完就提交了 offset,進程一掛就丟數據。Flume 消費 Kafka 時也有對應配置,後面會講。生產者的 buffer.memory 容易被忽略,如果發送速度遠大於 Kafka 處理速度,緩衝區撐滿後 send 會阻塞,表現就是採集線程卡住不動。解決方法不是無限調大 buffer,而是找上游生產速率或下游消費瓶頸。

3.3 group.id 和 offset 策略:重啟不丟數的基本功

Kafka 消費者必須有 group.id,同一個 group 裡的消費者在邏輯上共享分區。Flume 作為 Kafka Source 消費時,如果不指定 group.id,默認會生成一個隨機 group,重啟後 resume 從最新的位置開始讀,導致漏數據。所以要養成固定 group.id 的習慣,比如 flume-weather-group 。

檢查消費位置有兩個常用命令。一個看 consumer group 當前的 lag,一個手動模擬消費驗證數據格式。

kafka-consumer-groups.sh –bootstrap-server node01:9092 \\
–group flume-weather-group –describe

kafka-console-consumer.sh –bootstrap-server node01:9092 \\
–topic weather-topic –from-beginning –max-messages 5

–describe 會輸出每個分區的 current-offset 、 log-end-offset 、 lag 三列。如果 lag 一直漲,說明消費者處理不過來;如果 lag 為 0,說明消費速度跟得上生產速度。這個命令是鏈路調試的必備工具。 kafka-console-consumer 驗證數據格式時,記得加 –max-messages ,不然在生產環境會一直刷屏。如果這裡能看到爬蟲採集到的 JSON,説明 Kafka 這一段沒問題,問題大概率出在 Flume 消費端。

offset 策略裡還有一個參數 auto.offset.reset 。新 group 第一次消費一個沒有 offset 的 topic 時, earliest 表示從頭開始, latest 表示從最新開始。對天氣數據項目,我建議用 earliest ,這樣 Flume 重啟或建新 group 時能把積壓數據補上。如果業務允許丟數據,再考慮 latest 。這個選擇本身要寫清楚,不然重啟一次就會發現丟了一堆「看起來無所謂」的數據。

4. Flume 收集數據導入 HBase:從 Kafka Source 到 HBase Sink 的完整配置

4.1 為什麼選 Flume 做管道:它和 Kafka Connect 的差別

鏈路裡選 Flume 是有道理的。Flume 本來就是為日誌而生,它的 agent 可以靈活定義 source、channel、sink,尤其適合「消費 Kafka 再寫 HBase」這種場景。你可能會問,直接用 Kafka Connect 的 HBase Sink 不就行了?Kafka Connect 也是一條路,但項目裡既然用 Flume,說明作者要的是輕量、可控、易改。Flume 配置是文本文件,改完重啟 agent 就生效,不需要寫 Java 代碼;而且 Flume 對後面接 HBase 的 serializer 擴展更直接,寫自定義類也方便。

Flume 的三段式結構要理解:source 從哪拿數據,channel 暫存數據,sink 把數據送出去。這個項目裡 source 是 KafkaSource,channel 可以選 memory 或 file,sink 是 HBaseSink。用 memory channel 吞吐高,但 agent 進程掛了 channel 裡的數據會丟;用 file channel 會持久化,但性能稍差。天氣數據重啟丟幾條影響不大,不過為了穩定,我一般用 file channel。下面配置會把兩者都寫出來,你自己取捨。

4.2 flume.conf 實操配置:Kafka Source + File Channel + HBaseSink

Flume agent 的配置最怕就是屬性名拼錯。KafkaSource 的參數前綴和普通 Kafka 消費者略有不同,很多人在 kafka.bootstrap.servers 和 bootstrap.servers 之間反覆試錯。下面是一份可以直接套用的配置,把關鍵參數標出來了。

agent.sources = kafka-source
agent.channels = file-channel
agent.sinks = hbase-sink

# Kafka Source:消費 weather-topic
agent.sources.kafka-source.type = org.apache.flume.source.kafka.KafkaSource
agent.sources.kafka-source.kafka.bootstrap.servers = node01:9092,node02:9092
agent.sources.kafka-source.kafka.topics = weather-topic
agent.sources.kafka-source.kafka.consumer.group.id = flume-weather-group
agent.sources.kafka-source.kafka.consumer.auto.offset.reset = earliest
agent.sources.kafka-source.batchSize = 100
agent.sources.kafka-source.batchDurationMillis = 2000

# File Channel:防止 agent 重啟丟數據
agent.channels.file-channel.type = file
agent.channels.file-channel.checkpointDir = /data/flume/checkpoint
agent.channels.file-channel.dataDirs = /data/flume/data
agent.channels.file-channel.capacity = 100000
agent.channels.file-channel.transactionCapacity = 1000

# HBase Sink:寫入 weather 表
agent.sinks.hbase-sink.type = org.apache.flume.sink.hbase.HBaseSink
agent.sinks.hbase-sink.table = weather
agent.sinks.hbase-sink.columnFamily = cf
agent.sinks.hbase-sink.serializer = org.apache.flume.sink.hbase.RegexHbaseEventSerializer
agent.sinks.hbase-sink.serializer.regex = (.*)
agent.sinks.hbase-sink.batchSize = 200

# 串起來
agent.sources.kafka-source.channels = file-channel
agent.sinks.hbase-sink.channel = file-channel

這份配置裡有幾個值得注意的地方。KafkaSource 的 kafka.consumer.group.id 決定了消費位置,一定要和前面第 3 章說的保持一致,不要讓 Flume 每次重啟都換 group。 batchSize 是 Flume 從 Kafka 一次拉多少條, batchDurationMillis 是等待多長時間,兩個配合起來控制消費節奏。如果設太大,KafkaSource 一次 poll 會阻塞比較久,HBase 端如果寫得慢,數據會堆在 channel 裡。file channel 的 capacity 是能緩存多少事件,我設 100000,因為 Kafka 裡如果突然湧入大量積壓數據,channel 太小會把 agent 直接卡死。HBase sink 的 serializer 控制每條事件怎麼變成 HBase 的 put, RegexHbaseEventSerializer 是按正則解析 event,如果你在生產者端發的是普通 JSON 字符串,最好在項目裡自定義一個 serializer,把 JSON 字段拆成 HBase 的列。資源包的 src/main/java 裡寫的 serializer 類就是幹這個用的。

配置寫完後,啟動命令是 flume-ng agent -n agent -c conf -f flume.conf 。這裡的 -n agent 必須和配置文件裡 agent.sources 的前綴一致,否則 Flume 會報「component not defined」。這是一個非常容易踩的初級坑。啟動後重點看日誌裡有沒有出現 KafkaSource 成功訂閱、 HBaseSink 成功創建 table 這類信息,沒有的話就要用下一節的 HBase 命令驗證數據到底寫沒寫進去。

4.3 HBase 表設計與預分區:RowKey 決定寫入速度

HBase 表設計決定了這條鏈路能不能穩定寫入。天氣表不需要複雜的列族,一個 cf 就夠。但 RowKey 一定要設計,否則所有數據都往一個 region 寫,會出現熱點,寫入延遲就會飆升。常見做法是用「城市 + 時間戳」或「城市反轉 + 時間戳」。如果用 city + timestamp 做 rowkey,同一城市的數據連續寫到一個 region,查詢方便,但寫入往單點壓。用 加鹽 或 哈希前綴 可以把寫入分散到多個 region。下面是一個帶預分區的建表語句。

hbase shell <<EOF
create 'weather',
{NAME => 'cf', VERSIONS => 1, TTL => 86400},
{SPLITS => ['a', 'g', 'm', 's']}
EOF

SPLITS 按 rowkey 首字母分了 5 個區間,寫入時只要 rowkey 首字母分佈均勻,請求就會分散到不同 region。如果數據量不大,可以不預分區,但一旦 Flume 批量寫入,單 region 的壓力會立刻暴露。TTL 設 86400 秒,也就是一天,天氣歷史數據如果沒有分析價值,沒必要長期留著。 VERSIONS => 1 表示每個 cell 只保留一個版本,避免 HBase 默認存多版本浪費空間。

建完表後,可以用 count 'weather' 或 scan 'weather', {LIMIT => 10} 驗證數據。 scan 能看到 rowkey 和列值,如果 scan 出來是空的但 Kafka 裡有數據,說明 Flume 的 serializer 沒匹配上,或者 HBase 表名、列族寫錯了。這類問題的排查方法,下一章會單獨展開。總之,Flume 到 HBase 這一段能不能通,最終看 scan 結果,不要只看 Flume 進程還在跑就以為成功。

5. 排查與避坑:Hive 映射 HBase 與 Superset 展示的常見問題

5.1 Hive 建 HBase 映射表的正確姿勢

數據落到 HBase 之後,直接查 HBase Shell 不方便做聚合分析,所以要用 Hive 把 HBase 表映射成一張外部表。Hive 的 HBaseStorageHandler 能讓 Hive 查 HBase 裡的數據,但映射關係必須完全對準。下面是一條實際可執行的建表語句。

CREATE EXTERNAL TABLE weather_hbase (
city STRING,
temperature DOUBLE,
humidity INT,
weather STRING,
wind STRING
)
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES (
"hbase.columns.mapping" = ":key, cf:temperature, cf:humidity, cf:weather, cf:wind"
)
TBLPROPERTIES (
"hbase.table.name" = "weather"
);

hbase.columns.mapping 必須從 :key 開始,表示 HBase 的 rowkey 對應 Hive 的第一個字段 city 。後面每一列都要寫完整格式 列族:列名 ,順序要和 Hive 字段順序一致。這是我見過最容易翻車的地方:HBase 表的列名是 cf:temperature ,Hive 映射裡寫成 cf:temp ,查詢結果就是一片 null。另外,Hive 的 temperature 字段最好和 HBase 裡存的字符串能互轉,如果生產者發的是 Map<String, String> , temperature 是字符串 "23.5",Hive 映射 DOUBLE 時會做轉換,但遇到空字符串就會轉失敗,返回 null。建議在爬蟲端就把空值統一處理掉,不要讓髒數據流到 HBase。

建表成功後,執行一個最簡單的查詢驗證映射是不是通的,比如 SELECT city, temperature FROM weather_hbase LIMIT 5 。如果結果有值,再往下做 Superset 接入;如果查出來全 null,先回 HBase Shell 用 scan 確認數據真實存在,再用 DESCRIBE weather_hbase 檢查字段順序。很多問題其實不是 Hive 語法錯,而是 HBase 裡的列名和映射對不上。

5.2 Superset 接入 Hive 數據源:驅動和授權是兩個坑

Superset 分析展示是最後一環,也是人們最期待的環節,但接入 Hive 數據源有兩個常見攔路虎:驅動沒裝好,用戶認證失敗。Superset 通過 PyHive 連接 HiveServer2,SQLAlchemy 連接串一般寫成 hive://superset_user:password@hive-server:10000/default 。如果 Superset 所在機器沒有安裝 PyHive 依賴,點「Test Connection」會直接報 No module named pyhive 。這不是 Superset 配置問題,而是 Python 環境缺包,需要在啟動 Superset 的虛擬環境裡手動裝 pyhive[sasl] 和 thrift 。我在部署時就在這個坑上卡了半天,一度懷疑是 Hive 端口沒開。

認證方面,Hive 如果開了 LDAP 或 Kerberos,Superset 連接串就得帶上對應認證方式。簡單環境下,直接寫 hive://user@host:10000/default?auth=NOSASL 比較省事。這裡要提醒,不要把 Hive 用戶密碼寫在 Dashboard 的公開配置裡,至少用環境變量引用。Superset 接入後,數據集列表裡能看到 weather_hbase 這張表,说明 Hive 層面已經通。後續做圖表時,如果某個維度顯示不出來,先回到 SQL Lab 跑一句 SELECT * 看原始數據長什麼樣,再判斷是 Superset 的問題還是數據本身的問題。

5.3 五個高頻踩坑:現象、原因、解決

這條鏈路從爬蟲到展示,至少有五個坑是反覆出現的。每一條我都按「現象 → 原因 → 解決」拆開寫,復現的時候直接對號入座。

坑一:Flume 寫 HBase 很慢,agent 日誌出現大量重試

現象是 Flume 日誌裡有 Blocking waiting for space in channel 或 HBase 寫入超時,數據一直往 Kafka 消費,但 HBase 表行數不漲。原因通常是 HBase 表沒有預分區,所有寫請求打到同一個 region,同時 Flume sink 的 batchSize 設置太大,單次提交的 put 太多觸發服務端限流。解決方法:按 rowkey 做預分區,把 batchSize 調到 100~200,並檢查 HBase 的 region server 日誌裡是否有 GC 停頓。如果還慢,看是不是 rowkey 前綴太集中,加鹽後重新寫。

坑二:Hive 映射表查出來全是 null

現象是 SELECT * FROM weather_hbase 返回很多行,但每一列都是 NULL。原因很直接: hbase.columns.mapping 裡的列名、順序和 HBase 表實際列不一致。解決方法:先用 scan 'weather', {LIMIT => 1} 看真實列名,再和建表語句逐字段比對。特別注意 HBase 裡的列名是 cf:temperature 不是 temperature ,Hive 映射裡漏掉 cf: 前綴也會靜默失敗。

坑三:Superset 看板日期維度亂碼或查不到

現象是時間字段顯示成「2025-01-01T00:00:00.000Z」,按小時篩選不對。原因是爬蟲端把時間存成了 ISO 字符串,Superset 默認按 UTC 解析,而 Hive 裡沒有明確轉時區。解決方法:在 Hive 表裡把時間字段轉成 TIMESTAMP ,或者 Superset 的數據集字段設置裡把格式化選成對應時區。這不是 bug,是時間類型沒統一,最好在爬蟲端就存成 yyyy-MM-dd HH:mm:ss 。

坑四:Kafka 重啟後 Flume 重複消費大量數據

現象是 Kafka 重啟後,HBase 裡出現一批重複 rowkey。原因通常是 Flume 的 kafka.consumer.group.id 沒固定,重啟後新 group 從 earliest 開始重新消費。解決方法:固定 group.id,建議設置 agent.sources.kafka-source.kafka.session.timeout.ms 大一點,讓 Flume 在 Kafka 重啟期間不被踢出 group。如果想要丟數據而不是重複數據,可以把 auto.offset.reset 改成 latest ,但這要看業務接受哪種不一致。

坑五:爬蟲接口偶爾返回 403 或空數據

現象是 Kafka 裡某一時段數據明顯變少,HBase 的 count 也對應下降。原因是被採集端對高頻請求觸發了風控,或者接口超時後拋異常導致整個城市循環中斷。解決方法:在爬蟲端加 User-Agent 輪換、請求間隔控制,同時把單個城市的抓取異常 catch 住,不能讓一個城市失敗影響後面城市。超時時間也要分開設置,SocketTimeout 和 ConnectTimeout 都設成 5 秒左右最穩。這個坑看起來不起眼,但會直接造成鏈路「看似正常,數據缺一段」的假象。

6. 全鏈路驗證:用一分鐘冒煙測試檢查採集到展示是否真通

6.1 冒煙測試腳本

每次改完配置,我都會強制自己走一遍冒煙測試,而不是直接看 Superset 圖表。先確認 Kafka topic 裡有數據,再確認 HBase 能查到數據,最後在 Superset SQL Lab 執行查詢。三步都過,才說明鏈路通了。

# 第一步:從 Kafka 消費最近 3 條數據
kafka-console-consumer.sh –bootstrap-server node01:9092 \\
–topic weather-topic –max-messages 3

# 第二步:查 HBase 最新寫入的行
hbase shell -e "scan 'weather', {LIMIT => 3, REVERSED => true}"

# 第三步:Hive 裡驗證映射表
hive -e "SELECT city, temperature, humidity FROM weather_hbase LIMIT 3;"

6.2 看板製作技巧與日常檢查

Superset 裡新建圖表時,我習慣先用「表」這種最樸素的圖形驗證數據,確認行數、字段、時間範圍都對,再做柱狀圖或折線圖。天氣數據最適合做的圖是「各城市實時溫度柱狀圖」和「單個城市 24 小時溫度趨勢線」。SQL Lab 裡可以直接寫 SELECT city, MAX(temperature) FROM weather_hbase GROUP BY city 這類語句,Superset 支持結果可視化,調試效率很高。日常檢查則直接看 Kafka consumer group 的 lag,如果 lag 長時間居高不下,多半是 Flume 或 HBase 寫入出問題,而不是爬蟲停了。

6.3 一套可複用的檢查習慣

這套項目我前後跑過好幾遍,最深刻的教訓是:不要在 Flume 日誌還沒看明白的時候就去調 Superset。有一次我以為 Hive 映射表建錯了,反覆改 STORED BY ,最後發現是 Flume 的 serializer 把 JSON 當成了整個字符串存到一列裡,Hive 映射的字段根本對不上。從那以後,我每次接到類似鏈路,都會先按「Kafka 有數 → HBase 有數 → Hive 有數 → Superset 有數」的順序強制走一遍冒煙測試,哪一步斷了就在哪一步查日誌,不跨層猜。希望幫到你。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

赞(0)
未经允许不得转载:171主机测评 » 天气爬虫到Kafka Flume HBase大数据实时处理链路实战
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址