項目網址: http://gigahttpd.sourceforge.net/
版本: 0.1
提交時間:2008-04-08
本版作者:魯義明 (Yiming Lu) lu.yiming.lu@gmail.com
所屬: GigaHttpd 開發文檔
* 先說實話
寫這一版設計思想的時候,我知道自己還很無知,很多想法可能都是錯的,甚至整個設計都是徹底不可行。不過沒關係,讓我們來持續改進。
* HTTP Server 功能的簡單描述
接收一個 HTTP 請求,返回請求的資料。
* 功能細化一下
(1) 建立一個 TCP 串連。
(2) 接收 HTTP 請求,GET 或 POST,驗證身份 Session,解析 URL。
(3) 從所有 Object 中找到對應 URL 的 Object。
(4) 如果這個 Object 需要計算(比如 Post Body 中解析出來資料進行運算 ),則啟動計算過程,儲存計算結果。
(5) 將這個 Object 的資料返回給使用者。
* 我們面對的挑戰
每秒處理並響應 10 億個 HTTP 請求,每個請求處理過程都要經過以上的步驟。
* 我們可用的資源
(1) 1000 台 PC。
(2) 每台 PC 上有 8 到 16 個 64 位的 CPU Core。
(3) 每台 PC 上有 8 到 16 GB 記憶體。
(4) 每台 PC 上有 8 到 16 塊 1G/10G 的乙太網路卡。
(5) 每台 PC 上有兩塊 1TB 的硬碟,通過硬體 RAID 卡類比成一塊。
(6) 足夠多的 10G 乙太網路交換器。
(7) 開源的 Linux 作業系統。
* 系統啟動並執行環境
以下條件假設已經或將會存在,即我們不在我們考慮之列。
(1) 裝置間空間夠大,電力充足 :)
(2) 支援 1G 使用者同時訪問的足夠的外網頻寬,可能是很多路寬頻接入,包括 DNS 負載平衡。
(3) 全部 IPv6。如果有 IPv4 接入,前端接 IPv6/IPv4 NAT 轉換器吧。
(4) 有足夠好足夠多的的 TCP 串連負載平衡器,能把 1G 個 TCP 串連分攤到上萬塊網卡上。
* 一個應用!
在開始設計之前,請先明白一個核心問題,我們的系統只處理一個應用!這意味著這是一個精簡到最小的不能再分解的應用。
舉例說明一:假設應用是一個大論壇,由很多子論壇,每個子論壇相對獨立。這個應用可以不用我們的系統,因為每個自論壇可以單獨的使用簡單的 HTTP Server。當然,如果因為某種原因,比如統一驗證身份 Session,或複雜的內部結算交易,使用我們的系統是合適的。
舉例說明二:假設應用是一個多人即時繪畫系統,支援 10 億人同時在一張紙上畫畫。這個應用就很難再分解,我們的系統適合於這種應用。
哦,不要笑話某些想法的瘋狂,我們就是要支援更加瘋狂的公益創意或商業創意,能夠付諸實現 :)
* TCP 串連
在網卡的驅動程式裡處理。即不進入核心的 TCP/IP 協議棧,也沒有某個進程在阻塞在 socket 的 listen() 中。
所以每太 PC 中需要有“正常”的網卡,也有專用的“資料網卡”。我們為“資料網卡”設計單獨的驅動程式。
“資料網卡”驅動程式從以太資料包中直接判斷,如果是 TCP 串連請求,直接返回 accept。
“正常”網卡的資料處理過程不做任何修改。我們可以通過“正常”網卡登入到 PC 的 Linux 上,進行管理。
* 身份認證
使用者身份認證包括首次認證,即使用者名稱/密碼檢驗;以及 HTTP Session 驗證。
我們有 1G 使用者,假設每個使用者佔用 4K 位元組(一頁),包括使用者名稱、密碼、該使用者當前所有的 Session/TCP 串連資訊、TCP 輸入資料緩衝區、TCP 輸出資訊,以及一些附加資訊,等等。總共需要 4TB 記憶體來存放所有的使用者資訊。
為了實現高效能,系統運行中所有資料均存放在記憶體,為了便於統一存取,我們把 10TB 的記憶體設計在一個連續的記憶體位址空間內,即內部資料引用都用 64 位的地址指標來表示。
每台 PC 大約有 10G 記憶體,所以 4TB 的使用者資料存放在 400 台 PC 上。1G 使用者用 32 位位元就可以定位,所以 1G 使用者資訊連續的存放在 4TB 空間內,用 44 位記憶體位址來定位。
使用者名稱/密碼檢驗時,把使用者的使用者名稱用 Hash 演算法翻譯成一個地址指標,然後去記憶體中連續的小範圍內尋找使用者,找到後,檢查使用者名稱/密碼。檢 查通過後,把該使用者的 32 位地址,再加上一個大隨機數,合并作為 Session ID。後續的 Session 檢驗就簡單了。為了便於提高效能, 可以把 SessionID 放在 URL 中,關於這個後續版本再討論。
所以,當一個 PC 的“資料網卡”收到一個使用者登入請求後,相應的 CPU Core 首先計算使用者的 32 位地址,如果這個地址不在本 PC,那就把這個請求發送給那個地址所在的 PC。
出於效能的考慮,在設計網頁時,一次提交 Form 的資料的不要超過一個乙太網路資料包的容量(約 1.4KB),這也是向用戶端發送的 TCP 視窗尺寸。接收 Upload 檔案的情況留待後續設計考慮。
所以,在每個 PC 中的 CPU Core 分成兩種,一個是“正常”的 CPU Core,其他的是“資料 CPU Core”,“資料 CPU Core” 只處理“資料網卡”的收發資料任務。“資料 CPU Core” 工作在核心級,主要是為了高速度讀寫網卡,如果進出核心的開銷能 夠忍受的話,也可以工作在使用者級。工作在核心級的“資料 CPU Core”,為了不影響正常的作業系統核心資料,可以設定單獨的記憶體映射,記憶體頁目錄頁 表,即不能訪問正常的作業系統資料。當然“資料 CPU Core” 不參與正常的作業系統任務調度。“資料 CPU Core” 有自己的調度方式。在 “正常 CPU Core” 閒置時候,也可以把它暫時設定成“資料 CPU Core”。
* 解析 URL,找到 Object
出於效能的考慮,系統中的所有 Object 都儲存成一塊靜態記憶體資料區,通過地址指標來訪問。
系統中根據使用者提交的資料計算而成的結果,也儲存成靜態記憶體資料區。比如論壇裡提交的文章;部落格裡提交的文章;部落格裡的評論;即時繪圖繪製出來的映像等。即時映像的資料區到期後可以重複使用。
靜態檔案,從硬碟上預讀入記憶體。
如果應用是大資料檔案,比如電影,則適當增加全系統的記憶體,或者按照一定的演算法做硬碟緩衝,或者限制使用者的資料流量(只要保證電影連續播放),來減少頻寬和記憶體佔用。關於這個問題留待以後設計討論。
所以系統中的 Object,可以嵌套包含 Object。
每個 Object 對應的 URL,在明確可預期是待用資料的情況下,可以用地址指標來做 URL,即使系統重新啟動,重新載入所有資料也保持地址不變。如果 Object 是動態,可以用字串或者字元竄加 ID 來表示。
所以整個系統的所有 Object,也是統一的放在一個巨大的地址空間中。另一方面,Object 的總量是有限的,1G 的使用者存取的很多內容 是重複的,往往很多人同時訪問的資料量並不會很大,比如商品股票交易資料、熱門的影視作品。而像商品股票交易資料重點需要做好儲存,不一定是大量曆史資料 的即時訪問。如果很多人同時訪問大量的資料,比如全球精細地圖系統、人類拍攝過的所有電影高清晰版全庫,則資料流量不會很大,而且是待用資料,每個使用者的 頻寬可以限制在一定範圍內,所以可以採用硬碟資料緩衝機制。
所以整個系統中,可以用 5TB 來儲存所有 Object,每個 Object 通過指標地址來訪問。所有的 Object 分布在超過 500 台 PC 上。考慮到訪問量大的 Object,應該在多個 PC 上儲存副本,以實現高效能。此處負載平衡的問題留待以後設計討論。
關於存取權限,某個使用者是否有訪問某個 Object 許可權的問題,可以設計一定格式一定數量的“授權碼”,每個需要被保護的 Object 都 給定一個或幾個“授權碼”。這些授權碼還可以組成角色,或多級(多層)角色。使用者可以被設定成多種角色,以此來實現大範圍的授權。至於 Object 對 單個使用者授權,則可以在 Object 中記錄使用者的 ID(地址指標),比如部落格內容只能由部落格作者自己來修改。
許可權的記憶體佔用設計在 1MB 到 100MB 之間。關於這個後續設計可以繼續討論。
* 計算 Object
在此特製計算量大的 Object,比如即時繪圖,或者商品交易撮合。
計算過程如果可以並行分解後合成,比如即時繪圖,那就分解到若干個 CPU Core 上進行計算,計算後發送到一個 CPU Core 上合成。如果計算過程不能並行分解,比如某單一產品交易撮合,那就在一個 CPU Core 上計算。
計算的結果儲存成待用資料。如果需要複製到多個 PC 上,則通過乙太網路廣播。如果廣播太多影響效能,可以再劃分子網。
* 將 Object 資料發給使用者
出於效能的考慮,不使用輸出緩衝,即不在全域記憶體間複製資料作為輸出緩衝。
為每個 Object 設定一個輸出 TCP 串連池,池中的每個串連資料是 TCP 串連的發送狀態。完整的 HTTP 響應資料就通過這裡發 送回去,即發送靜態 Object 的一部分資料,大小是乙太網路資料包尺寸與使用者 TCP 接受視窗尺寸的相比取小,以太資料包裡面打成IP包,包括服務 器端的公用 IP 地址,準備好後扔給路由器,發送給使用者。
這部分 TCP 串連池的記憶體是動態分配的,大小接近 1TB,分攤到每個 Object 所在的 PC。記憶體配置管理方法可以類似 Linux 核心的 Slab,整頁分配。因為 TCP 的發送時間可以設定逾時,所以可以保證這些記憶體會在一定時間內重新投入使用。
* 一個典型的 HTTP 請求/響應過程
(1) 使用者正在使用瀏覽器/聊天工具之類的用戶端,發 TCP 請求(一個 IP 包)到系統,首先到達負載平衡器。
(2) 負載平衡器將 TCP 請求發給一台 PC 的“資料網卡”,“資料網卡”的驅動程式直接回複,建立 TCP 串連。
(3) 使用者端發過來一個 HTTP POST 請求(假設不超過一個以太資料包),負載平衡器將此請求發給使用者管理 PC 的“資料網卡”。
(4) 使用者管理 PC 內的一個“資料 CPU Core”解析 Session ID,發現不是自己的使用者,於是將此請求轉寄給第二個使用者管理 PC。
(5) 第二個使用者管理 PC 檢驗使用者的 Session ID 正確後,解析 URL 得到 Object ID,然後按負載平衡演算法將此請求轉寄給某個 Object 管理 PC 的“資料網卡”。
(6) Object 管理 PC 的“資料 CPU Core” 解析使用者的 POST Body,計算並更新 Object 的待用資料。
(7) Object 管理 PC 把更新過的待用資料廣播發送其他的 Object 管理 PC。
(8) Object 管理 PC 在此 Object 的 TCP 串連池中增加一個串連記錄。
(9) Object 管理 PC 通知負載平衡器,此 TCP 串連的後續 IP 包都發到本機,最好發到本網卡。
(10) Object 管理 PC 從待用資料中取出一部分,打包成包含 IP 包的乙太網路包,IP 包的目的地址是使用者的 IP 地址,發給路由器。
(11) 使用者收到 IP 包後,發回接收確認 ACK 包,負載平衡器將此包發給 Object 管理 PC,Object 管理 PC 繼續打包並發送下一個資料包。
(12) Object 管理 PC 發送全部資料,或者連線逾時,從串連池中刪除此串連記錄。如果一個頁面中的所有串連記錄都被刪除,則回收此串連池。
(13) 一個大小為 10KB 的 Object 請求/響應能在 1 秒鐘內走完上述過程,在同時有 1G 個請求的情況下。
* 遺留問題
本版設計先不考慮下列問題,後續版本會加入。
(1) SSL/TLS
(2) 熱插拔/容錯
(3) 資料在硬碟上如何存放,以及是否使用資料庫。
(4) 內部負載平衡
(5) 內部網路資料轉寄丟失問題
(6) CPU Cache 最佳化