為什麼需要模型?先搞懂物理模型
模型不是拿來完美描述現實,而是拿來讓你我對「分散式系統長什麼樣子」講同一種語言
真實世界很殘酷,設計者要扛四種麻煩
上一章讓你看到分散式系統五花八門——搜尋引擎、線上遊戲、金融交易。這一章要處理一個更根本的問題:一個要在真實環境長期運作的系統,得在各式各樣的狀況與威脅下都還能正確工作。課本把設計者要面對的麻煩分成四大類:
使用方式千變萬化——有網頁一天被點好幾百萬次,行動裝置時連時斷,多媒體應用又要超高頻寬、超低延遲;環境差異極大——硬體、作業系統、網路全都不一樣(異質性 heterogeneity),規模可能從十幾台到上百萬台電腦;內部問題——時鐘不同步、資料更新互相衝突、各種軟硬體失效;外部威脅——對資料完整性與機密性的攻擊、阻斷服務攻擊。
光看這四類麻煩就頭暈了,更別說同時處理。所以課本的解法不是列一張更長的檢查清單,而是換一種思考工具——用描述性模型(descriptive model)把複雜度馴服下來。
直接讀原文,旁邊就是白話
這是本章開場,作者對「三種模型為什麼存在」的正式交代。原文放左邊,白話導讀放右邊——先讀懂原文的氣口,再看整段在說什麼。
這一章要介紹三種重要、而且互補的方式,用來描述與討論分散式系統的設計。物理模型只看「有哪些電腦與裝置、怎麼連起來」,故意不管具體是哪家廠牌的技術;架構模型看軟體元件各自負責什麼運算與通訊任務,怎麼分工;基礎模型則抽象地去描述大多數分散式系統都會遇到的共同難題(像時間、失效、安全)該怎麼解。三種模型不是三選一,而是像三張不同用途的藍圖,疊在一起才看得到系統的全貌。
地圖刻意省略樹木與行人,只留下道路與地標,反而讓你更容易找路。模型也一樣——刻意省略細節,讓我們更容易推理系統的行為,而不是被雜訊淹沒。
蓋一棟大樓,需要好幾張圖
想像你要描述一棟大樓,一張圖是不夠的:結構圖畫出有幾根柱子、幾片牆、用什麼建材;平面配置圖畫出客廳、廚房、臥室各自負責什麼、彼此怎麼連通;消防規範抽象地規定「火災時怎麼逃生」「結構要承受多大地震」。三張圖描述同一棟樓的不同面向,缺一不可——這正是三種模型互補(complementary)的意思。
最具體、看硬體:有哪些電腦、裝置與網路互連?故意不管是哪家廠牌的技術——對應大樓的結構圖。
看軟體分工:哪些元件在做運算與通訊?彼此怎麼分工?對應大樓的平面配置圖。
最抽象、看本質難題:互動、失效、安全各有什麼特性與保證?對應大樓的消防與結構規範。
基礎模型底下又分三個子模型:互動模型(效能與時間)、失效模型(故障分類)、安全模型(威脅與防禦)。後面章節會一個一個展開。
招牌互動:物理模型的三個世代,規模怎麼爆炸
先把最精簡的基準物理模型(baseline physical model)釘死:就是一組可擴充的電腦節點,由網路互連來傳遞訊息。從這條基準線出發,課本點出三個世代——規模與異質性一路躍進。
因區域網路(多半是 Ethernet)而興起,通常只有 10-100 個節點,服務很少(共享印表機、檔案、email),系統大致同質(homogeneous),開放性還不是重點——就像一個十幾戶人家、彼此都認識、房子蓋得差不多的小村莊。
隨網際網路爆發成長(例如 Google 1996 年上線),節點數量暴增、跨組織、高度異質——網路、電腦架構、作業系統、語言、開發團隊通通不一樣,因此開始重視開放標準與middleware(中介軟體),就像大城市人口暴增、來自四面八方,需要交通號誌與通用規範來維持秩序。
行動運算、普及運算(ubiquitous computing)、雲端/叢集運算興起,節點可達數十萬台,異質性再升級——就像超級都會圈,有人帶著手機到處移動,連洗衣機都連網,還有一整片合作供電的電廠。
從小村莊到超級都會圈,規模與居民背景差異越來越大,越需要通用規範來協調不同背景的居民——這正是為什麼「開放性」與「服務品質」隨著世代越來越被重視。
當代物理模型:三股趨勢,各打破一個舊假設
早期系統裡的節點通常是桌機,相對固定不動、獨立自主。當代三股趨勢,一個接一個打破了這個假設:
打破「節點固定不動」——筆電、手機到處移動,因此需要 service discovery(服務發現)與自發互通的能力。
打破「節點是獨立的電腦」——把運算嵌入日常物件與環境,例如智慧家庭裡的洗衣機也在運算。
打破「每個節點各自獨立負責一個角色」——一群節點合力提供同一服務,例如 Google 搜尋背後是一整個叢集。
當系統大到極致,甚至會變成系統的系統(system of systems)——由多個本身就是完整系統的子系統合作組成。課本舉的例子是洪水預測系統:感測器網路監測河川,叢集電腦跑模擬,另一套系統再透過手機發早期警報,三者各自獨立卻合作完成任務。
網際網路本身就是「網路的網路」;系統的系統是同一個道理套用到更高一層——每個子系統自己就能獨立運作,合作時又拼出一個更大的能力。
小試身手
把「三種模型分工」和「物理模型三世代」放進腦子裡,來兩題檢查一下:
搞懂了物理世界長什麼樣子,接下來要問:誰在通訊、它們又是怎麼通訊的?往下捲。
誰在通訊、怎麼通訊:架構模型的第一道問題
先搞懂「誰在通訊」,再搞懂「怎麼通訊」——這是讀懂任何分散式架構圖的兩把鑰匙
看懂一張架構圖,其實只要問四個問題
面對一個分散式系統,想拆解它的骨架,課本說先問四個問題:什麼在通訊(實體)?怎麼通訊(範式)?它們扮演什麼角色與責任?又怎麼放置到實體基礎設施上?這一站,我們先啃第一個——也是最根本的一個:什麼在通訊。
答案可以從兩種角度看。系統觀點很老實:通訊的實體通常就是process(行程)——所以分散式系統常被想成「一堆行程+行程間通訊」。問題觀點則更貼近你寫程式時的感覺,提出了 object、component、web service 三種更好用的抽象。
在感測器網路這類原始環境裡,作業系統可能根本不支援 process 抽象,這時通訊實體只能退回到node(節點)這一層。另外,多數環境裡 process 還會搭配thread(執行緒),嚴格說來,真正收送訊息的端點常常是 thread,不是 process 本身。
直接讀原文:通訊實體的三個問題觀點抽象
這段是課本從「系統觀點」過渡到「問題觀點」的關鍵段落,把 object、component、web service 三個抽象一次性交代清楚。原文放左邊,白話放右邊。
從系統觀點看,答案很單純:分散式系統裡真正在通訊的實體,通常就是 process。
object 是透過 interface(介面)存取的,還有一份專門描述「這個物件能做什麼方法」的 IDL(介面定義語言)規格。
component 跟 object 最關鍵的差別是:它不只講清楚自己「提供」什麼介面,還把自己「需要」哪些其他 component/介面才能正常運作,也一併寫進合約。
web service 本質上就是活在全球資訊網裡的東西,用 Web 的標準來描述、也讓別人能發現這個服務。
把這三種抽象想成你要合作的對象:object 像一位專業師傅,你透過一張「服務清單」(interface)知道他會做哪些事;component 像一位更負責任的外包廠商,他不只列出「我提供什麼」,還老實講「我需要你先準備好哪些東西」——等於給你一份完整合約,沒有藏起來的隱性需求;web service 則像一家掛在網路黃頁上的公司,用網址當地址,常常跨公司邊界做生意。
招牌互動一:四個通訊實體,各是誰的抽象?
process、object、component、web service ——把它們拖到最貼切的描述上,配完按「對答案」。
process/thread 靠作業系統支撐,是任何分散式系統的底層;object 靠 interface(IDL)存取,適合組織內物件導向系統;component 在 interface 之外明示相依性,適合第三方放心組裝;web service 靠 URI+XML/Web 標準,天生適合跨組織的 B2B 整合。
搞懂「誰在通訊」之後,換問「怎麼通訊」
知道了通訊實體,課本接著把「怎麼通訊」分成三大類:行程間通訊(IPC)、遠端呼叫(remote invocation)、間接通訊(indirect communication)。原文先講遠端呼叫和間接通訊的根本差異:
遠端呼叫這一類,通訊是寄件人跟收件人之間明確的雙向關係——寄件人清楚知道要把訊息/呼叫送給誰。
間接通訊則相反:寄件人不必知道收件者是誰(空間解耦),寄件人和收件人也不必同時存在(時間解耦)。
以 RPC 來說,遠端電腦上 process 裡的程序,可以被當成本地位址空間裡的程序直接呼叫——分散的細節全被藏起來了。
下面這張圖,把三大通訊範式排在一起,點一下每個方塊,看看它的解耦程度與代表技術。
招牌互動二:看一次 RPC 呼叫,怎麼「隱藏」分散的細節
RPC(遠端程序呼叫)最了不起的地方,就是讓你感覺不到「這是遠端」。按「下一步」,看一次呼叫怎麼在 client 和 server 之間跑一趟。
你直接撥給某個人(明確指定收件者),雙方必須同時在線,你也知道對方是誰。RPC、RMI(遠端方法呼叫)更厲害的地方,是它讓這通越洋電話聽起來就像對方在隔壁房間。
間接通訊像公佈欄或郵局信箱
如果遠端呼叫是打電話,那間接通訊就是留言板:寄信的人不必知道誰會來看(空間解耦),寄信的人跟讀信的人也不必同時存在(時間解耦)。課本列出五種間接通訊技術,各自解決不同的場景。
一對多——訊息送給一個「群組」,用群組識別碼代表,加入群組才會收到訊息,寄件人不需要知道每個收件者是誰。
大量發布者送出事件,大量訂閱者依興趣收到——就像報紙訂閱:報社印新聞,訂閱者依興趣收報,中介負責把對的內容送給想要的人。
點對點——生產者把訊息投入佇列,消費者從佇列取出,或被通知有新訊息到達,提供生產者與消費者之間的一層間接。
process 把結構化資料(tuple)放進一個持久的元組空間,其他 process 依樣式讀取或移除,讀寫雙方不必同時在線。
讓不共用實體記憶體的 process,像讀寫本地變數一樣共享資料——把「共享記憶體」這個熟悉的寫法,包裝成一個抽象。
要把同一則訊息送給很多人 → group communication 或 publish-subscribe。一對一、可靠投遞、可暫存 → message queue。讀寫雙方不必同時在線 → tuple space。想用「讀寫共享變數」的熟悉寫法 → DSM。
小試身手
「誰在通訊」跟「怎麼通訊」,這一站的兩把鑰匙都拿到了。來兩題檢查一下:
知道了「誰在講話」,下一步是看這些角色怎麼分工、服務放在哪裡。往下捲。
角色與放置:Client-Server、P2P 與架構模式
同一份服務,擺在哪裡、由誰主導,決定了整個系統會不會卡住、會不會難以擴展
先問一句:誰主導、誰配合?
行程(或物件、元件、服務)互動時,會擔任不同的角色——而這些角色,就決定了整個系統的架構長什麼樣子。這一節,課本挑出兩種最重要的風格互相對照:client-server(主從)與peer-to-peer(對等)。
Client-server 是提到分散式系統時最常被舉的例子,歷史最悠久,至今也最廣泛被採用:client 行程向個別的 server 行程發出請求,去存取 server 管理的共享資源。而 server 自己也可能是別的 server 的 client——例如 web server 常是本地檔案 server 的 client,用來管理網頁檔案;web server 跟大部分網路服務也都是 DNS 服務的 client。
更有趣的是搜尋引擎:它一方面回應瀏覽器 client 的查詢(此時它是 server),一方面又派出 web crawler 去對別的 web server 發 HTTP 請求(此時它是 client)。這兩件事彼此獨立、不太需要同步,可以並行執行——同一個程式,同時活成兩種角色。
Client-server 就像上餐廳:客人(client)點餐,廚房(server)做菜,角色分明、流程簡單明瞭。但所有客人都靠同一間廚房——客人一多,廚房就開始塞車。這正是 client-server 最大的隱憂:服務集中在單一位置,一旦超過那台機器與網路頻寬的容量,就卡住了。
直接讀原文,旁邊就是白話
這幾句是課本介紹 client-server 與 peer-to-peer 差異的原話。左邊原文、右邊白話導讀——先讀懂原文的氣口,再看整段在說什麼。
Client-server 的定義很直白:client 行程去找個別的 server 行程要東西,存取 server 管理的共享資源——雙方角色分得清清楚楚,可能還分別跑在不同的主機上。
Peer-to-peer 剛好相反:參與同一件任務的所有行程都扮演差不多的角色,彼此合作、地位對等,完全沒有 client 跟 server 的區分。
Client-server 的優點是直接又相對簡單,但擴展性很差——把服務集中放在單一位址,一旦超出那台機器和網路頻寬的承受量,就沒辦法再長大了。
P2P 的關鍵洞見,是把「使用者自己擁有」的運算與網路資源,也一起拿來支援服務——這樣一來,能用的資源量就會隨著使用者人數一起變多。
Client-server 把資源鎖進一個地址,換來簡單,卻也換來瓶頸;P2P 把資源攤開在每個使用者身上,換來成長性,卻也換來協調的複雜度。沒有誰絕對更好,只有「你在賭哪一邊」。
招牌互動:同一件事,兩種跑法
下面先示範 client-server 怎麼跑,再示範 P2P 怎麼跑。按「下一步」,感受一下「集中處理」跟「大家互相直接交換」有多不一樣。
每位賓客都帶一道菜來,同時也吃別人帶的菜——人人既是「供應者」也是「享用者」。來的人越多,桌上的菜反而越豐盛,這正是 P2P 最迷人的特性:資源隨參與者增加而成長。但百樂餐也比較亂:誰帶了什麼、東西放哪、要不要多帶一份備用(複製副本),協調起來遠比上餐廳複雜。
把兩種架構定格成一張對照表
動畫跑太快?這是靜態總結——兩種角色風格最核心的取捨。
角色明確主從,直接又簡單。案例:Web、DNS、FTP、電子郵件。缺點:擴展性差——服務集中在單一位址,超過那台機器與網路頻寬的容量就卡住。
角色全部對等,資源隨使用者人數一起成長。案例:Napster、BitTorrent。缺點:複雜度高——物件被分散到大量電腦上,還要複製多份副本以分攤負載、應付斷線。
為什麼 P2P 會「複雜許多」?因為在 P2P 裡,大量資料物件被分散,每台電腦只存一小部分;每個物件還要複製(replicate)到好幾台,以分攤負載並在某台斷線時仍可用。要「放置物件、找回物件、維護副本」,自然比 client-server 複雜許多。
別把 client-server 想成一條單向的路。Web server 是檔案 server 的 client;搜尋引擎既是 server(回應瀏覽器查詢),又是 client(它的 web crawler 去抓別的網站)。角色是相對的,看你站在哪一段互動裡。
P2P 的擴展瓶頸怎麼解?先談「放置」這件事
同樣的物件或服務,放在哪台機器、哪個行程,會大幅影響效能、可靠度與安全。放置沒有萬用公式,課本聚焦四種策略:
用多個 server 行程合作提供服務,可分割(partition)物件分散管理(如 Web),或維持複製副本(如 Sun NIS 讓每台伺服器持有同一份密碼檔)。好處:分攤負載、提升可用性。
快取是離某個 client(或一群 client)較近的資料存放區。要用先查快取、有最新副本就直接給;沒有才向原始 server 取得。好處:降低延遲與對外網路的負載。
行動程式碼(mobile code)如 applet,被下載到瀏覽器本地執行,互動反應快,不必每次都隔著網路等。好處:本地執行、可支援 server 主動 push。
行動代理(mobile agent)帶著程式與資料親自跑到各個站點,在當地做許多次本地查詢,再帶結果回來。好處:把多次昂貴的遠端呼叫換成當地的本地呼叫。
行動程式碼像「外送食譜到你家」:你點連結,程式碼被下載到瀏覽器在本地執行;行動代理則像「派一位採購員出差」——帶著程式與資料親自跑到各站點比價、查詢,再帶結果回來。但這兩者都繞不開同一個共同主題:安全。行動程式碼可能危害本機資源,所以瀏覽器會限制 applet 對本地資源的存取;行動代理對它造訪的電腦也是威脅,接收方應依代理代表的使用者身分決定可用哪些本地資源,而且代理本身也可能因被拒存取而無法完成任務,因此適用範圍有限。
常喝的飲料先放小冰箱(離你近的快取),要喝時先看小冰箱有沒有、且還新鮮,有就直接拿;沒有才去大賣場(原始 server)補貨。Web 瀏覽器會用特別的 HTTP 請求,跟原始伺服器確認快取的頁面是不是還是最新的,再決定要不要直接顯示。
一般 Web 互動都由 client 發起(你點、你問,server 才答);若要 server 主動推播(例如股價更新),就需要先下載特別的程式碼來接收更新——這正是行動程式碼的另一種用法。
再拆一層:Layering 是垂直的,Tiering 是水平的
放置談的是「這個物件放哪台機器」,但整個系統還有更大的組織方式:分層(layering)把複雜系統切成數層——上層使用下層提供的服務,且上層不需知道下層怎麼實作。最底層是平台(platform)(硬體+作業系統),上面疊一層中介軟體(middleware),用來遮蔽異質性、提供方便的程式設計模型。
分層式架構(tiering)則與 layering 互補:layering 管垂直抽象,tiering 則是把某一層的功能切開、放到適當的伺服器與實體節點上。把應用拆成「呈現(presentation)」「應用(application)」「資料(data)」三塊邏輯後,就能決定要用二層還是三層。
Layering 像蛋糕一層疊一層地往上疊,上層吃下層的服務;tiering 則像把某一層的內餡拆開,分別放進不同的廚房(伺服器)去做。兩者談的是完全不同的維度,並不是互相取代的關係。
局部更新、瘦客戶端、以及 middleware 的極限
傳統 Web 要更新一小塊也得整頁重新請求,使用者只能乾等。AJAX像派一個小跑腿:瀏覽器非同步向 server 要一小塊資料,回來只更新該部分,其餘畫面照常可操作。Google Maps 拖地圖時即時補上新圖磚,就是經典例子。
瘦客戶端(thin client)把複雜度搬到網路端的服務,本地裝置只負責視窗式介面——好處是手機等簡單裝置也能用到強大服務;壞處是像 CAD、影像處理這類高互動圖形作業,因為要傳大量影像資料,延遲會令人難忍。VNC 把這個想法推到極致——協定只做一件原始的事:把一塊像素矩形畫到螢幕指定位置,因此能跨任何作業系統運作。
有些與通訊相關的功能,只有站在通訊端點的應用程式自己才能完整可靠地實作——就像貴重包裹,就算快遞(通訊系統)有基本保障,真正關鍵的檢查只有寄收件人(端點應用)自己能完整做到。例如傳超大郵件,TCP 無法從重大網路中斷中恢復,於是郵件服務必須在應用層自己加一層容錯:記錄進度、用新連線續傳。這對 middleware 設計者是個兩難:不是所有通訊功能都能被抽象到 middleware 而與應用無關。
小試身手
把角色、放置、layering/tiering 串起來,你已經摸到「系統長什麼樣子」的骨架了。來兩題:
角色與放置談的是「空間」怎麼分工——誰在哪、資源放哪台機器。接下來要談的是「時間」這件事有多不可靠:往下捲。
沒有全域時鐘:互動模型與同步/非同步
沒有一台電腦的時鐘會跟別人一樣快——這個看似小事的物理限制,決定了整個系統怎麼想「時間」
先講最實際的:訊息在路上要花多久、能塞多少、會不會忽快忽慢
分散式系統裡的行程(process)靠訊息互動,彼此的行為由分散式演算法描述——每個行程有自己完全私有的狀態,別的行程無法直接偷看。而互動模型要面對的第一個限制因素,是通訊效能常常是瓶頸,課本用三個量描述它:
latency(延遲)是訊息開始送出、到對方開始收到之間的時間;bandwidth(頻寬)是單位時間內網路能傳的總資訊量;jitter(抖動)則是一連串訊息送達時間的變異。
latency 是從你家出發到抵達目的地要花的時間——即使路很寬,只要距離遠或塞在交流道,也得等。bandwidth 是這條路有幾線道,單位時間能讓多少車通過;車太多就得共用車道、互相排擠。jitter 則是一車隊抵達時間的忽快忽慢——對播音樂這種需要等間隔送達的場景,抖動會讓聲音斷斷續續、嚴重失真。
直接讀原文,旁邊就是白話
這幾句話幾乎是本節最核心的定義區塊。原文放左邊,白話導讀放右邊——先讀懂原文的氣口,再看整段在說什麼。
訊息從一個行程開始送出、到另一個行程開始收到之間的延遲,就叫 latency。
電腦網路的 bandwidth,是單位時間內這條網路能傳輸的總資訊量。
jitter 是一連串訊息送達時間的變異——忽快忽慢的程度。
電腦的時鐘會偏離完美時間,更麻煩的是,每台電腦的偏離速率還都不一樣。
就算一開始把所有電腦的時鐘都對到同一個時間,只要不持續校正,過一段時間它們還是會明顯地各說各話。
這是時鐘漂移率(clock drift rate)的最佳畫面:每支錶走快走慢,且快慢程度彼此不同,就算一開始對好時間,過陣子也會差很多。這就是為什麼分散式系統維持不了單一的全域時間。
招牌互動:看三台電腦的時鐘各走各的
下面三台電腦一開始把時鐘校到完全一致。按「下一步」,看它們的時鐘怎麼各自漂移、又只能靠訊息互相估計時間——而不是真的「對到」。
用 GPS 接收器校時精度約 1 微秒,聽起來很準——但 GPS 訊號室內收不到,成本也不可能每台電腦都裝。改用「有準確時源的電腦發送計時訊息給別人」,結果又受「變動的訊息延遲」拖累,無法完美。所以結論很直白:全域時間終究不可能完美,這正是下一段兩種模型的起點。
既然對不準,那就老實面對:兩種對時間的態度
由於很難對行程執行、訊息傳遞、時鐘漂移設限,課本索性給出兩個極端模型,一個對時間有強假設,一個完全不假設。
三項都有已知界限:每個步驟的執行時間有已知上下界;每則訊息在已知有界時間內送達;每個行程的時鐘漂移率有已知界限。好處是可以用timeout(逾時)偵測行程失效;難處是很難給出保證得了的真實界限——界限若不可靠,設計就不可靠。
對執行速度、訊息延遲、時鐘漂移都不設任何界限。一步可能花一皮秒,也可能花一世紀;訊息可能瞬間到,也可能好幾年才到。這恰好描述了網際網路。
兩支軍隊 Apple 與 Orange 紮營在兩座山頭,靠信差穿越山谷約定何時一起衝鋒。非同步 Pepperland:信差速度變化極大,送「衝鋒!」可能 5 分鐘到,也可能 3 小時才到,沒人能保證時間,很難精準約好同時行動。同步 Pepperland:大家知道每則訊息至少 min、至多 max 分鐘會到——帶頭的送出「衝鋒!」後等 min 分鐘再衝,另一隊收到後等 1 分鐘再衝,就能保證在帶頭者之後、且不超過 (max − min + 1) 分鐘內跟上。
凡對非同步系統有效的解法,對同步系統也有效——因為同步只是在非同步的基礎上多加了時間假設,假設更寬鬆的解法自然也適用在假設更多的情境。
動手配配看:這句話屬於同步系統,還是非同步系統?
把下面幾個特徵,拖到它該屬於的類別。配完按「對答案」。
在非同步系統中,timeout 只能說明「這個行程目前沒有回應」——它可能當機了,也可能只是很慢,或者訊息還在路上晃。你分不清是壞了還是變慢,這正是後面失效模型要處理的麻煩。
既然時鐘靠不住,那怎麼排出事情發生的先後?
來看一個 email 例子:X 寄出主旨「Meeting」的信,Y、Z 各自回覆「Re: Meeting」。由於投遞延遲各自變動,某些使用者可能看到順序顛倒——先看到回覆,才看到原信。
如果時鐘能完美同步,每則訊息都可以帶上送出時間戳 t1 < t2 < t3 來排序。但時鐘無法完美同步,於是 Lamport(1978)提出logical time(邏輯時間):不靠時鐘,靠因果關係來排序。
訊息一定是先被送出,才會被對方收到——這件事跟時鐘準不準完全無關,是邏輯上必然的順序。
回覆一定是在收到原訊息之後才送出的。以 email 例子來說,Y 一定是先收到 X 的 m1,才送出回覆 m2。
邏輯時間把這些順序關係轉成編號——越晚發生的事件編號越大,就能在沒有準時鐘的情況下,推斷出合理的事件順序。
邏輯時間的精神:就算每個人的手錶都不準,我們仍然知道「先寄信才有人收信、收信後才會回覆」——靠這些因果關係,一樣能排出讓人信服的先後順序。
小試身手
時間不可靠、時鐘各走各的——這是本節最核心的物理限制。來兩題檢查一下:
時間不可靠之後,最後要面對的是:東西會壞、也會有人蓄意搞破壞。往下捲。
會壞在哪裡、會被誰盯上:失效模型與安全模型
東西會壞、人會使壞——先把「壞的方式」分類清楚,才知道怎麼防
系統會壞,但「怎麼壞」有分類
在分散式系統裡,行程(process)與通訊通道都可能失效(failure)——偏離了正確或期望的行為。光說「壞了」沒有用,我們需要一套失效模型(failure model),把「壞的方式」講清楚,這樣才能分析它的影響,進而設計出扛得住這種壞法的系統。
課本引用 Hadzilacos 與 Toueg 的分類法,把行程與通訊通道的失效分成三大類:遺漏失效(omission failures)、任意失效(arbitrary / Byzantine failures)、時序失效(timing failures)。這套分類會貫穿全書——後面章節談可靠通訊協定、談交易的兩階段提交,都是在回答「面對這些已知的壞法,我該怎麼設計」。
就像醫生看診不會只寫「不舒服」,而要分「發炎、感染、外傷」——工程師面對系統故障,也得先分清楚是「該做的沒做」(遺漏)、還是「做了但亂做」(任意),防禦手段完全不同。
直接讀原文,旁邊就是白話
這幾句話是課本對遺漏失效與任意失效最精準的定義。原文放左邊,白話導讀放右邊。
被歸類為遺漏失效的錯誤,指的是行程或通訊通道「該做的動作沒做」的情況。
當我們說一個行程「當機」了,意思是它已經停住,再也不會執行程式裡的任何後續步驟。
如果其他行程能「確定地」偵測到某個行程已經當機,這種當機就叫做 fail-stop。
任意失效(或稱 Byzantine 失效)這個詞,用來描述最糟糕的失效語意——任何類型的錯誤都可能發生。
行程的任意失效沒辦法靠「它有沒有回應請求」來偵測,因為它可能就是任意地選擇不回覆。
一般 crash 靠 timeout(逾時)去猜對方是不是壞了,但在非同步系統裡,timeout 只能告訴你「對方沒回應」,卻不能告訴你他是當機了、變慢了,還是訊息根本還在路上。
把「壞的方式」定格成三大類
課本用員工來比喻這三種失效,非常好記:
行程的遺漏失效主要是crash(當機):本來該交報告卻人間蒸發。若公司有打卡系統(同步系統+保證訊息送達的 timeout),大家能確定他沒來,這就是 fail-stop;若沒有這套機制,你只知道他沒回訊息,卻不確定是請假、塞車還是真的當了。通訊通道也會遺漏失效——訊息在半路被丟掉(dropping messages),再細分為 send-omission、receive-omission、channel-omission。
他可能交假報告、亂傳訊息、甚至裝沒事其實什麼都沒做——回傳錯誤的值、該做的不做、不該做的亂做。你無法靠「他有沒有回應」判斷,因為他可能故意亂回。通道的任意失效(內容被竄改、送出不存在的訊息、重複投遞)在現實中很罕見,因為通訊軟體早就用 checksum 與序號把這類錯誤攔下來了。
只在同步系統裡才談得上——執行、傳遞或時鐘漂移超過了事先設定的界限。只有你事先約定了截止時間,才能說他「遲交=時序失效」;若你根本沒設期限(非同步系統),他再慢你也不能說他失效。
課本特別指出:失效可以依嚴重程度分級,前面談的遺漏失效、時序失效、效能失效都屬於良性失效(benign failures),而且分散式系統裡大多數失效都是良性的——真正棘手、防不勝防的任意失效,反而是少數。
招牌互動:這個情境屬於哪種失效?
把下面三個失效情境,拖到它對應的失效類別上。配完按「對答案」。
每個元件都由其他元件組成,因此可以用較不可靠的元件建出較可靠的服務——這就是遮蔽。例如 checksum 把「內容被破壞」這種任意失效,轉成「丟棄該訊息」的遺漏失效;再用重傳把遺漏失效藏起來——一步步把最壞情況,馴服成看起來一切正常。
失效模型防「壞掉」,安全模型防「使壞」
失效模型處理的是意外——東西自然壞掉。但分散式系統還要面對另一種威脅:有人故意搞破壞。安全模型建立在架構模型之上:要確保分散式系統安全,就要保護行程、保護它們互動用的通道、並保護它們封裝的物件,不被未授權存取。
伺服器替使用者管理一堆物件,使用者透過 client 送出invocation,要伺服器對物件做操作。不同物件給不同人用,因此要用access right(存取權)規定誰能對某物件做哪些操作。而擁有這份存取權、下命令的權威,就叫principal(當事人)——可以是使用者,也可以是行程。
為了分析威脅,課本假設一個enemy(敵人/對手)——它能對任何行程送任何訊息,也能讀取或複製任兩個行程之間的任何訊息。就像郵差路線上有個壞人,能偷看每封信、也能冒名寄信。這種假設不是說現實一定有這麼強的敵人,而是「假設最壞情況,防禦才不會留破口」。
敵人能幹什麼壞事:對行程 vs 對通道
假設好了敵人的能力,課本接著把威脅拆成兩個對象:行程和通訊通道。
伺服器可能同時收到來自許多不同 client 的 invocation,光看請求本身,它沒辦法確定這一次請求背後真正的 principal 是誰——來源位址可以被偽造。反過來,client 也可能收到冒牌伺服器(spoofing)送來的假結果,卻以為是真的。
敵人可以在訊息穿越網路與中間的閘道時,複製、竄改或注入訊息。另一種手法是把攔截到的舊訊息存下來,之後再重複送出,讓同一則訊息被一次又一次地重複利用——這就是重放攻擊(replay attack)。
這正是重放攻擊的經典例子——敵人存下一筆轉帳請求,重複送出就能一再得利。安全通道要靠時間戳才能擋住這種手法。
招牌互動:把「安全通道」一步步建起來
面對敵人竊聽、竄改的威脅,課本教的防禦是一層層疊上去的:先靠密碼學與共享祕密做加密(encryption),再做認證(authentication),最後疊出一條安全通道(secure channel)。點點看下面每個節點:
雙方確知彼此身分(不會被冒名)、內容防偷防改(隱私與完整性)、每封都有時間戳(防重放)——三個性質缺一不可,正是安全通道的完整定義。
還有兩種威脅,最後小試身手
除了竊聽與竄改,課本還點名兩種威脅:denial of service(阻斷服務)——用大量無謂請求灌爆資源,讓正常使用者用不了;以及mobile code(行動程式碼)——接收並執行外來程式碼,可能是木馬。
加密、認證、安全通道都有成本,所以實務上要先做threat model(威脅模型),盤點所有攻擊面,再權衡防禦的效益與成本——不是所有系統都要把每一道防線都拉滿。
這一章從物理模型的「一堆各異的電腦、靠不太準的網路連著」出發,走到架構模型的用戶端-伺服器與各種互動樣式,再到互動模型裡「同步 vs 非同步」這條分水嶺,最後停在這裡:失效模型告訴你系統會怎麼自然地壞掉——遺漏、任意、時序;安全模型告訴你系統會怎麼被人故意弄壞——敵人竊聽竄改、重放,而加密、認證、安全通道就是你的防線。這五個模型合起來,就是描述、分析、設計任何分散式系統的一整套語言。
從物理模型的硬體差異,到架構模型的分工方式,到互動模型的時間假設,再到今天的失效與安全——你已經走完了描述一個分散式系統所需的完整骨架。這套骨架會在後面每一章反覆被用到:不管談的是通訊協定、複製、交易,最終都繞不開「這個系統長什麼樣、會怎麼壞、會被誰盯上」這三個問題。準備好,翻到下一章,繼續往下深挖分散式系統的設計細節。