先講一個你已經知道的 Hub-Spoke
你可能沒聽過「Hub-Spoke 拓撲」這個詞,但你每天都在使用它。
打開任何一張航空公司的航線圖。你會看到幾個巨大的節點——桃園、成田、新加坡樟宜——從這些節點輻射出密密麻麻的航線,連接到幾十個較小的城市。大節點是 Hub(樞紐),小城市是 Spoke(輻條)。
點對點(上)vs Hub-Spoke(下):透過中心節點轉發,大幅減少連線數量。圖片來源:Wikipedia(公有領域)
為什麼航空公司要這樣設計?因為如果每個城市都直飛其他城市,30 個城市需要 435 條航線。但如果所有城市都先飛到 Hub,再從 Hub 轉飛,只需要 30 條航線。Hub 是協調者,集中處理調度、轉機、和資源分配。
這個模式在資訊系統中也很常見:一個中心節點協調多個邊緣節點。資料在 Hub 集中,Spoke 負責第一線操作。
但傳統 Hub-Spoke 有一個致命假設:Hub 永遠在線。
航班可以等 Hub 機場開放。包裹可以等分揀中心處理。但在災難現場,如果 Hub 掛了,病人不能等。
xGrid 的 Hub-Spoke 做了兩個關鍵的觀念修改:每個 Spoke 都是完整的系統,不只是終端。 而且——任何一個 Spoke 都能在現場接手成為新的 Hub。
斷線不是故障,是預期狀態
傳統的系統把網路斷線當成「故障」來處理——偵測到斷線、觸發告警、等待恢復。
xGrid 把斷線當成「正常」來設計。每台裝置都是完整的系統——有自己的資源系統、自己的資料庫。斷線只是暫時失去了同步能力,不是失去了運作能力。
這就是 xGrid 版 Hub-Spoke 跟航空版最大的差異:Spoke 不是等待 Hub 指令的終端,而是能獨立運作的完整系統。Hub 提供的是協調,不是能力。
每個節點都是完整系統
這是整個設計最關鍵的觀念:每台裝置出廠時都是一個完整的醫療站。
角色不是由硬體決定的。同一台機器,可以是 Hub,也可以是 Spoke——差別在它扮演的角色,不在它是什麼零件。這意味著你不需要準備「Hub 專用機」和「Spoke 專用機」。倉庫裡放的不是「兩種零件」,而是「一堆相同的備品」。任何一台壞了,從箱子裡拿一台新的,接上去,繼續。
最小的部署只需要一台機器,不需要任何網路基礎設施——一個電源、一台平板,就是一個完整的醫療站。需要擴編?帶另一台過來接上,它就成為新的 Spoke。一台機器能撐起前進醫療站;一組機器能撐起一間醫學中心。同一套設計,依規模伸縮。
兩層獨立的網路 — 一層斷,另一層撐
xGrid 的部署是兩層各自獨立的網路疊在一起:一層負責操作(每台機器各自提供無線覆蓋,平板連上最近的那台就能工作),一層負責站與站之間的同步。
關鍵在於這兩層完全獨立。負責同步的那層斷了?每個站的平板繼續操作,只是站與站之間暫時失去同步。某台機器的無線覆蓋故障?同步照跑,那個區域的平板改連鄰近的覆蓋就好。
一層斷了,另一層撐著。這就是「斷線是預期狀態」落實到網路設計上的樣子。
任何一個 Spoke 都能接手
這是整個設計最強大的能力,而它有兩種樣貌。
主動帶走。 大規模傷亡事件中,指揮中心通知十公里外出現第二個傷亡集中點,需要立刻開設第二個醫療站。你走到其中一台 Spoke,把它和電池、平板一起裝進背包,到了新地點接上電源——它就成為一個完整的、獨立運作的新醫療站,帶著原本 Hub 不久前的所有病患資料。不需要事先規劃,不需要特殊機器。
被動接手。 Hub 的硬體故障了——電源燒了、被掉落的天花板砸中。每台 Spoke 都在持續監測 Hub 是否還在。確認 Hub 真的離線後,操作員指定其中一台 Spoke 接手。因為每台 Spoke 手上都持有一份近乎即時的備份,接手後病患資料的損失有一個明確的上限;在傷患湧入的高峰,操作員還能手動把這個上限壓得更低。
升級接手是一個整體成功或整體失敗的動作——要嘛完整接手,要嘛回到原狀,不會出現「升級到一半卡住」的半成品。
為什麼是人的決定,不是機器自動? 因為在斷網環境下,你無法確定 Hub 是真的壞了,還是只是網線鬆了。如果兩台 Spoke 同時自動接手,你會得到兩個 Hub 各自收治病患——這叫 split-brain,事後合併資料會是一場災難。所以接手必須是人的刻意決定。
殭屍 Hub 與腦裂防護 — 靠機制,不靠自律
「不要同時升級兩台」是一條規則。但規則在災難現場會被打破——萬一有人在混亂中多按了一次呢?
所以光靠自律不夠。xGrid 的設計讓過期的 Hub 自己讓位:當一台壞掉又被接回電源的舊 Hub 重新開機,它會發現現場已經有一個「更新一輪」的 Hub 在運作——它不會試圖搶回主權,而是自動退位成 Spoke。不需要人去關它。
同樣地,如果一台 Spoke 在重連時同時看到兩個彼此矛盾的「主站」,它不會自己亂挑一個,而是停下來請人確認。每個部署也彼此隔離,你的 Spoke 不會意外連上隔壁部署的 Hub。
這個機制不能百分之百預防腦裂——如果兩個完全斷網的子群各自接手了一個 Hub,你確實會得到兩個獨立的 Hub。但它保證:只要兩個子群重新接上網路的那一刻,較舊的那個會自動讓位。 問題從來不是「如何永遠防止腦裂」,而是「如何在腦裂發生後最快速地自動修正」。
衝突解決:取決於資料的性質
兩台裝置在斷線期間,各自修改了同一筆資料,接回來時怎麼辦?
答案取決於資料是什麼。可以累加的就累加——主站消耗 5 個紗布、衛星站消耗 3 個,正確答案是 8 個被消耗,而不是「以較新的為準」(那會遺失其中一邊)。不可修改的紀錄(生命徵象、交班)兩邊都保留。
最重要的是那些錯誤代價太高、不允許自動解決的資料:血袋、管制藥品。一袋血被兩個站同時標記為「已發放」,這不是可以用時間戳解決的問題。系統會把它標成衝突,等負責人員親自確認。
把「人工判斷」當成某些情況的正確答案,而不是需要消除的缺陷——這是為高風險環境設計時的關鍵分野。
設計哲學:為斷線而生
大多數系統的設計前提是「網路是可靠的」,然後為不可靠的情況做例外處理。
xGrid 的設計前提是「網路是不可靠的」,然後為可靠的情況做優化。
這個顛倒導致了完全不同的設計決策:
- 每個節點都是完整系統(不是只能顯示畫面的終端機)
- 角色由扮演決定,不由硬體決定(不需要「特殊的 Hub 機器」)
- 同步是定期的批次操作(不是即時的持續連線)
- 衝突解決是預設行為(不是例外處理)
- 人工判斷是某些情況的正確答案(不是需要消除的缺陷)
- 接手是人的刻意決定(因為 split-brain 比等待更危險)
- 但過期的 Hub 自動讓位(因為這是事實,不是自律)
網線被踢掉不是故障。Switch 被砸壞不是末日。Hub 燒掉不是終結。
它們只是拓撲重組的觸發點。
