去年幫一棟六層舊公寓的管委會做一個小東西:把氣象署的地震速報接到地下室的蜂鳴器跟LINE群。環境是 Raspberry Pi 3、Raspbian 11、Python 3.9,外網走社區的第四台網路,偶爾會瞬斷。資料量不大,一天幾十則,但每一則都要求十秒內送到。
做法很土。先用 WebSocket 訂閱速報頻道,收到 JSON 後只取需要的欄位,寫進一個 SQLite 佇列,再由另一支程式讀取、觸發警報。重點在斷線處理,我沒有用現成的重連套件,自己寫了迴圈:
import json, time, websocket
def run(url, on_event):
backoff = 1
while True:
try:
ws = websocket.create_connection(url, timeout=15)
backoff = 1
while True:
raw = ws.recv()
if raw:
on_event(json.loads(raw))
except Exception as e:
log.warning("ws down: %s", e)
time.sleep(backoff)
backoff = min(backoff * 2, 60)
寫入佇列前先算一個事件雜湊(時間、規模、位置),重複就丟掉。這樣斷線重連時,對方補送的舊資料不會讓蜂鳴器半夜亂叫。
為什麼要自己管重連?因為現成套件預設會無限快速重試,遇到社區網路那種「連得上但沒資料」的半死狀態,它不會報錯,只會安靜地掛著。我加了一個 watchdog:超過 90 秒沒收到任何訊息就主動關掉 socket 重連。代價是每天會多幾次無謂的握手,但換來的是不會漏掉真正的速報。
會失效的地方很明確。第一,Pi 的系統時間如果沒校時,事件雜湊會全部對不上,重送就擋不住;我後來加了開機強制 ntpd 同步。第二,地下室收不到手機訊號,LINE 通知只能靠有線網路,網路一斷,本地蜂鳴器還是會響,但群組訊息就補不回來。第三,這種做法只適合「通知」,不適合「判斷」——我曾經拿它去比對兩家速報的規模,結果兩邊演算法不同,同一個地震差到 0.4,後來就放棄了。
還有一個坑:WebSocket 連線閒置太久會被中間的 NAT 踢掉,我原本以為設 timeout 就夠,結果是連線層活著、應用層早就沒資料。現在改成每 30 秒送一次 ping,沒 pong 就重連。
你們那邊如果也是用長連線接速報,斷線後的補送是怎麼去重的?我目前只用時間加座標的雜湊,遇到同一地震更新規模就擋不住,還在想更穩的鍵。
0 条评论
还没有评论,写下第一条。