01

認識 Peer-to-Peer 的世界

P2P 的核心定義、與 client-server 的差別,以及每個節點既是用戶也是貢獻者的共同特徵。

先讀原文段落,旁邊就是白話

這是一本英文書。左邊放原文、右邊放白話導讀——你既讀得懂,也順手碰了原文。

原文 · 認識 Peer-to-Peer 的世界 423 10 PEER-TO-PEER SYSTEMS 10. 2 Napster and its legacy 10. 3 Peer-to-peer middleware 10. 5 Overlay case studies: Pastry, Tapestry 10.
白話導讀

P2P 的核心定義、與 client-server 的差別,以及每個節點既是用戶也是貢獻者的共同特徵。

什麼是 Peer-to-Peer 系統

P2P 的核心定義、與 client-server 的差別,以及每個節點既是用戶也是貢獻者的共同特徵。

STEP 1

深度探秘

沒有老闆,大家都是員工也都是客人

Peer-to-Peer 到底是什麼

我們最熟悉的網路模式是 client-server(用戶端-伺服器):你的瀏覽器(client)去跟某台強大的伺服器要資料。所有資源都集中在伺服器這一邊。

peer-to-peer(P2P,點對點) 完全翻轉了這個想法:

P2P 的目標是消除對中央伺服器的需求,靠著網路邊緣那些一般人的個人電腦,把資料與資源大規模地分享出去。

在 P2P 裡,每一台參與的電腦都叫做一個 peer(對等節點),它們有幾個共同特徵:

  • 每個使用者都貢獻資源:你想用別人的東西,自己也得拿出儲存空間、頻寬或運算力。
  • 所有節點功能對等:雖然各自貢獻的多寡可能不同,但每個節點的能力與責任是一樣的,沒有誰天生比較特別。
  • 不依賴中央管理:系統能正確運作,不需要任何由單一機構集中管理的設備。
  • 可提供一定程度的匿名性給資源的提供者與使用者。

換句話說,P2P 就是一群地位平等的電腦,互相幫忙、共同撐起一個服務。

💡
關鍵

P2P 靠網路邊緣對等的個人電腦互相分享資源,不需要中央伺服器,每個節點功能對等且都要貢獻。

STEP 2

生活妙喻

社區共享冰箱 vs 便利商店

便利商店與社區共享冰箱

想像兩種拿食物的方式:

  • client-server便利商店:所有商品都放在店裡,店家負責進貨、管理、結帳。你只是顧客,只負責拿。店越大,能服務的人越多,但一旦店面塞爆或停電,大家就都沒得買了。
  • P2P社區共享冰箱:每戶人家都把自己多的食材放進公用冰箱,也可以去拿別人放的。沒有「老闆」,每一戶既是提供者也是取用者

這個比喻剛好對應 P2P 的特徵:

共享冰箱的特性 對應的 P2P 概念
每戶都要放東西進去 每個使用者都貢獻資源
每戶角色一樣,沒有店長 所有節點功能對等
沒有總公司也能運作 不依賴中央管理
你不知道是誰放的菜 一定程度的匿名性

而且有個好處:就算某幾戶搬走了,冰箱裡別人放的東西還在,服務不會整個垮掉。

💡
關鍵

client-server 像便利商店有老闆集中管理;P2P 像社區共享冰箱,人人既貢獻又取用、沒有中心。

STEP 3

實用超能力

為什麼要這麼麻煩?因為規模

P2P 解決的真實問題:擴展性

為什麼不乾脆把伺服器蓋大一點就好?因為 client-server 有兩個天花板:

  1. 管理與故障成本:所有主機都得由服務提供者自己擁有與管理,電腦一多,管理和修復的成本就會主導一切。
  2. 網路頻寬限制:單一伺服器站點能拉到的實體頻寬有限,再多人連也擠不進來。

P2P 把這個問題分散掉。它的設計目標是:

打造一個完全去中心化、自我組織的服務,隨著電腦加入與離開,動態地把儲存與運算負載平衡到所有參與的電腦上。

flowchart TD
  A[使用者需求成長到全球規模] --> B{用 client-server}
  B --> C[伺服器與頻寬成為瓶頸]
  A --> D{用 P2P}
  D --> E[把負載分散到數十萬台個人電腦]
  E --> F[去中心化 自我組織 動態負載平衡]

真實例子:當寬頻普及後,大量桌機 24 小時開機上網,就成了適合分享資源的平台。SETI@home 就靠全球 391 萬台志願者電腦的零碎運算力,分析無線電望遠鏡資料尋找外星訊號,創下當時最大單一運算紀錄。這就是 P2P 的超能力——把散落各處的閒置資源聚沙成塔。

💡
關鍵

P2P 的價值在於擴展性:把儲存與運算負載分散到數十萬台個人電腦,突破單一伺服器的成本與頻寬天花板。

🔆
生活妙喻:client-server 與 P2P 的差別 ≈ 便利商店 vs 社區共享冰箱

便利商店由店家集中管理、顧客只取用;共享冰箱裡每戶既放東西也拿東西、沒有老闆,正如 P2P 每個節點都貢獻又取用、沒有中心。

🔆
生活妙喻:聚集閒置資源 ≈ 募集大家的零錢湊成一筆大錢

單一個人的零錢微不足道,但把幾百萬人的零錢聚在一起就很可觀;P2P 把每台電腦的零碎儲存與運算力聚集起來,撐起大規模服務。

本節字彙

peer(對等節點)
P2P 系統中一台參與的電腦,它和其他節點地位平等,既使用資源也貢獻資源。
🧠 peer=『同儕』,大家平起平坐沒有上下。
client-server(用戶端-伺服器)
傳統網路模式,資源集中在伺服器,用戶端只負責請求與取用。
🧠 一邊『點餐』一邊『出菜』,角色固定分明。
decentralized(去中心化)
系統不依賴任何單一中央節點來運作,控制與資料分散在許多節點上。
🧠 de-(去掉)+ central(中心)=沒有中央。
某新創想做一個全球音樂分享服務,預期使用者會多到接近世界人口規模。創辦人問你:「為什麼不乾脆一直加大我們的中央伺服器就好?」以下哪個是 P2P 相對於純 client-server 的核心理由?
在一個 P2P 系統裡,有位使用者說:「我只想下載別人的檔案,但我不打算分享自己的任何東西。」這違反了 P2P 的哪一項核心特徵?
下列哪一句最能描述 P2P 系統中各節點的角色關係?

不可變資料與三代演進

為何 P2P 最適合儲存不可變資料、用安全雜湊讓資源自我認證,以及 Napster 到中介軟體的三代演進。

STEP 1

深度探秘

用內容的指紋當名字

GUID:用安全雜湊產生的全球唯一名字

P2P 系統裡的資源(檔案、資料物件)要怎麼命名?答案是 GUID(globally unique identifier,全球唯一識別碼)

GUID 通常是把資源的部分或全部內容丟進一個安全雜湊函數(secure hash)(例如 SHA-1)算出來的值。

安全雜湊有個神奇特性:內容只要差一點點,算出的雜湊值就完全不同;而且幾乎不可能找到兩份不同內容算出相同雜湊。這帶來一個關鍵好處:

  • 自我認證(self-certifying):客戶端拿到資源後,可以自己重算一次雜湊,跟 GUID 比對。一致就代表內容沒被竄改。

這招很厲害,因為 P2P 的資料是存在不受信任的陌生人電腦上的,自我認證讓你不必信任存放者也能驗證內容。

但天下沒有白吃的午餐:

因為 GUID 是從內容算出來的,一旦內容改變,雜湊值就變了,原本的 GUID 就失效。所以這招要求資源的內容是**不可變(immutable)**的。

這就是為什麼 P2P 儲存系統「天生最適合存不可變的物件」,例如音樂、影片這類發布後就不再修改的檔案。

💡
關鍵

GUID 由內容的安全雜湊算出,能讓資源自我認證、防竄改,但代價是資源內容必須不可變。

STEP 2

生活妙喻

封蠟印章與會變心的菜單

蓋了封蠟印章的信

把 GUID 想成一封信的封蠟印章

  • 你把信的內容拿去蓋一個獨一無二的印章(雜湊),任何人收到信都可以檢查印章對不對。
  • 只要有人偷改信的一個字,印章就對不起來了——你立刻知道信被動過手腳。這就是自我認證

但這個印章有個前提:信的內容不能改。一旦你想改信,就得重蓋一個全新的印章(新的 GUID),舊印章就作廢了。

所以這套機制:

  • 不會再改的東西超好用:一首歌、一部電影,發布後內容固定,封蠟印章永遠有效。
  • 會一直改的東西很麻煩:像一份每天更新的菜單,每改一次就要換印章,所有指著舊印章的目錄全都要跟著更新。

這正是為什麼課本說 P2P 儲存「天生適合不可變物件」。要存會變的資料,得另外請『可信的伺服器』來管理版本(OceanStore、Ivy 就是這樣做)。

💡
關鍵

GUID 像封蠟印章,能驗證內容沒被改過,但只要內容一變印章就作廢,所以最適合不會再改的資料。

STEP 3

實用超能力

看懂 P2P 的三代演進史

P2P 發展的三個世代

了解這段演進史,能幫你把整章後面的內容串起來:

flowchart TD
  A[第一代 Napster] --> B[中央索引 找檔案 檔案存使用者電腦]
  C[第二代 Gnutella Freenet Kazaa BitTorrent] --> D[分散式索引 更高擴展性 匿名 容錯]
  E[第三代 中介軟體 Pastry Tapestry CAN Chord] --> F[與應用無關 保證有限跳數內送達 結構化放置副本]
世代 代表 索引方式 特點
第一代 Napster(1999) 中央索引 開創 P2P 檔案分享,但索引集中成弱點
第二代 Gnutella、Freenet、Kazaa、BitTorrent 分散式但各自為政 更可擴展、有匿名與容錯,但演算法各家不同
第三代 Pastry、Tapestry、CAN、Chord、Kademlia 結構化 overlay 與應用無關,保證在有限跳數內送達請求

第三代的關鍵突破是:它保證在有界的網路跳數內把請求送到,並有結構地把副本放在可用主機上,同時考慮主機的不穩定、可信度與負載平衡。後面幾章我們就會深入第三代的 Pastry 與 Tapestry。

💡
關鍵

P2P 經歷三代:Napster 的中央索引、第二代各自為政的分散式檔案分享、第三代與應用無關且保證有限跳數的中介軟體。

🔆
生活妙喻:用安全雜湊產生 GUID 並自我認證 ≈ 信件的封蠟印章

封蠟印章從信的內容產生,收信人能檢查印章是否相符以確認沒被竄改;GUID 由內容雜湊算出,客戶端重算比對即可驗證資源完整性。

🔆
生活妙喻:不可變物件最適合 ≈ 出版後的書 vs 每天更新的公告欄

已出版的書內容固定,封蠟印章永遠有效;每天更新的公告欄每改一次就要換印章,所有引用都得更新,正如可變資料對雜湊命名的不友善。

本節字彙

GUID(全球唯一識別碼)
P2P 系統用來識別節點與物件的純名稱,通常由內容的安全雜湊算出,全球範圍幾乎不會重複。
🧠 Globally Unique ID=全世界唯一的身分證號。
secure hash(安全雜湊)
把任意內容轉成固定長度指紋的函數,內容稍變指紋就全變,幾乎不可能偽造碰撞。
🧠 資料的『指紋』,改一個字指紋就不一樣。
immutable(不可變)
物件一旦建立其內容就不再改變的性質;P2P 雜湊命名最適合這類資料。
🧠 im-(不)+ mutable(可變)=不可變。
你從一個陌生人的 P2P 節點下載到一個 GUID 為 X 的檔案。你並不信任這個陌生人,擔心檔案被動過手腳。為什麼用安全雜湊產生的 GUID 能幫你確認檔案沒被竄改?
一個團隊想用 P2P 雜湊命名的方式儲存「每天都會被多人編輯的共享文件」。為什麼這直接套用會很麻煩?
為什麼說 P2P 儲存系統「天生最適合儲存不可變的物件」,例如音樂或影片檔案?
02

從 Napster 學到的教訓

Napster 的中央索引架構、五步驟流程、被告下架的法律原因,以及匿名性的議題。

先讀原文段落,旁邊就是白話

這是一本英文書。左邊放原文、右邊放白話導讀——你既讀得懂,也順手碰了原文。

原文 · 從 Napster 學到的教訓 Napster and its legacy 10. 3 Peer-to-peer middleware 10. 5 Overlay case studies: Pastry, Tapestry 10. 6 Application case studies: Squirrel, OceanStore, Ivy 10.
白話導讀

Napster 的中央索引架構、五步驟流程、被告下架的法律原因,以及匿名性的議題。

Napster 的運作與興衰

Napster 的中央索引架構、五步驟流程、被告下架的法律原因,以及匿名性的議題。

STEP 1

深度探秘

中央索引 + 邊緣檔案的混合招式

Napster 怎麼運作

Napster 在 1999 年推出,是第一個爆紅的 P2P 檔案分享服務,巔峰時有數百萬註冊用戶、數千人同時交換音樂。它的設計很巧妙:

索引集中、檔案分散——Napster 用一個(有複製的)中央索引記錄『誰有什麼檔案』,但實際的音樂檔案存在使用者自己的電腦上

它的運作可以拆成五個步驟:

sequenceDiagram
  participant C as 客戶端
  participant S as Napster 索引伺服器
  participant P as 持有檔案的對等節點
  C->>S: 1 查詢檔案位置
  S->>C: 2 回傳擁有該檔案的對等節點清單
  C->>P: 3 直接向對方請求檔案
  P->>C: 4 對方直接傳送檔案
  C->>S: 5 把自己的檔案更新到索引

注意第 3、4 步:檔案傳輸是兩台使用者電腦之間直接進行的,索引伺服器完全沒碰到檔案內容。第 5 步也很關鍵——客戶端會把自己擁有的檔案連結回報給索引,這樣資源池才會越來越大。

Napster 成功的關鍵,就是讓全網際網路的使用者能取用一大批分散在各處的檔案,實現了「分享網路邊緣資源」的理念。

💡
關鍵

Napster 用中央索引記錄誰有什麼檔案,但檔案存在使用者電腦上、由兩端直接傳輸,索引不碰檔案內容。

STEP 2

生活妙喻

社區的『誰有什麼』公佈欄

社區公佈欄與借東西

想像一個社區有個中央公佈欄

  • 每戶人家把『我有的東西』(一台梯子、一本書、一片 DVD)登記到公佈欄上。這就是 Napster 的索引
  • 你想借梯子,就去看公佈欄(步驟 1、2:查詢、拿到清單),發現三號住戶有。
  • 然後你直接走去三號家借(步驟 3、4:直接向對方拿東西)。公佈欄管理員根本不用碰到那把梯子。
  • 你借到後,也把自己有的東西登記上去(步驟 5:更新索引)。

這個比喻清楚點出 Napster 的精髓與弱點:

  • 精髓:東西(檔案)都在各家手上,分散而豐富。
  • 致命弱點:那個公佈欄是唯一的、位置固定的。如果有人想關掉整個借東西的活動,只要去拆掉公佈欄就好——不必一家一家去處理。Napster 的索引伺服器正是因為位置固定、無法匿名,最後成了法律訴訟的目標。
💡
關鍵

Napster 像社區中央公佈欄:東西在各家手上但登記集中,公佈欄位置固定,成了被攻擊與被告的單點。

STEP 3

實用超能力

為什麼被關,以及匿名性的兩難

Napster 為何被迫關閉

Napster 最後因版權訴訟被關。它的開發者辯稱:「複製過程完全發生在使用者電腦之間,我們沒參與,所以不該負責。」但這個論點失敗了:

索引伺服器被認定是整個過程不可或缺的一環。而且因為索引伺服器位於眾所周知的固定位址,營運者無法匿名,因此成了訴訟的明確目標。

課本給的啟示很深刻:一個更徹底分散的檔案分享服務,可能達成更好的責任分離——把責任攤到所有使用者身上,讓法律追訴變得極為困難。這也是第二代系統往「分散式索引」與「匿名」發展的動機之一。

匿名性:技術與社會的拉扯

匿名性對 P2P 設計者是個重要議題:

  • 在節點眾多的系統裡,可以讓請求與結果的路由繞得很曲折來隱藏來源,也可以把檔案內容拆散存到多個節點
  • 如果檔案在上傳前先加密,存放者甚至能合理地宣稱『我不知道我存了什麼』。

但匿名也有正當的社會價值:在壓迫的社會中,對抗審查、保護吹哨者、維護言論自由,都可能需要匿名。當然,這些匿名技術會增加成本,而且研究顯示現有匿名性在某些攻擊下其實很脆弱。

💡
關鍵

Napster 因索引伺服器位置固定、無法匿名而被告下架;更分散的設計能攤分責任,也讓匿名(含對抗審查)成為 P2P 的重要課題。

🔆
生活妙喻:中央索引 + 邊緣檔案 ≈ 社區中央公佈欄登記誰有什麼東西

公佈欄集中登記各家擁有的物品,借東西時直接去對方家拿,管理員不碰物品本身,正如 Napster 索引集中而檔案分散、由兩端直傳。

🔆
生活妙喻:中央索引是單點弱點 ≈ 拆掉公佈欄就能癱瘓整個借東西活動

要阻止社區互借只需拆掉唯一的公佈欄,不必逐戶處理;Napster 的索引伺服器位置固定無法匿名,因此成為訴訟的單一打擊點。

本節字彙

centralized index(中央索引)
集中記錄『哪個節點擁有哪些資源』的目錄,Napster 用它來定位檔案。
🧠 像一張集中的『誰有什麼』總清單。
network locality(網路鄰近性)
兩節點間的網路距離(如跳數);Napster 分配伺服器時會優先選離客戶端近的,以分散負載。
🧠 locality=『就近』,找離你近的人拿東西。
anonymity(匿名性)
隱藏資源提供者與使用者身分的性質,可用曲折路由、內容拆散與加密達成,對抗審查時尤其重要。
🧠 anonymous=『不具名』,查不到是誰。
Napster 的開發者在訴訟中辯稱「檔案複製完全發生在使用者電腦之間,我們沒參與」。法院為什麼仍認定 Napster 要負責?
在 Napster 的五步驟流程中,第 3、4 步是『客戶端直接向持有檔案的對等節點請求並接收檔案』。這個設計細節說明了什麼?
課本指出,一個『更徹底分散』的檔案分享服務可能比 Napster 更難被法律追訴。為什麼?

Napster 的限制與啟示

統一索引的可擴展性限制、Napster 借助音樂檔案特性的取巧之處,以及對後續系統的啟發。

STEP 1

深度探秘

統一索引:成也索引,敗也索引

統一索引的限制

Napster 證明了「幾乎完全靠一般網路使用者的資料與電腦」就能蓋出有用的大規模服務。但它有個結構性限制:

Napster 用一個(有複製的)統一索引(unified index),記錄所有可用的音樂檔案。

為什麼這對音樂分享還行,對很多應用卻是問題?

  • 對音樂分享而言,索引副本之間的一致性要求不強:就算某副本稍微過時、某首歌暫時搜不到,使用者晚點再下載也沒差,所以不太影響效能。
  • 但對許多其他應用來說,這就是個大限制了。課本點出關鍵原則:

除非存取資料物件的路徑被分散開來,否則物件的探索與定址很可能成為瓶頸。

換句話說,把『找東西』這件事集中在一處,遲早會塞車。後面第三代中介軟體最重要的工作,就是把這個「定位」的責任分散到整個網路。

💡
關鍵

Napster 的統一索引對一致性要求不高的音樂分享尚可,但存取路徑若不分散,物件的探索與定址終將成為瓶頸。

STEP 2

生活妙喻

只有一個服務台的大賣場

只有一個櫃台的大賣場

想像一間超大賣場,東西(檔案)擺得到處都是,但只有一個服務台負責回答「某商品在哪一櫃」。

  • 平日人不多時,這個服務台應付得來——就像音樂分享,大家不急、晚點查到也行。
  • 但一旦來客暴增,所有人都得排到同一個服務台問路,隊伍立刻大排長龍。商品再多、賣場再大都沒用,瓶頸在那個唯一的問路櫃台

這正是課本說的:「存取路徑若不分散,物件探索與定址會成為瓶頸。」

解法是什麼?把問路的服務分散——每個區域都設自己的小服務台,各自熟悉一部分商品的位置。這樣大家就近問路,不會全擠在一處。第三代 P2P 中介軟體做的就是這件事:把『知道東西在哪』這份知識分割並分散到所有節點上。

💡
關鍵

統一索引像賣場唯一的問路櫃台,人一多就塞車;解法是把『知道東西在哪』分散到各區自己的小櫃台。

STEP 3

實用超能力

Napster 偷吃步的兩個前提

Napster 借了音樂應用的東風

Napster 之所以能用相對簡單的設計成功,是因為它取巧地利用了音樂分享這個應用的特殊性質。看懂這兩點,你就懂為什麼同樣手法不能直接套到別的應用:

音樂分享的特性 帶來的好處
音樂檔案從不更新 不必確保所有副本在更新後仍保持一致
不要求個別檔案的可用性保證 某檔案暫時找不到,晚點下載即可,降低對個別電腦與連線可靠度的需求

換句話說,Napster 巧妙地把問題的『難的部分』給避開了:

  • 不用煩惱一致性(因為內容不變)。
  • 不用煩惱高可用性(因為晚點拿也行)。
flowchart TD
  A[音樂檔案不會更新] --> B[不需維持副本一致性]
  C[不要求單檔可用性保證] --> D[不需個別電腦高可靠]
  B --> E[簡單設計就能成功]
  D --> E

這給後人的啟示是:當你要設計支援會更新的資料需要高可用保證的 P2P 系統時,就必須正面解決這些被 Napster 繞過的難題——這正是 OceanStore、Ivy 等後續系統的挑戰。

💡
關鍵

Napster 靠音樂檔案不更新、不需單檔可用性保證這兩個特性繞過了一致性與高可用的難題;別的應用無法照搬,得正面解決。

🔆
生活妙喻:統一索引成為瓶頸 ≈ 大賣場只有一個問路櫃台

商品分散在賣場各處,但所有人都得到唯一的櫃台問路,人一多就大排長龍;統一索引把定位集中在一處,存取量大時就成瓶頸。

🔆
生活妙喻:利用應用特性繞過難題 ≈ 選一條剛好沒有陡坡的路線

Napster 像挑了一條剛好避開陡坡的路:因為音樂不更新、不需高可用,它就不必爬『一致性』與『高可用』這兩座山。

本節字彙

unified index(統一索引)
把所有可用資源集中記錄在一處的索引;Napster 採用之,雖有複製但存取路徑仍集中。
🧠 unified=『統一集中』的一份大目錄。
consistency(一致性)
多個副本之間資料保持相同的程度;音樂分享對此要求不高,但更新型應用就很重要。
🧠 各副本『說法一致』,不會你一版我一版。
bottleneck(瓶頸)
系統中限制整體吞吐量的那個最窄環節;存取路徑不分散時,定位服務就會成為瓶頸。
🧠 瓶子的『頸部』最窄,東西都卡在那裡。
為什麼 Napster 的統一索引對音樂分享『還算可以』,但對許多其他應用會是限制?
課本說『除非存取資料物件的路徑被分散,否則物件的探索與定址很可能成為瓶頸』。這句話對第三代 P2P 中介軟體的設計有什麼指引?
Napster 能用相對簡單的設計成功,部分是因為『音樂檔案從不更新』。這個特性具體幫它省去了什麼麻煩?
03

Peer-to-Peer 中介軟體與 Routing Overlay

中介軟體要提供的簡單介面、全球可擴展、負載平衡、就近互動、容忍高度動態的主機進出與安全匿名等需求。

先讀原文段落,旁邊就是白話

這是一本英文書。左邊放原文、右邊放白話導讀——你既讀得懂,也順手碰了原文。

原文 · Peer-to-Peer 中介軟體與 Routing Overlay GUID, IP address] pairs that act as node handles specifying the next hop to be taken by messages addressed to GUIDs that match each given prefix. Grey-shaded entries in the table body indicate that the prefix matches the current GUID up to the given value of p: the next row down or the leaf set should be examined to find a route. Although there are a maximum of 32 rows in the table, only log16 N rows will be populated on average in a network with N active nodes. p = GUID prefixes and corresponding node handles n 0 0 1 2 3 4 5 6 7 8 9 A B C D E F n n n n n n n n n n n n n n n 1 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6F 6E 6F n n n n n n n n n n n n n n n 2 650 651 652 653 654 655 656 657 658 659 65A 65B 65C 65D 65E 65F n n n n n n n n n n n n n n n 3 65A0 65A1 65A2 65A3 65A4 65A5 65A6 65A7 65A8 65A9 65AA 65AB 65AC 65AD 65AE 65AF n n n n n n n n n n n n n n n SECTION 10.
白話導讀

中介軟體要提供的簡單介面、全球可擴展、負載平衡、就近互動、容忍高度動態的主機進出與安全匿名等需求。

中介軟體的功能與非功能需求

中介軟體要提供的簡單介面、全球可擴展、負載平衡、就近互動、容忍高度動態的主機進出與安全匿名等需求。

STEP 1

深度探秘

中介軟體要做的『功能』與要達到的『品質』

P2P 中介軟體要解決什麼

P2P 應用的核心難題是:讓客戶端能快速又可靠地存取資料,不論資料散落在網路何處。 第三代中介軟體就是專門來自動處理『放置』與『定位』這兩件事的。

它的需求分成兩類:

功能需求(Functional)——它要『做到什麼』

  • 讓客戶端能定位並與任一資源溝通,即使資源散布在許多主機上。
  • 隨意新增與移除資源,也能新增與移除主機
  • 提供一個簡單、與資源型別無關的程式介面給應用開發者(就像其他中介軟體一樣)。

非功能需求(Non-functional)——它要『做得多好』

功能需求講的是『能不能做』,非功能需求講的是『做得夠不夠好』,下一步會逐一介紹。簡單說,它必須在極大規模、會頻繁變動、且彼此不太信任的環境下,依然跑得又快又穩。

把功能需求想成『菜單上有沒有這道菜』,把非功能需求想成『上菜快不快、味道穩不穩、衛不衛生』。

💡
關鍵

P2P 中介軟體的功能需求是定位與存取任一分散資源、可隨意增刪資源與主機並提供簡單介面;非功能需求則關乎做得多好。

STEP 2

生活妙喻

全球快遞公司的服務承諾

全球快遞公司

把 P2P 中介軟體想成一家全球快遞公司,客戶(應用程式)只要說「把這件東西送到收件人手上」,剩下的路線規劃全交給它。

它的功能很單純:收件、送件、可以隨時開新據點或關掉舊據點。但真正考驗它的是這些服務品質(非功能需求)

快遞公司的挑戰 對應的非功能需求
要能服務全世界數十萬個地址 全球可擴展性:支援數萬到數十萬台主機、數百萬個物件
不能讓某幾個站點累垮,別的閒著 負載平衡:把工作平均分散
盡量讓貨物走近的路線 就近互動最佳化:把資源放在常存取它的節點附近
據點可能隨時開張或倒閉 容忍高度動態的主機進出
不能讓陌生據點偷看或竄改包裹 異質信任下的資料安全
有時收件/寄件人想匿名 匿名、可否認性與抗審查

一家只會收送件、卻常常爆倉、走冤枉路、站點一倒就找不到包裹的快遞公司,是沒人敢用的。非功能需求才是真正的勝負關鍵。

💡
關鍵

中介軟體像全球快遞:功能是收送與開關據點,但真正的考驗是可擴展、負載平衡、就近、容忍據點進出、安全與匿名等服務品質。

STEP 3

實用超能力

為什麼『主機隨時會落跑』是最大魔王

高度動態:P2P 最棘手的非功能需求

在這些非功能需求裡,最折磨人的是容忍高度動態的主機進出。因為 P2P 的主機是一般人的電腦,沒人保證它會一直開機、連線、不出錯。

課本舉了驚人的對比數據:

環境 平均連線時長(session) 同時在線比例
Overnet(一般網際網路使用者,85000 台主機) 平均 135 分鐘、中位數 79 分鐘 1468 台抽樣中僅 260–650 台在線
微軟企業內網(20000 台機器) 平均 37.7 小時 約 14700–15600 台在線

差距為何這麼大?因為一般網路使用者會關機、斷線、隨興進出;而企業內網的機器通常整天開著、由專人管理。

這代表中介軟體必須能:

flowchart TD
  A[主機加入] --> B[整合進系統並重新分配負載以利用其資源]
  C[主機離開 自願或故障] --> D[偵測其離開並把負載與資源重新分配給其他節點]

換句話說,系統要像一支永遠在換人卻不能停賽的球隊:隨時有人上場、有人離場,但比賽要持續進行。這種「在不穩定的基礎上提供穩定服務」的能力,正是 P2P 中介軟體最核心的本事。

💡
關鍵

最棘手的非功能需求是容忍主機高度動態進出:一般網路使用者連線短暫又隨興,系統必須在主機不斷上下線時自動整合、偵測離開並重新分配負載。

🔆
生活妙喻:功能需求 vs 非功能需求 ≈ 菜單有這道菜 vs 上菜快又穩又衛生

功能需求是『能不能做到』,像菜單上有沒有;非功能需求是『做得多好』,像上菜速度、口味穩定與衛生,後者才決定餐廳成敗。

🔆
生活妙喻:容忍主機高度動態進出 ≈ 一支不斷換人卻不能停賽的球隊

球員隨時上下場,但比賽必須繼續;P2P 主機不斷進出,中介軟體要持續整合新節點、偵測離開者並重新分配負載,服務不能中斷。

本節字彙

functional requirement(功能需求)
系統『必須能做到的事』,如定位資源、增刪資源與主機、提供簡單介面。
🧠 function=功能,講『能做什麼』。
non-functional requirement(非功能需求)
系統『要做得多好』的品質要求,如可擴展、負載平衡、就近、容忍動態、安全。
🧠 non-functional=不是『能不能』而是『好不好』。
session(連線時段)
一台主機從上線到被自願或被迫斷線的這段可用期間;一般使用者的 session 往往很短。
🧠 一次『開機上線到下線』的時段。
下列哪一項屬於 P2P 中介軟體的『功能需求』,而非『非功能需求』?
課本比較了 Overnet(一般網路使用者)與微軟企業內網的主機連線時長:前者平均 135 分鐘,後者平均 37.7 小時。這個巨大差距主要說明了什麼?
當一台新主機加入 P2P 系統時,中介軟體應該做什麼?

Routing Overlay 與分散式雜湊表

routing overlay 的角色與任務、GUID 作為純名稱,以及 DHT 與 DOLR 兩種 API 模型。

STEP 1

深度探秘

在應用層自己蓋一套路由系統

什麼是 Routing Overlay

P2P 中介軟體的核心是一個叫 routing overlay(路由覆蓋層) 的分散式演算法,負責定位節點與物件。

它叫 overlay(覆蓋層),是因為它在應用層自己實作了一套路由機制,獨立於底層的 IP 路由之上

routing overlay 的主要任務與其他職責:

  • 路由請求到物件:客戶端把含有物件 GUID 的請求交給 overlay,overlay 就把它路由到存有該物件副本的節點。若物件有多個副本,overlay 會送到最近的『活著』的節點
  • 插入物件:節點為新物件算出 GUID 並通知 overlay,使其可被所有人取用。
  • 刪除物件節點加入與移除:節點進出時,overlay 會重新分配責任。

這裡的 GUID 是所謂的純名稱(pure name,又稱不透明識別碼(opaque identifier)

完全不透露物件的位置——光看 GUID 你猜不出東西放在哪。

💡
關鍵

routing overlay 是建在 IP 之上的應用層路由演算法,靠不透露位置的 GUID 把請求路由到持有物件副本的最近節點。

STEP 2

生活妙喻

一群只認得鄰居的接力郵差

接力傳遞的郵差網路

想像一個沒有總郵局、沒有完整地址簿的世界,只有一群郵差,每個郵差只認得一小部分的鄰居,並大致知道整個世界的方向感。

你要寄信給一個只知道『編號』(GUID)的收件人:

  • 你把信交給身邊任一個郵差。
  • 這個郵差看編號,把信轉給他認識的、編號離目標更近的那個郵差
  • 下一個郵差再做同樣的事……一棒接一棒,最後信就到了最接近目標編號的郵差手上。

這就是 routing overlay 的精神:沒有人需要知道全世界所有東西在哪,每個節點只要維護一小部分的『鄰居知識』,靠接力就能把請求送到目的地。

而那個『編號』(GUID)很特別——它是純名稱:純粹是個身分代號,完全看不出收件人住哪。這跟我們平常的地址(含國家、城市、街道,看得出位置)完全不同。位置的事,交給接力郵差們合力解決就好。

💡
關鍵

routing overlay 像一群只認得鄰居的接力郵差,靠 GUID 編號一棒棒轉送,沒人需要掌握全局;GUID 是純名稱,看不出位置。

STEP 3

實用超能力

兩種 API:DHT 與 DOLR

DHT 與 DOLR:兩種存取風格

因為 GUID 是隨機分布的識別碼,且被用來決定物件放哪、又怎麼取回,所以 routing overlay 常被稱為分散式雜湊表(DHT,Distributed Hash Table)。它有兩種常見的 API 風格:

DHT 風格(如 Pastry 上的 PAST)

put(GUID, data)   把資料存到所有負責此 GUID 的節點上(含副本)
remove(GUID)      刪除 GUID 與其關聯資料
value = get(GUID) 從負責節點之一取回資料

特點:你只管 put / get,由 DHT 層自己決定資料放哪、複製到哪。 資料放在 GUID 數值最接近的節點,以及接下來最接近的 r 個主機(r 是複製因子)。

DOLR 風格(如 Tapestry)

publish(GUID)              讓執行此操作的節點成為該 GUID 物件的主機
unpublish(GUID)            讓該物件無法被存取
sendToObj(msg, GUID, [n])  把訊息送給物件 可選送給 n 個副本

特點:物件可以放在任何地方,由你(應用)決定,DOLR 層只負責維護 GUID 與『副本所在節點位址』的對應,並把請求路由到最近的副本。

比較 DHT DOLR
物件放哪 由 DHT 層決定(放在 GUID 最近的節點) 由應用決定,再用 publish 告知
核心操作 put / get publish / sendToObj

簡單記:DHT 像你把東西寄到『置物櫃中心』讓系統決定放哪格;DOLR 像你自己保管東西,只去登記『它在我這』。

💡
關鍵

routing overlay 常以 DHT 形式呈現:DHT 用 put get、由系統決定放置;DOLR 用 publish sendToObj、由應用決定放置並登記副本位置。

🔆
生活妙喻:routing overlay 接力路由 ≈ 一群只認得鄰居的接力郵差

每個郵差只認識一小部分鄰居並有方向感,靠把信轉給編號更接近目標的郵差,一棒接一棒送達;沒人需要掌握全局,正如 overlay 各節點只維護局部知識。

🔆
生活妙喻:DHT 與 DOLR 的差別 ≈ 置物櫃中心 vs 自己保管再登記

DHT 像把東西交給置物櫃中心、由系統決定放哪格(put/get);DOLR 像你自己保管物品、只去登記『它在我這』(publish),由系統記下位置並導引到最近副本。

本節字彙

routing overlay(路由覆蓋層)
建在 IP 之上的應用層分散式路由演算法,負責把請求路由到持有物件副本的節點。
🧠 overlay=『蓋在上面』的一層路由。
DHT(分散式雜湊表)
用 GUID 當鍵、put/get 存取的分散式儲存抽象,由系統決定資料放在哪些節點。
🧠 把雜湊表『攤開分散』到很多台電腦。
pure name / opaque identifier(純名稱/不透明識別碼)
完全不透露物件位置的識別碼;GUID 就是這種,看了也猜不出東西放哪。
🧠 opaque=『不透明』,看不穿裡面藏的位置。
為什麼說 GUID 是一種『純名稱』或『不透明識別碼』?這對 routing overlay 有什麼意義?
一個物件在 P2P 系統中有多個副本。當客戶端用 GUID 請求它時,routing overlay 理想上會怎麼處理?
在 DHT 模型(如 PAST over Pastry)中,當你呼叫 put(GUID, data) 時,資料會被放在哪裡?

Overlay 路由 vs IP 路由

為何需要在 IP 之上再加一層應用層路由,從規模、負載平衡、容錯、目標識別等面向比較。

STEP 1

深度探秘

既然有 IP 路由,為何還要疊一層?

一個合理的疑問

routing overlay 乍看跟網際網路最底層的 IP 封包路由很像——都是在『把訊息送到目的地』。那為什麼 P2P 還要在 IP 之上再疊一層應用層路由?

課本用一張比較表回答(Figure 10.1),從五個面向看出兩者的差異。我們先看前三個:

面向 IP 路由 Overlay 路由
規模 Scale IPv4 只有 2³² 個位址,IPv6 雖大但位址是階層化的、大量空間被預先分配 GUID 命名空間非常大且平坦(> 2¹²⁸),能更充分被使用、定址更多物件
負載平衡 路由器負載由網路拓樸與流量決定 物件位置可隨機化,使流量與網路拓樸脫鉤
網路動態(增刪物件/節點) 路由表非同步更新,時間常數約 1 小時 路由表可同步或非同步更新,延遲只要幾分之一秒

關鍵洞察:IP 的設計受限於它身為網際網路主協定的『歷史包袱』,這些限制太根深柢固,無法直接改造來支援 P2P,所以乾脆在它之上另蓋一層。

💡
關鍵

Overlay 之所以疊在 IP 之上,是因為 IP 的階層化定址、慢速更新等歷史包袱太根深柢固;overlay 提供平坦巨大的命名空間、隨機化位置與快速更新。

STEP 2

生活妙喻

國家行政區劃 vs 興趣社團名冊

行政地址 vs 社團編號

IP 位址想成國家的行政地址:「X 國 Y 市 Z 街 5 號」。它是階層化的,而且各區段早就被劃分好、分配給特定行政單位。好處是好管理,壞處是僵硬——你不能隨便把一個門牌搬到地球另一端。

GUID 想成一個全球興趣社團的會員編號:純粹是一串隨機號碼,不綁任何地點。社團名冊很大很平坦,誰都能加入拿一個號碼,而且這個號碼和你住哪完全無關

這個比喻解釋了兩者的差異:

  • 規模:行政地址受限於既有劃分;社團編號空間巨大又平坦,能容納更多會員。
  • 負載平衡:行政地址綁定地理,流量跟著地理走;社團編號隨機分布,可以讓事情故意打散,不跟地理綁在一起。
  • 更新速度:改行政區劃要走漫長的官僚流程(約 1 小時級別);社團名冊只要動動滑鼠就更新(幾分之一秒)。

所以 P2P 選擇用『社團編號』這套更靈活的命名系統,疊在僵硬的『行政地址』之上。

💡
關鍵

IP 位址像僵硬、階層化、預先劃分的行政地址;GUID 像平坦、隨機、與地點無關的社團會員編號,更適合大規模、隨機放置與快速更新。

STEP 3

實用超能力

容錯、最近副本與匿名:後兩個面向

Overlay 在容錯、定位與安全上的優勢

比較表還有後三個面向,這些正是 P2P 真正想要的超能力:

flowchart TD
  A[容錯] --> A1[IP 由管理者設計冗餘 容忍單一故障 n 倍複製昂貴]
  A --> A2[Overlay 路由與物件參考可 n 倍複製 容忍 n 個節點或連線故障]
  B[目標識別] --> B1[IP 每個位址對應唯一目標節點]
  B --> B2[Overlay 可路由到目標物件的最近副本]
  C[安全與匿名] --> C1[IP 僅在所有節點受信任時安全 無法匿名]
  C --> C2[Overlay 在有限信任環境也能達成安全 可提供一定匿名]

逐項看:

  • 容錯:IP 的冗餘由管理者設計,通常只能容忍單一路由器或連線故障,做 n 倍複製成本高昂;Overlay 則可把路由與物件參考複製 n 份,容忍 n 個節點或連線故障。
  • 目標識別:每個 IP 位址只對應唯一一個目標節點;Overlay 卻能把訊息路由到目標物件的最近副本——這對效能與可用性是巨大優勢。
  • 安全與匿名:IP 定址只有在所有節點都受信任時才安全,且位址擁有者無法匿名;Overlay 則能在信任有限的環境下達成安全,並提供一定程度的匿名

總結:Overlay 不是要取代 IP,而是借助 IP 來傳送,同時在上層補上 IP 給不了的:超大平坦命名、隨機放置、n 倍複製容錯、最近副本路由,以及有限信任下的安全與匿名。

💡
關鍵

相較 IP,overlay 能 n 倍複製以容忍 n 個故障、路由到目標物件的最近副本,並在信任有限的環境下達成安全與一定的匿名。

🔆
生活妙喻:GUID 命名空間 vs IP 位址 ≈ 全球社團會員編號 vs 國家行政地址

行政地址階層化、預先劃分、綁定地理;社團編號平坦巨大、隨機、與地點無關,正如 GUID 比 IP 更利於大規模、隨機放置與快速更新。

🔆
生活妙喻:路由到最近副本 ≈ 去最近的連鎖分店而非指定唯一一家

IP 像只能找那唯一一家店;overlay 像連鎖店,系統自動把你導向最近還營業的分店,效能與可用性都更好。

本節字彙

flat namespace(平坦命名空間)
識別碼之間沒有階層結構、可均勻使用的命名空間;GUID 空間平坦且巨大,能定址大量物件。
🧠 flat=平的,沒有層層分級。
n-fold replication(n 倍複製)
把資料或參考複製 n 份分散存放,使系統能容忍 n 個節點或連線故障。
🧠 複製 n 份,壞 n 個還活著。
nearest replica(最近副本)
與請求者網路距離最近的那份物件副本;overlay 可把請求路由到它以降低延遲。
🧠 就近取貨,找最近的那一份。
既然 IP 路由已經能把訊息送到目的地,為什麼 P2P 系統還要在它之上疊一層 routing overlay?
在『負載平衡』這個面向上,overlay 路由相較於 IP 路由的優勢是什麼?
關於『目標識別』,IP 路由與 overlay 路由的根本差異是什麼?
04

案例研究:Pastry、Tapestry 與非結構化

Pastry 的 128 位元 GUID、leaf set 與 routing table 兩階段路由、prefix routing 如何達成 O(log N)

先讀原文段落,旁邊就是白話

這是一本英文書。左邊放原文、右邊放白話導讀——你既讀得懂,也順手碰了原文。

原文 · 案例研究:Pastry、Tapestry 與非結構化 PASTRY, TAPESTRY 439 Figure 10. 6 illustrates this for a Pastry system with l = 4. (In typical real installations of Pastry, l = 8. ) Based on the definition of leaf sets we can conclude that at each step M is forwarded to a node that is closer to D than the current node and that this process will eventually deliver M to the active node closest to D.
白話導讀

Pastry 的 128 位元 GUID、leaf set 與 routing table 兩階段路由、prefix routing 如何達成 O(log N) 跳數。

Pastry 的路由原理

Pastry 的 128 位元 GUID、leaf set 與 routing table 兩階段路由、prefix routing 如何達成 O(log N) 跳數。

STEP 1

深度探秘

在一個環形的數字空間裡找路

Pastry 是什麼

Pastry 是一個實作 DHT 的 routing overlay。它的關鍵特性:

  • 所有節點與物件都有 128 位元的 GUID。節點的 GUID 由其公鑰的安全雜湊算出,物件的由名稱或內容算出,因此 GUID 隨機散布在 0 到 2¹²⁸−1 之間
  • 在有 N 個節點的網路中,Pastry 能在 O(log N) 步內,正確把訊息路由到任一 GUID。
  • 若該 GUID 對應的節點正活著,訊息就送給它;否則送給數值上最接近它的活著節點

這裡有個重要觀念:

Pastry 講的『接近(closeness)』是在一個完全人造的空間——GUID 的數字空間——而不是真實的地理或網路距離。

而且這個數字空間被當成環形(circular):GUID 0 的下方鄰居是 2¹²⁸−1,首尾相接。實際在網際網路上把訊息從一個 Pastry 節點送到另一個,可能要走很多個 IP 跳;為了不繞遠路,Pastry 會用 locality metric(在地性度量,如跳數或往返延遲) 來挑選鄰居,盡量讓 GUID 上的鄰居在真實網路上也近。

💡
關鍵

Pastry 用 128 位元隨機 GUID,在環形的數字空間裡以 O(log N) 步路由到數值最接近的活著節點,並用 locality metric 讓鄰居在真實網路上也近。

STEP 2

生活妙喻

圍成一圈的人傳紙條

圍成一圈、按號碼傳紙條

想像一大群人圍成一個圓圈,每人胸前有一個隨機號碼(GUID),號碼首尾相接(最大號旁邊就是 0 號)。你要把紙條送到『某個號碼』手上。

招式一:只認得左右鄰居(leaf set)

最笨但保證正確的方法:每個人只記得號碼上離自己最近的左右各幾位鄰居(這叫 leaf set,葉節點集合)。你拿到紙條,就看目標號碼,把紙條傳給你左右鄰居中號碼最接近目標的那個人。

  • 好處:一定送得到。
  • 壞處:超慢!一圈這麼多人,一位一位傳,平均要傳 N/2l 次。

招式二:認得『各種開頭』的遠方朋友(routing table)

聰明的做法:每個人除了左右鄰居,還準備一張通訊錄(routing table),裡面有號碼開頭各不相同的遠方朋友。要送紙條時,先看目標號碼前面幾位和自己一樣,就直接跳給一個『開頭更接近目標』的遠方朋友。一跳就拉近一大段,所以只要 O(log N) 次就到。

這就是 prefix routing(前綴路由) 的精神:每跳都讓『開頭相符的位數』多一位,像在玩猜數字一樣快速逼近。

💡
關鍵

Pastry 像圍成圈按號碼傳紙條:leaf set 只認左右鄰居保證正確但慢,routing table 認得各種開頭的遠方朋友,靠 prefix routing 每跳多對一位快速逼近。

STEP 3

實用超能力

看懂 prefix routing 的兩階段演算法

Pastry 路由演算法(兩階段理解)

GUID 用十六進位來看。routing table 依 GUID 的十六進位前綴分類,有 32 列(128÷4),每列 15 個項目(每個可能的十六進位值各一,扣掉自己那個值)。

路由時的判斷流程(簡化版):

flowchart TD
  A[收到要送往 D 的訊息] --> B{D 落在我的 leaf set 範圍內嗎}
  B -- 是 --> C[轉給 leaf set 中數值最接近 D 的節點或就是我]
  B -- 否 --> D[找出 D 與我 GUID 的最長共同前綴長度 p]
  D --> E{routing table 該位置有項目嗎}
  E -- 有 --> F[轉給該節點 它與 D 多共享一位前綴]
  E -- 無 --> G[轉給 leaf set 或 table 中前綴同樣長但數值更接近 D 的節點]

關鍵在 prefix routing:比較目標 D 與自己 A 的十六進位數字,由左到右找出最長共同前綴的長度 p,然後用 p 當列、D 的第 p+1 位當行,去 routing table 取出一個『與 D 多共享一位前綴』的節點當下一跳。

用一個課本例子感受一下:從 65A1FC 送到 D46A1C,靠 routing table 大約 log₁₆(N) 跳就能送達,路徑像這樣逐步逼近:65A1FC → D13DA3 → D4213F → D462BA → D46A1C,每一跳開頭相符的位數都在增加。

這就是 Pastry 的超能力:在龐大網路中,只靠每個節點維護一小張表,就能用對數級的跳數精準找到任何東西。

💡
關鍵

Pastry 路由先看 leaf set,否則用 prefix routing:算出與目標的最長共同前綴 p,查 routing table 跳到多共享一位前綴的節點,每跳逼近一位,達成 O(log N)。

🔆
生活妙喻:環形 GUID 空間與 leaf set ≈ 圍成一圈、只認得左右鄰居的人

眾人圍圈按隨機號碼排列、首尾相接,每人只記得左右最近的鄰居(leaf set),靠把紙條傳給號碼更接近目標的鄰居保證送達,但很慢。

🔆
生活妙喻:prefix routing ≈ 玩猜數字時一位一位對上

每跳讓『開頭相符的位數』多一位,就像猜數字一步步鎖定,使搜尋以對數速度逼近目標,達成 O(log N) 跳。

本節字彙

leaf set(葉節點集合)
每個 Pastry 節點記錄的、GUID 數值上左右最接近的鄰居清單;單靠它也能正確但低效地路由。
🧠 leaf=身邊最近的『左右鄰居』。
routing table(路由表)
依 GUID 前綴分類、記錄各種開頭遠方節點的表;用於 prefix routing 快速跳躍。
🧠 一本『各種開頭都有人』的通訊錄。
prefix routing(前綴路由)
每跳讓目標 GUID 的共同前綴多相符一個十六進位數字的路由法,達成 O(log N) 跳數。
🧠 一位一位『對開頭』,越對越接近。
Pastry 宣稱能在 N 個節點的網路中以 O(log N) 步路由到任一 GUID。這個對數級效能主要靠什麼達成?
Pastry 中所謂節點之間的『接近(closeness)』,指的是什麼?
若 Pastry 節點只用 leaf set(左右最近鄰居)來路由,會發生什麼?

Pastry 的容錯與在地性

節點加入與離開的協定、leaf set 修復、heartbeat 偵測失效、locality 最佳化與引入隨機性對抗惡意節點。

STEP 1

深度探秘

新節點如何加入,舊節點如何離開

節點加入:O(log N) 訊息就搞定

Pastry 是完全自我組織的。新節點加入時,會用一個加入協定來取得自己的 routing table 與 leaf set,並通知其他節點更新:

  1. 新節點先算出自己的 GUID(通常用 SHA-1 雜湊公鑰),然後聯絡一個附近的 Pastry 節點 A。
  2. A 透過 Pastry 把一個 join 訊息路由出去,最後會到達 GUID 數值上最接近新節點 X 的現有節點 Z。
  3. 沿途經過的所有節點(A、B、C…、Z)都把自己相關的 routing table 與 leaf set 內容傳給 X。
  4. X 拼湊出自己的表:A 的第一列適合當 X 的第一列、B 的第二列適合 X 的第二列…而 Z 的 leaf set 幾乎就是 X 想要的 leaf set(只差一個成員)。
  5. 最後 X 把自己的表內容送給所有相關節點,讓它們也更新。

整個加入過程只需要 O(log N) 則訊息

節點失效或離開

當一個節點的直接鄰居(GUID 空間上的)再也聯絡不上它時,就認定它失效。此時要修復含有該失效節點的 leaf set:發現者找一個靠近失效節點的活節點,要它的 leaf set 副本,從中找到合適的替補。routing table 的修復則是**『發現時才修』**——路由失敗時就改用同一列的另一個項目。

💡
關鍵

Pastry 完全自我組織:新節點靠 join 訊息沿路收集鄰居資料、O(log N) 則訊息即可建表;節點失效時靠鄰居的 leaf set 副本找替補修復。

STEP 2

生活妙喻

新鄰居搬進社區,老鄰居搬走

社區的迎新與補位

把 Pastry 想成一個自我管理的社區:

迎新(節點加入)

新住戶 X 搬來,先找一個附近的老鄰居 A 打招呼。A 幫他把一封『自我介紹信』(join 訊息)沿著社區傳出去,傳到號碼跟 X 最像的住戶 Z。沿途每戶都把自己的通訊錄分享一部分給 X:

  • A 離 X 近,A 的『第一頁通訊錄』很適合借給 X 當第一頁。
  • 一路上各戶各借一頁最合適的。
  • 號碼最像 X 的 Z,他的『左右鄰居名單』幾乎就是 X 要的,只差一位。

這樣 X 很快就湊出自己完整的通訊錄,再把自己加進大家的名單。整個迎新成本很低(O(log N) 則訊息)。

補位(節點失效)

如果某戶突然失聯(左右鄰居敲門都沒人應),鄰居就知道他失效了。發現的人去找另一個靠近失聯者的鄰居,借來他的『左右鄰居名單』,從裡面挑一位來補上空缺。至於通訊錄裡的遠方朋友若搬走了,不急著全面更新,等下次寄信失敗時,再換同一頁的另一個朋友即可。

💡
關鍵

Pastry 像自我管理社區:迎新時新住戶沿路向鄰居各借一頁通訊錄快速建表;補位時向附近鄰居借名單找替補,遠方朋友則發現失效時才換。

STEP 3

實用超能力

在地性、heartbeat 與對抗惡意節點

三招讓 Pastry 又快又穩又安全

在地性(locality):讓路由走近路

Pastry 的路由結構高度冗餘——任兩節點間有很多條路。建表時,每個位置常有好幾個候選節點,Pastry 用 proximity neighbour selection(鄰近鄰居選擇) 配合 locality metric(IP 跳數或延遲)挑最近的。雖然資訊不完整無法達到全域最佳,但模擬顯示路由平均只比最佳路徑長約 30–50%

Heartbeat(心跳):偵測失效

所有節點會定期向 leaf set 中的鄰居發心跳訊息(固定間隔發送、表示『我還活著』)。若太久沒收到某鄰居的心跳,就啟動修復程序。MSPastry 還在每一跳加上確認(acknowledgement):沒收到確認就改走另一條路重送,並把該節點標記為疑似失效。此外用 gossip protocol(八卦協定) 約每 20 分鐘交換一次路由表資訊,修補失效項目、防止在地性慢慢退化。

對抗惡意節點:引入隨機性

flowchart TD
  A[標準 prefix routing 走最佳路徑] --> B{少數情況隨機調整}
  B --> C[故意取較短的共同前綴 改用較早一列的路由]
  C --> D[產生不同但較不最佳的路徑]
  D --> E[搭配客戶端重送 即使有少量惡意節點也能最終成功]

因為 heartbeat 不一定夠快、也擋不住故意搗亂的惡意節點,所以:依賴可靠送達的客戶端要採 at-least-once(至少一次) 機制、重送請求數次,給 Pastry 時間偵測與修復;同時在路由選擇中引入少量隨機性,讓重送能繞過卡住的惡意節點,最終成功。

💡
關鍵

Pastry 用 locality metric 挑近鄰把繞路控制在約 30–50%、用 heartbeat 與每跳確認偵測失效、並靠路由隨機性加客戶端重送來對抗少量惡意節點。

🔆
生活妙喻:節點加入沿路收集鄰居資料 ≈ 新住戶向沿途鄰居各借一頁通訊錄

新住戶的自我介紹信沿社區傳遞,沿途每戶把最合適的一頁通訊錄借給他,號碼最像的那戶再借出左右鄰居名單,快速拼出完整資料。

🔆
生活妙喻:routing table 發現時才修 ≈ 寄信失敗才換另一個朋友的地址

遠方朋友搬走不急著全面更新通訊錄,等下次寄信撲空時再改用同一頁的另一個朋友,省下持續維護的成本。

本節字彙

self-organizing(自我組織)
系統能在節點進出時自動重整結構、無需中央協調的性質;Pastry 加入與修復都只需 O(log N) 訊息。
🧠 自己整理自己,不用人管。
heartbeat(心跳訊息)
節點定期發送的『我還活著』訊號;太久沒收到就判定鄰居失效並啟動修復。
🧠 像把脈,沒了心跳就知道出事。
locality metric(在地性度量)
衡量真實網路距離的指標(如 IP 跳數或延遲),用來挑選真實網路上近的鄰居以降低傳輸延遲。
🧠 量『真的有多近』來選鄰居。
一個新節點 X 要加入 Pastry。它聯絡附近節點 A 後,join 訊息被路由到 GUID 最接近 X 的現有節點 Z。為什麼 Z 的 leaf set 對 X 特別有用?
Pastry 對 routing table 的修復採取『發現時才修』的策略。這樣做的好處是什麼?
Pastry 用 proximity neighbour selection 配合 locality metric 來建構 routing table。模擬顯示這帶來什麼效果?

Tapestry 與 DOLR

Tapestry 如何用 DOLR 介面隱藏 DHT、用 publish 建立位置對應、把副本放在使用者附近的彈性。

STEP 1

深度探秘

同樣 prefix routing,但換了一種介面

Tapestry 與 Pastry 的異同

Tapestry 也實作 DHT、也用 prefix routing 路由訊息,這點和 Pastry 一樣。但它對外露出的是 DOLR 介面(見上一章的 publish / sendToObj),把底層的 DHT 藏起來。

關鍵差異在『誰決定物件放哪』:

在 Tapestry,持有資源的節點用 publish(GUID) 把資源宣告給系統,而資源仍由持有者自己保管。

如果同一份資源被複製到多處,每個持有副本的節點都用相同的 GUID 去 publish,於是 Tapestry 的路由結構裡會有多筆指向不同節點的對應

Tapestry 用 160 位元的識別碼

  • NodeId:指向執行路由動作的電腦。
  • GUID:指向物件。
  • 對任何 GUID 為 G 的資源,存在一個唯一的 root node(根節點),其 GUID(記為 R_G)在數值上最接近 G。
💡
關鍵

Tapestry 與 Pastry 同樣用 prefix routing,但露出 DOLR 介面:持有者用 publish 宣告資源並自行保管,每份副本用相同 GUID 登記,每個 GUID 有唯一的 root node。

STEP 2

生活妙喻

圖書館的『館藏登記處』

自己保管書,去登記處掛號

把 Tapestry 想成一套圖書館館藏登記系統,但每本書是放在各個讀者自己家裡的:

  • 你手上有一本《Phil 的書》(GUID = 4378),你不必把書交出去,只要去登記處掛號:『這本書(4378)在我這裡』——這就是 publish(GUID)
  • 每本書都有一個固定的主管登記員(root node),就是號碼跟這本書最接近的那位(例如 4377 負責管 4378)。你的掛號訊息會沿路被送到這位主管登記員那裡。
  • 沿途經過的登記員都會順手記下『4378 在某某讀者家』這個對應(cache),主管登記員當然也記下。
  • 如果有別人也有同一本書的副本,他也用同樣的書號去掛號,於是系統裡就有好幾筆『4378 在不同人家』的記錄。

之後有人想借 4378,系統就沿著這些掛號記錄,把他導向離他最近的那位持有者(這些記錄會依網路距離排序)。

這跟 DHT(把書直接交給置物櫃中心、由系統決定放哪格)不同:Tapestry 讓你自己保管書,只去登記它在哪。

💡
關鍵

Tapestry 像圖書館登記處:你自己保管書,只用 publish 去掛號『書在我這』,掛號訊息送往號碼最接近的 root node,沿途節點快取對應,借書時導向最近的持有者。

STEP 3

實用超能力

DOLR 的彈性:把副本放在使用者身邊

Tapestry 給應用的彈性

DOLR 介面讓物件可以放在任何地方、由應用決定,這帶來一個實用的超能力:

應用可以把副本放在(網路距離上)常存取它的使用者附近,藉此降低延遲、減少網路負載,或確保對網路與主機故障的容忍。

flowchart TD
  A[持有副本的節點] -->|publish 相同 GUID| B[沿路送往 root node]
  B --> C[沿途節點快取 GUID 到持有者 IP 的對應]
  C --> D[多筆對應依網路距離排序]
  D --> E[借取時導向最近可用的副本]

不過課本提醒:Pastry 與 Tapestry 的這個差別並非根本性的

  • Pastry 應用也能達成類似彈性:只要讓 GUID 對應的物件當作『代理(proxy)』,指向更複雜的應用層物件即可。
  • 反過來,Tapestry 也能用它的 DOLR API 來實作一個 DHT。

換句話說,DHT 與 DOLR 比較像是同一套底層能力的兩種包裝風格,而非兩種完全不同的技術。

實務上,Tapestry 收到對同一 GUID 的多筆 (G, IP) 對應時,會依到該 IP 的網路距離(往返時間)排序,所以對被複製的物件而言,後續訊息會自然被送往最近可用的副本。Tapestry 正是後面 OceanStore 檔案儲存系統的底層基礎。

💡
關鍵

DOLR 讓應用把副本放在常用者附近以降延遲與容錯;但 DHT 與 DOLR 差別非根本,彼此可互相實作,Tapestry 會依網路距離把請求導向最近副本。

🔆
生活妙喻:publish 登記而非交出物件 ≈ 去圖書館登記處掛號『書在我這』

你自己保管書,只去登記處掛號它在你手上,掛號訊息送往號碼最接近的主管登記員,沿途登記員順手記下對應,正如 publish 建立 GUID 到持有節點的對應。

🔆
生活妙喻:把副本放在使用者附近 ≈ 在常借書的社區設置在地分館

把熱門書的副本放在常借閱的社區附近,借閱者就近取得、減少跑遠路,正如 DOLR 讓應用把副本放在常存取者附近以降延遲與負載。

本節字彙

DOLR(分散式物件定位與路由)
一種 routing overlay 介面,物件放置由應用決定,用 publish 登記、由系統維護 GUID 到節點位址的對應並導向最近副本。
🧠 自己保管、只去『登記與導航』。
root node(根節點)
對某 GUID 為 G 的資源,GUID 數值上最接近 G 的唯一節點,負責維護該物件的位置對應。
🧠 每本書的『主管登記員』,號碼跟它最像。
publish(發布)
Tapestry 中持有者用來宣告『我持有某 GUID 物件』的操作,沿路在節點快取位置對應。
🧠 把『我有這個』公告出去並登記。
Tapestry 與 Pastry 在路由機制上有何共通,又在介面上有何不同?
在 Tapestry,同一份資源的多個副本被不同節點持有時,它們會如何讓系統知道?
DOLR 介面給 Tapestry 應用帶來的一個實用彈性是什麼?

非結構化 P2P 與 Gnutella

結構化與非結構化的優缺點比較、洪泛搜尋的問題,以及 expanded ring search、random walk、gossip 與 Gnutella 的 ultrapeer 與 QRP。

STEP 1

深度探秘

有規劃 vs 隨遇而安的兩種 P2P

結構化 vs 非結構化

前面的 Pastry、Tapestry 都是結構化(structured 的:

結構化系統有一套全域政策管理網路拓樸、物件放置與搜尋/路由函數,底層有一個明確的分散式資料結構(如 DHT 與環狀結構)和對應的演算法。

好處是保證能找到物件、且有時間與複雜度的上限、訊息開銷相對低;代價是得維護這些複雜結構,在高度動態的環境下既困難又昂貴。

相對地,非結構化(unstructured) 系統:

對拓樸與物件放置沒有全域控制。overlay 是臨時拼湊出來的——每個加入的節點只遵循一些簡單的本地規則去建立連線(連上一組鄰居,鄰居又連著別的鄰居……)。

好處是自我組織、天生抗節點故障;代價是要找物件只能搜尋整個網路,無法保證找得到、效能不可預測,還可能產生過量訊息流量。

有趣的是,儘管有這些缺點,非結構化反而是網際網路上的主流——Gnutella、FreeNet、BitTorrent 都採非結構化。一份 2008/9 的研究指出 P2P 檔案分享佔了全網際網路 43%–70% 的流量。

💡
關鍵

結構化 P2P 有全域政策、保證找到且有時間界限但維護成本高;非結構化無全域控制、自我組織抗故障但搜尋是機率性、易產生過量流量,卻是網際網路主流。

STEP 2

生活妙喻

圖書館 vs 在市集裡問人

找書的兩種方式

結構化=有編目的圖書館

圖書館每本書都按一套全域規則(杜威分類法)擺放,你查目錄就能保證找到,而且很快。代價是:得有人持續維護這套編目系統,書一進一出都要重新上架歸位,工程浩大。

非結構化=在熱鬧市集裡問人

市集裡沒有目錄,你想找某樣東西,只能逢人就問:『你有嗎?沒有的話,幫我問問你認識的人?』每個攤販只認得身邊幾個攤販,問題就這樣一傳十、十傳百。

  • 好處:完全不用維護任何目錄,攤販來來去去都沒差,市集自己就運轉起來(自我組織、抗故障)。
  • 壞處:你不保證問得到(也許東西就在某個沒被問到的角落),而且如果每個人都對所有鄰居大喊大叫,市集很快就會被吵到塞爆

這個『逢人就問、一傳十十傳百』的笨方法,就叫 flooding(洪泛),正是早期 Gnutella 0.4 的做法——簡單,但完全不可擴展。

💡
關鍵

結構化像有編目的圖書館:保證找到但要維護編目;非結構化像在市集逢人就問:自我組織抗故障但不保證找到,naive 的 flooding 會吵到塞爆網路。

STEP 3

實用超能力

聰明搜尋與 Gnutella 0.6 的進化

讓非結構化搜尋變聰明

為了改善 flooding 的問題,發展出幾種搜尋策略:

策略 做法
Expanded ring search(擴展環搜尋) 從小的 TTL(time-to-live)開始一輪輪擴大,因為很多請求在近處就能滿足
Random walks(隨機漫步) 放出幾個『漫步者』,各自在網路圖上走自己的隨機路徑
Gossiping(八卦/流行病協定) 節點以某機率把請求傳給鄰居,像病毒在人群中擴散

這些策略搭配內容複製(整檔複製、或把檔案碎片散布各處,如 BitTorrent),能大幅降低搜尋開銷、提升可擴展性。

Gnutella 0.6:兩大關鍵改良

flowchart TD
  A[Leaf node 葉節點] -->|連上少數| B[Ultrapeer 超級節點]
  B -->|彼此重度連接 32 以上連線| C[Ultrapeer 超級節點]
  A2[Leaf node] -->|送出 QRT 檔案雜湊| B
  B -->|合併 QRT 只往有結果的路徑轉送查詢| C
  1. Ultrapeer 階層(hybrid 架構):不再人人平等,把資源較多的節點選為 ultrapeer(超級節點),彼此重度連接(各 32 條以上連線);一般節點當 leaf(葉節點),只連上少數 ultrapeer。這大幅減少了徹底搜尋所需的最大跳數。Skype 也採這種混合架構。
  2. Query Routing Protocol(QRP,查詢路由協定):節點把自己檔名雜湊出的數字組成 Query Routing Table(QRT) 送給 ultrapeer;ultrapeer 合併所有葉節點與自己的 QRT 再彼此交換。這樣 ultrapeer 就能只往『可能有結果』的路徑轉送查詢,大幅減少無謂流量。

此外,查詢中帶有發起 ultrapeer 的網路位址,所以一旦找到檔案,可直接用 UDP 回傳,不必沿原路繞回去。

💡
關鍵

非結構化用 expanded ring search、random walk、gossip 配合複製來省搜尋成本;Gnutella 0.6 靠 ultrapeer 階層減少跳數、靠 QRP 只往可能有結果的路徑轉送查詢。

🔆
生活妙喻:非結構化搜尋(flooding) ≈ 在市集裡逢人就問、一傳十十傳百

市集沒有目錄,找東西只能逢人就問、請對方再問鄰居,naive 做法人人對所有鄰居大喊大叫,很快吵到塞爆,正如 Gnutella 0.4 的 flooding 不可擴展。

🔆
生活妙喻:ultrapeer 階層 ≈ 市集裡消息靈通的大盤商

把消息靈通、人脈廣的大盤商當樞紐,小攤販只要問少數幾個大盤商,大盤商彼此又熟識,就能快速問到,正如 ultrapeer 重度連接而 leaf 只連少數 ultrapeer。

本節字彙

structured / unstructured(結構化/非結構化)
結構化有全域政策與資料結構、保證找到;非結構化臨時拼湊、靠搜尋找物件、不保證找到但抗故障。
🧠 有編目的圖書館 vs 逢人就問的市集。
flooding(洪泛)
把搜尋請求轉給所有鄰居、再層層轉發的 naive 搜尋法,簡單但易塞爆網路、不可擴展。
🧠 像水淹一樣四處擴散,很快淹沒網路。
ultrapeer(超級節點)
Gnutella 0.6 中資源較多、被選為樞紐並重度連接的節點,一般 leaf 節點只連上少數 ultrapeer。
🧠 ultra=超級,人脈最廣的樞紐。
結構化 P2P(如 Pastry)相較於非結構化 P2P,最主要的優勢與代價分別是什麼?
為什麼非結構化 P2P 系統被形容為『自我組織且天生抗節點故障』?
早期 Gnutella 0.4 採用的 naive flooding 搜尋有什麼主要問題?
05

應用案例:Squirrel、OceanStore、Ivy

網頁快取的基本運作與新鮮度檢查、Squirrel 如何用每台桌機的零碎資源取代專用快取伺服器,以及它的評估結果。

先讀原文段落,旁邊就是白話

這是一本英文書。左邊放原文、右邊放白話導讀——你既讀得懂,也順手碰了原文。

原文 · 應用案例:Squirrel、OceanStore、Ivy SQUIRREL, OCEANSTORE, IVY 449 10. 6 Application case studies: Squirrel, OceanStore, Ivy The routing overlay layers described in the preceding section have been exploited in several application experiments and the resulting applications have been extensively evaluated. We have chosen three of them for further study, the Squirrel web caching service based on Pastry, and the OceanStore and Ivy file stores. 1 Squirrel web cache The authors of Pastry have developed the Squirrel peer-to-peer web caching service for use in local networks of personal computers [Iyer et al.
白話導讀

網頁快取的基本運作與新鮮度檢查、Squirrel 如何用每台桌機的零碎資源取代專用快取伺服器,以及它的評估結果。

Squirrel 網頁快取

網頁快取的基本運作與新鮮度檢查、Squirrel 如何用每台桌機的零碎資源取代專用快取伺服器,以及它的評估結果。

STEP 1

深度探秘

先搞懂網頁快取在做什麼

網頁快取的基本原理

瀏覽器發出 HTTP GET 請求要網頁、圖片等物件時,這些請求可能由三個地方滿足:瀏覽器本機快取、代理快取(proxy cache)、或來源伺服器(origin server)——看誰手上有『夠新鮮』的副本。

收到 GET 請求時有三種情況:物件不可快取快取未命中(miss)、或在快取中找到。前兩種會往來源伺服器方向轉發;找到時,則要檢查這份快取夠不夠新鮮

物件存放時附帶一些 metadata:

  • 最後修改時間戳 T、可選的存活時間 t(time-to-live)、或 eTag(內容算出的雜湊)。
  • T + t 晚於現在時間,就視為新鮮,直接回傳給客戶端,不必聯絡來源伺服器。
  • 否則發出 conditional GET(cGET) 去驗證:可能是 If-Modified-Since(帶上次修改時間戳)或 If-None-Match(帶 eTag)。來源伺服器回傳完整物件,或一個『未修改』訊息。

傳統做法是用一台專用伺服器或叢集跑代理快取,需要可觀的專用運算資源。

💡
關鍵

網頁快取讓請求由本機、代理快取或來源伺服器中持有新鮮副本者滿足;用時間戳、TTL 或 eTag 判斷新鮮度,過期則用 conditional GET 向上驗證。

STEP 2

生活妙喻

辦公室的『共用冰箱便當』

不用專人採買,大家湊出一個共用快取

傳統代理快取像辦公室請了一位專職採買:他有一台大冰箱(專用伺服器),所有人要吃的都先問他有沒有現貨。好用,但要養這個人和那台大冰箱(專用運算資源)。

Squirrel 的想法是:不必請專人,讓每位同事各自分出冰箱一格湊成一個共用快取。

  • 每個網頁物件用它的 URL 算 SHA-1 雜湊得到 128 位元的 Pastry GUID。
  • GUID 數值最接近該物件的那位同事,就成為這個物件的 home node(家節點),負責保管它的快取副本。
  • 你要某個網頁時,本機沒有新鮮副本,就透過 Pastry 把請求路由到那位 home node 同事。

有趣的是:因為這個 GUID 不是用來驗證內容的(只是用來決定誰負責),所以它不需要根據整個網頁內容來算,光用 URL 算就好。Squirrel 作者用 end-to-end 論證辯護這點:網頁從來源到客戶端途中本來就可能在很多點被破壞,快取頁的認證幫助不大;真要安全,該用 HTTPS(端到端 TLS)。

💡
關鍵

Squirrel 不請專職採買,而是每位同事各分一格冰箱湊成共用快取;用 URL 雜湊出 GUID,數值最接近者當 home node 保管該物件副本。

STEP 3

實用超能力

它真的夠用嗎?看評估數據

Squirrel 的運作與評估

每個客戶端都跑一個本地 Squirrel proxy 程序:本機沒有新鮮副本時,就把 Get 或 cGet 透過 Pastry 送到 home node。

flowchart TD
  A[客戶端要網頁] --> B{本機快取有新鮮副本}
  B -- 有 --> C[直接用]
  B -- 沒有 --> D[經 Pastry 送請求到 home node]
  D --> E{home node 有新鮮副本}
  E -- 有 --> F[直接回客戶端 未修改或新副本]
  E -- 沒有 --> G[home node 向來源伺服器發 Get 或 cGet]
  G --> H[取得後回傳客戶端 並視情況快取]

Squirrel 用微軟兩個真實環境的流量軌跡模擬評估(劍橋 105 台、雷德蒙德 3.6 萬台以上),和集中式快取比較三個面向:

面向 結果
降低外部頻寬(與命中率相關) 集中式命中率 29%(雷德蒙德)/ 38%(劍橋);Squirrel 為 28% / 37%,幾乎相同
使用者感受到的延遲 Squirrel 多了路由跳數(雷德蒙德平均 4.11 跳、劍橋 1.8 跳),但區網傳輸只要幾毫秒,相較跨網際網路 10–100ms 的未命中存取,使用體驗相近
加在客戶端的負載 每節點平均每分鐘只替別人服務 0.31 次請求,負載極低

結論:Squirrel 效能可媲美集中式快取,而加在每台桌機上的額外負載低到使用者幾乎感覺不到。後來它被部署為 52 台機器區網的主要快取,結果證實了這些結論。

💡
關鍵

Squirrel 用桌機零碎資源達成與集中式相近的命中率,雖多了路由跳數但區網延遲極小、體驗相近,且每節點額外負載極低,被證實實際可用。

🔆
生活妙喻:用桌機零碎資源取代專用快取 ≈ 辦公室共用冰箱便當 vs 請專職採買

與其請專人與大冰箱(專用伺服器),不如每位同事各分一格冰箱湊成共用快取,正如 Squirrel 用每台桌機的零碎儲存與運算取代專用快取伺服器。

🔆
生活妙喻:home node 由 GUID 數值最接近者擔任 ≈ 誰的座號跟物品編號最近誰就負責保管

用 URL 雜湊出物件編號,座號最接近的同事負責保管該物件副本,正如 GUID 數值最接近的節點成為 home node。

本節字彙

proxy cache(代理快取)
在客戶端與來源伺服器之間、替多個客戶端暫存近期取回網頁物件的服務,以加速並省頻寬。
🧠 替你『代為快取』網頁的中間人。
home node(家節點)
Squirrel 中 GUID 數值最接近某物件的節點,負責保管該物件的快取副本。
🧠 那個物件的『家』,由座號最近的人當。
conditional GET / cGET(條件式取得)
帶上時間戳或 eTag 去驗證快取是否仍新鮮的請求;伺服器回傳完整物件或『未修改』。
🧠 有條件地問:『有改過嗎?沒改就別傳了。』
Squirrel 與傳統代理快取最根本的差異是什麼?
在 Squirrel 中,某個網頁物件的 home node 是怎麼決定的?
為什麼 Squirrel 的物件 GUID 可以只用 URL 來計算,而不必根據整個網頁內容計算?

OceanStore 可變檔案儲存

OceanStore 如何支援可變資料:版本鏈與 copy-on-write、AGUID/VGUID/BGUID 三種識別碼、inner ring 的 Byzantine 共識與 erasure coding。

STEP 1

深度探秘

用『一連串不可變版本』來支援可變資料

OceanStore 想解決的難題

還記得 P2P 雜湊命名最適合不可變資料嗎?OceanStore(建在 Tapestry 之上)偏偏要支援可變(mutable)檔案——要在不斷變動的網路與運算資源中,提供超大規模、可持久、可靠的儲存。

它的聰明解法是:

每個物件被表示成一串有序的、不可變的版本(versions),原則上永久保存。任何更新都會產生一個新版本

而且各版本之間用 copy-on-write(寫時複製) 共享未變動的區塊:

兩個版本只有小差異時,只需要少量額外儲存——沒改到的資料塊大家共用。

物件的結構很像 Unix 檔案系統:資料塊透過一個叫 root block(根區塊) 的 metadata 區塊來組織與存取,必要時還有 indirection block(間接區塊),就像 Unix 的 i-node。再上一層,用一個持久的、外部可見的名稱(如檔案路徑)來對應到這一串版本。

💡
關鍵

OceanStore 用『一串不可變版本』來支援可變檔案:每次更新產生新版本,版本間以 copy-on-write 共享未變動的區塊,只需少量額外儲存。

STEP 2

生活妙喻

Google 文件的版本歷史

像文件的『版本歷史』

你用過協作文件的『版本歷史』功能嗎?OceanStore 的精神幾乎一模一樣:

  • 從不真的『覆蓋』舊內容。每次存檔都產生一個新版本,舊版本永久留著。
  • 而且系統很省空間:新版本只記錄『這次改了哪幾頁』,沒改的頁面直接沿用舊版本的——這就是 copy-on-write。

再來是命名的巧思。因為內容會變,不能用內容雜湊當物件的長期名字(內容一變雜湊就變,所有指向它的索引就壞了)。所以 OceanStore 用三種識別碼:

識別碼 像什麼 說明
BGUID(block GUID) 某一頁的指紋 資料塊的安全雜湊(不可變)
VGUID(version GUID) 某一個『存檔版本』的編號 該版本 root block 的 BGUID
AGUID(active GUID) 這份文件本身的永久代號 唯一識別『整串版本』,不由內容算出

關鍵:AGUID 是這份文件的永久身分證,不管你改幾次都不變;而每次存檔產生新的 VGUID。AGUID 由『應用層名稱(如檔名)+擁有者公鑰』雜湊產生,永遠固定。

💡
關鍵

OceanStore 像文件的版本歷史:永不覆蓋、每次存檔留新版本、copy-on-write 省空間;AGUID 是文件的永久身分(不由內容算出),VGUID 是各版本編號,BGUID 是資料塊指紋。

STEP 3

實用超能力

inner ring 的 Byzantine 共識與 erasure coding

在不可信的世界裡保證正確與可用

問題來了:AGUID 指向『目前是哪個版本』這件事會變,存在不可信的陌生人電腦上,怎麼確保它沒被亂改?

OceanStore 的答案是:

AGUID 與版本序列的對應,記在一張簽署過的憑證(certificate)裡。憑證含目前版本的 VGUID,而每個版本的 root block 又含前一版本的 VGUID,形成一條可往回追溯的版本鏈

更新憑證需要一小群主機——稱為 inner ring(內環)——達成共識:

flowchart TD
  A[要更新物件 產生新版本] --> B[inner ring 用 Byzantine 共識協定]
  B --> C[即使部分成員失效或惡意 仍能正確簽署新憑證]
  C --> D[新憑證取代 primary copy 並散布到較多 secondary copy]
  • inner ring 用 Byzantine agreement(拜占庭共識) 協定來更新並簽署憑證,即使部分成員失效或惡意也能維持正確。
  • 但 Byzantine 共識的成本隨主機數平方成長,所以 inner ring 刻意保持很小,再用 primary copy(主動複製)把憑證散布到更多 secondary copy。

資料塊用 erasure coding 省空間又容錯

唯讀的資料塊則用更省儲存的方式複製:把每個區塊切成 m 個碎片,用 erasure code(抹除碼) 編成 n 個碎片(n > m)。

神奇特性:只要拿到任意 m 個碎片就能重建整個區塊。所以系統可容忍多達 n − m 台主機遺失而資料仍在。Pond 實作用 m=16、n=32,儲存成本加倍,就能容忍多達 16 台主機故障而不丟資料。

💡
關鍵

OceanStore 用 inner ring 的 Byzantine 共識簽署版本憑證,即使部分成員惡意也正確;inner ring 保持小以控成本;資料塊用 erasure coding,任 m 個碎片即可重建,容忍 n−m 台故障。

🔆
生活妙喻:版本鏈與 copy-on-write ≈ 協作文件的版本歷史

永不覆蓋舊內容、每次存檔留新版本、只記錄改動的頁面而沿用未改頁面,正如 OceanStore 的不可變版本序列與 copy-on-write 共享未變區塊。

🔆
生活妙喻:erasure coding ≈ 把鑰匙拆成多片,湊齊任意幾片就能開鎖

把區塊切成 n 個碎片,只要拿到任意 m 個就能重建整塊,因此丟掉 n−m 片仍能還原,正如 erasure code 用適度冗餘換得高容錯。

本節字彙

copy-on-write(寫時複製)
更新時只複製有變動的部分、未變動部分由新舊版本共享,省下大量儲存。
🧠 只在『要寫』時才複製那一塊。
AGUID(active GUID)
OceanStore 中唯一識別一個物件『整串版本』的永久識別碼,因物件可變故不由內容算出。
🧠 文件的『永久身分證』,改幾次都不變。
Byzantine agreement(拜占庭共識)
即使部分參與者失效或惡意說謊,仍能讓誠實節點達成一致決定的協定;inner ring 用它簽署憑證。
🧠 就算有人搗亂說謊,大家仍能達成共識。
P2P 雜湊命名最適合不可變資料,但 OceanStore 要支援可變檔案。它用什麼巧妙設計來調和這個矛盾?
為什麼 OceanStore 用來識別整個物件的 AGUID『不能』由物件內容計算出來?
OceanStore 中,BGUID、VGUID、AGUID 三者的關係,下列何者正確?

Ivy 檔案系統

Ivy 如何用每位參與者一份的更新日誌重建檔案、用 view 處理部分信任與惡意參與者,以及 snapshot 與 close-to-open 一致性。

STEP 1

深度探秘

不存『檔案』,只存『更新日誌』

Ivy 的核心點子

Ivy 和 OceanStore 一樣,是建在 overlay 路由與分散式雜湊儲存之上的讀寫檔案系統,支援多讀者多寫者。但 Ivy 模擬的是 Sun NFS 伺服器。它最特別的設計是:

Ivy 不直接儲存檔案狀態,而是把檔案存成一份份更新日誌(logs)——記錄客戶端發出的每一次檔案更新請求。需要讀檔時,就掃描這些日誌來重建檔案

日誌記錄存在 DHash 這個分散式雜湊儲存服務裡(它的介面就像前面的 put/get DHT)。

Ivy 的結構:

  • 一個 Ivy 檔案系統由一組更新日誌組成,每位參與者各一份
  • 每位參與者只能往自己的日誌追加(append-only),但可以讀取所有人的日誌
  • 一份 Ivy 日誌是一個反向時間排序的鏈結串列,每筆是帶時間戳的更新記錄。DHash 用記錄內容的 SHA-1 雜湊當鍵來存取,並把記錄依時間戳串接。
  • 每位參與者還維護一個可變的 log-head,指向自己最新的日誌記錄。
💡
關鍵

Ivy 不存檔案本身,而是把每位參與者各一份的 append-only 更新日誌存進 DHash,讀檔時掃描日誌重建;每人只追加自己的日誌但可讀所有人的。

STEP 2

生活妙喻

每個人各記一本帳本,對帳算出總帳

大家各記一本流水帳

把 Ivy 想成幾個合夥人共管一筆帳,但沒有一本統一的總帳,而是每個人各記自己的一本流水帳(log)

  • 你只能在自己的帳本上往後加新記錄(append-only),不能改別人的,也不能改自己寫過的。
  • 但你可以翻閱所有人的帳本
  • 想知道『現在帳目總狀態』(讀檔案),就把大家的帳本攤開,按時間順序對帳,算出結果。

問題是:每個人各記各的、沒有全域時鐘,怎麼知道哪筆在前哪筆在後?Ivy 用 version vectors(版本向量/向量時間戳) 來替多本日誌的記錄排出一個全序

而且每本帳本有個『最新頁書籤』(log-head):

  • log-head 是個可變區塊,由擁有者用公私鑰對簽章。內容用私鑰簽、用對應公鑰驗證。
  • 任何拿到某人公鑰的參與者,都能取得他的 log-head,順著它讀到那本帳的所有記錄。

簡單記:Ivy 不維護一本會被搶著改的總帳(那容易出事),而是讓大家各記不可竄改的流水帳,要用時再對帳合併。

💡
關鍵

Ivy 像每位合夥人各記一本只能追加的流水帳、可互相翻閱,用向量時間戳對帳排序合併;每本帳有公私鑰簽章的 log-head 書籤指向最新記錄。

STEP 3

實用超能力

view、snapshot 與部分信任

Ivy 如何處理『部分信任』與惡意參與者

Ivy 最大的貢獻,是在『主機只能部分信任、可能被攻擊』的環境下管理安全與完整性。它的關鍵武器是 view(視圖)

view 是由一組參與者的更新日誌所建構出來的檔案系統狀態表示。可以移除某些參與者,再重算一個不含他們更新的 view

flowchart TD
  A[偵測到某參與者惡意 例如惡意刪檔] --> B[把他從 view 中移除]
  B --> C[不再用他的日誌計算檔案系統內容]
  C --> D[他刪掉的檔案在新 view 中重新出現]

因為每個人的更新存在各自獨立的日誌裡,所以能整本回滾:一旦發現安全漏洞或一致性問題,就把那位惡意參與者踢出 view,他的破壞自然消失。

snapshot:讓掃描不會慢死

每次讀檔都從頭掃描所有日誌會非常慢。Ivy 用本地快取與 snapshot(快照) 加速:snapshot 是各參與者在使用日誌時順手算出、存在本地的檔案系統表示。它是『軟性的(soft)』——若某參與者被踢出 view,snapshot 可能就失效要重算。

一致性與衝突

  • Ivy 採 close-to-open 一致性:對檔案的更新要等到檔案關閉後才對其他行程可見,這樣可把一檔的多次寫入累積成單一日誌記錄。
  • 因為各 Ivy 伺服器自主寫各自的日誌、不互相協調,序列化要在讀取合併時才做。多數更新可用向量時間戳排序,但仍可能有衝突更新,需用類似 Coda 的應用層方法(自動或人工)解決。
💡
關鍵

Ivy 用 view 處理部分信任:可移除惡意參與者並重算狀態讓其破壞消失;用 snapshot 與本地快取加速掃描、採 close-to-open 一致性,衝突更新用類 Coda 方法解決。

🔆
生活妙喻:用更新日誌重建檔案 ≈ 每位合夥人各記一本流水帳、對帳算總帳

不維護一本會被搶改的總帳,而是各記只能追加的流水帳、可互相翻閱,要用時按時間對帳合併,正如 Ivy 用各參與者的 append-only 日誌掃描重建檔案。

🔆
生活妙喻:view 與移除惡意參與者 ≈ 把作假帳的人剔除後重新對帳

發現某合夥人作假帳,就把他那本帳排除、用剩下的帳重新對帳,他造成的破壞便消失,正如 Ivy 從 view 移除惡意參與者並重算狀態。

本節字彙

log(更新日誌)
Ivy 中每位參與者各一份、只能追加的更新記錄串列;讀檔時掃描重建檔案。
🧠 一本只進不改的『流水帳』。
view(視圖)
由一組參與者日誌建構出的檔案系統狀態;可移除參與者並重算以排除其破壞。
🧠 從『選定的一群人帳本』算出的當前畫面。
close-to-open consistency(關閉到開啟一致性)
對檔案的更新要到檔案關閉後才對其他行程可見的一致性模型。
🧠 你關檔,我下次開檔才看得到你的改動。
Ivy 檔案系統儲存檔案狀態的方式,與一般檔案系統最大的不同是什麼?
在 Ivy 中,每位參與者對日誌的存取權限是什麼?
Ivy 用『view』機制來處理部分信任的環境。當偵測到某參與者惡意刪除了他擁有的檔案時,Ivy 能怎麼補救?