看板 BBSview 關於我們 聯絡資訊
以下內容轉載自 http://www.cndw.com/tech/network/2006032524569.asp 神說,要有光,於是有了光;神說,要灌水,於是有了 BBS 我們現在提到的 BBS ,通常指的都是 Telnet BBS ,用一個 term 軟件連接上,就可以看到文本的界面,比起如今花哨到無以復加的 WWW BBS 們來可謂是簡陋到了極點,然而就是這樣的 BBS,無數人每天面對它長達兩位數小時還樂在其中,恐怕 UI 設計專家們知道也要氣到吐血。 也不時有人發表預言,預言 Telnet BBS 將很快消亡而被更加富有表現力的WWW BBS 全面取代,只是年復一年,當年的預言者已經消失不見,BBS 上的用戶數目卻翻了一番又一番。。。這就是 Telnet BBS 的魅力。 Telnet BBS 系統數目眾多,但是從根源找起,大致可以分成兩大家族,Firebird BBS 和 Maple BBS,在大陸 Firebird BBS 的變種佔據了絕對優勢,在台灣地區則是 Maple BBS 的天下,由於台灣地區計算機發展歷史比較長,因此 BBS 的人氣也比大陸高,同時上站人數過萬的站點有好幾個,不過大陸畢竟有著人口優勢,近年來教育網幾大 BBS 的人數也迅速增長。 下面我們就分別介紹這兩大 BBS 家族。 首先是在大陸最為流行的 Firebird BBS ,最有名的 SMTH BBS, YTHT BBS, Firebird 2000 三大流派都是由此而來。很久很久以前,有那麼一群大學生,也可能是科研機構的研究員什麼的,他們整天在Unix 主機上面打滾,覺得要是能在主機上面做一個論壇樣的東西多好,於是他們就寫了一個命令行程序,運行這個程序,操作者可以在界面下面留言,為了讓多個人同時可以操作這個系統 ,他們把這個程序設置為系統某個用戶的 shell ,每個 telnet 上該主機的用戶,只要使用這個用戶的用戶名和密碼登陸,就可以進行交流。這就是 Internet BBS 的雛形。經過一段時間的發展,這個系統具有了相當多的交互功能,用戶不僅可以留言,還可以互相發送信件,發送信息,看到同時在線的用戶等等。 BBS 系統的開發者們為了讓更多的人能使用這個系統並完善之,將BBS 系統以開源協議發佈於網絡上面。只要擁有Unix 主機,就可以取得源代碼並安裝BBS 系統。因此BBS 系統以很快的速度發展起來。 在眾多BBS 系統中,某個叫做 Pirate BBS ,經過某些人修改後叫做 Eagle BBS 的分枝,流傳入了台灣地區,交大資訊工程系從他發展出了 Phoenix BBS,Phoenix BBS 是如今大部分中文 Telnet BBS 系統的祖先,然而它的名字卻遠不如其後輩響亮,在它的基礎上由中正資工進一步修改的 BBS 系統,被賦予了那個大陸 BBS 開發者耳熟能詳的名字--Firebird BBS。應該說, BBS 系統在傳入台灣地區時候雖然功能還比較簡陋,但是 BBS 系統的基本架構已經定型,比如多進程模型,共享內存信息交換,利用系統信號來傳遞呼叫消息,用文件存儲文章和索引等,這些設計在現在的 BBS 系統中大部分還在沿用,其中不少設計即使現在來看,也是相當標準有效的多進程 Unix 服務器設計。 Telnet BBS 是一種流行於大學和研究機構中的電子公告牌系統,和時下流行的Web BBS 系統不同,BBS 的界面採用純文本方式表現,用戶使用終端軟件連接BBS 系統,文本界面在服務器端生成並發送出來,客戶端軟件僅原樣顯示文本內容,屬於一種瘦客戶機的應用。Telnet BBS(後面除非特殊提到,否則簡稱BBS)在台灣地區和大陸的教育網地區比較流行,比較大規模的站點在線人數一般都在萬人以上。由於歷史原因,BBS 系統採用的是Unix 下相當傳統的1:1 多進程模型,每進程處理一個連接的模型,此種模型的好處是服務相對比較穩定,不會因為一個用戶出錯導致整個系統的不可用,但是也帶來耗費資源較多和進程之間通信比較困難的問題。BBS 服務器端的複雜邏輯也使得分佈式設計很難實施。因此BBS 通常是單機承擔幾乎所有負載,大陸地區較大規模的BBS 服務器上經常同時保持超過7000 進程,台灣地區的BBS 站甚至有並發20000 進程以上的紀錄。我們在維護大型bbs 站點的過程中,積累了一些優化和維護如BBS 這樣高並發進程服務器的經驗,考慮到1:1 進程模型服務仍然有很廣泛的應用,在這裡寫出和讀者共享。優化服務器是綜合性的工作,不僅需要修改代碼,還需要調整系統參數,包含有很多瑣碎的內容,根據目的來講,大致可以根據節約資源的類型分為磁盤IO 優化,內存優化,和CPU 優化等幾方面。 下面介紹的優化思路雖然應用於BBS,但是也適用於其他應用系統。 1: 磁盤IO 優化磁盤IO 優化可以說是服務器優化中最重要的一環,除了極少數的純計算性應用,幾乎所有的重載服務器最後都是卡在磁盤IO 瓶頸上面。 a) 盡量使用shm 等IPC 手段而不是文件多進程和多線程相比,最大的麻煩是不同執行環境交換信息不方便,因此很多程序員選擇了使用文件交換信息,例如最早的BBS 設計中,用戶的帳戶信息是存在於文件中的,進程從文件中讀出內容,有修改後就寫入文件。改進後的設計是將賬號信息文件完整讀入共享內存,所有修改都寫入共享內存,然後由外部進程定時往磁盤上面同步。甚至flock 這樣看起來不會造成太多IO 的同步操作都應該盡量避免,原因是flock 需要先open 文件,而open 文件需要找到i 節點,因此會佔用文件系統的inode 緩存空間,可能造成其他IO 操作的性能降低。在很多情況下面需要的只是一個跨進程的mutex,可以使用0/1 信號燈來實現。 b) 使用應用層緩存。很顯然,操作系統的緩存會受到很多因素的干擾,對於一些確定會經常訪問的內容,例如版面的最新幾片文章和最新列表,如果放入shm 中緩衝,性能會有大幅度提升。 c) 盡量減少關鍵IO 數據結構的大小 Bbs 文章列表的索引文件是由定長數據結構構成的,在這個數據結構中為了將來擴展方便,留下了很多保留域,造成了很多不必要的IO,刪除不必要的域之後,數據結構變小了一半,減少了很多IO。很多時候,擴展性和性能其實是對立的,如果很需要性能,那麼損失一定的擴展性也是不錯的選擇。 d) 避免在同一目錄下放過多文件或者使用合適的文件系統大部分文件系統對在同一目錄中的文件列表採用線性存儲,因此在一個目錄下面存在很多文件的時候,打開文件變得非常的慢,因此通常要將文件根據某種規則散列到不同的子目錄中,例如,文件 Atest 會被存放在 A/Atest ,如果文件太多,可能會需要對子目錄下面的文件再次進行散列。另一種解決文件過多影響效率的方法是使用有特殊優化的文件系統,例如Linux 下的reiserfs。在這些文件系統中,目錄中的文件列表是用平衡樹來組織的,因此同一目錄下面可以同時有數十萬個文件而不會降低太多性能。 e) 根據系統的訪問模式選擇適合的硬件配置和系統參數 Bbs 系統使用零散的文件存放文章,它的訪問模式基本是小文件隨機讀寫,而文章數據相對比較重要,因此bbs 使用strip 大小比較小的raid5 比較合適。文件系統選擇專門為小文件優化的 reiserfs,系統的預讀長度也可以調小一些,Linux 默認的長度是 256K, 有些偏大。如果是大文件連續讀寫的話,那麼raid 的strip 大小和系統的預讀長度應該放大,文件系統則盡量選擇結構簡單的文件系統例如ext2/3 等,如果數據並不是非常重要,那麼甚至可以取消raid5,代之以raid0 或者直接使用單獨的硬盤。 2: 內存使用優化 Bbs 系統使用的多進程模式相當耗費內存,在BBS 發展過程中,最早遭遇的瓶頸就是內存。減少內存的不必要浪費,可以節約出來作為系統緩存,從而間接提高更重要的IO 性能 a) 盡量避免動態初始化常量,使用const 說明將變量和常量區分開來。 Unix 系統在fork 出新進程的時候,子進程和父進程共享相同的空間,之後按照COW 機制,對修改的頁面才進行複製操作,常量如果可以預先計算出來(例如一些轉換表之類),就應該盡量避免在運行時動態初始化。另外因為只要修改一個字節,整個頁面就都會被複製,因此應該避免常量和會被修改的變量混在一起,編譯器本身會自動將不會被修改的內容放在一起,程序員需要做的事情,就是用const 通知編譯器哪些內容是不會被修改的。 b) 減小內存的峰值使用,特別是堆棧中內存很多人習慣寫程序時候在堆棧上聲明一個比較大的臨時數組,認為退出函數之後這部分內存會自動被釋放。殊不知這樣分配的內存並不會被動態被系統回收,因為系統並沒有一個明顯的標記可以得知堆棧內存是否還在使用中,特別是在多線程的環境下面,操作系統通常採用的措施是需要的時候分配頁面,但是在進程退出之前並不回收。即使是通過malloc 分配的堆內存,其頁面是否回收也視庫函數的實現而不確定。因此在無論什麼情況下,貿然分配過大的內存,都會對性能造成一定的影響。 c) 如有可能,盡量使用shm 來保證頁面一定會被多個進程共享。 3: CPU 優化這裡說的CPU 使用優化,不包括像使用hash 來代替線性查找這類最基本的算法優化,而是涉及一些和系統關係比較密切的操作。 a) 使用針對硬件優化的編譯器這應該是所有CPU 相關優化中最容易做到也是最容易看到效果的,Intel CPU 的Linux 系統上面使用 Intel C/C++ 編譯器,可以獲得很好的效果,甚至AMD 的Athlon 系列CPU 也能獲得一定程度的加速。BBS 進站時候需要初始化很多內容,計算量比較大,使用gcc 時候負載在4 左右,使用icc 編譯以後負載馬上下降到1 以下。推薦編譯時候針對特定CPU 指令集優化並且打開跨文件優化選項(-ipo) b) 使用單獨進程來初始化和維護共享內容,避免出現競爭導致邏輯錯誤嚴格講這並不能提升很多性能,只是為了減少多進程服務器上面經常出現的邏輯錯誤。在原始的BBS 設計中,共享資源的創建是由第一個訪問的進程在打開失敗時候創建的,但是重負載服務器上面有時候打開也會失敗,從而導致多次創建共享資源。 c) 序列化容易導致負載上升的行為 BBS 進程在進站時候需要進行很多的初始化工作,同樣進程退出的時候也要做很多的收尾工作,此時對CPU 或者IO 的佔用比較大,通過一個互斥鎖可以使多個進程不要同時進行這些操作,否則系統負載有可能上升到一定程度引起正反饋,導致系統徹底崩潰。 d) 盡量減少信號的使用 Unix 系統下面對於信號的實現的代價是比較大的,同時信號本身也很容易導致處理邏輯的混亂。高負載服務器應該盡量減少信號的使用。 e) 對於大範圍IO 讀取操作,使用mmap 調用使用mmap 操作比傳統的read 操作好處是減少了一次內核態到用戶態的拷貝。在大範圍IO 操作的時候具有優勢,BBS 中使用mmap 操作來在文件中搜索內容,速度最高時候提高了5 倍左右。但是需要注意的是,mmap 並不適用於有寫入的情況,因為mmap 寫盤的時候是以頁為單位進行操作,頁中只要有一個字節被改寫,就要往磁盤上面寫整個頁面的數據,無端增加了IO 量。以上是我們在維護大型BBS 站點時積累的一些經驗,供各位讀者參考。 -- 雖然 ﹀ ﹀ ﹀ ﹀ ︿ 我無法到達月亮 ﹀ ﹀ ﹀ ╱ \ 但在追月的過程中 ﹀ ﹀ ﹀ / 台 / 一定會有美好的事發生 ﹀ ﹀ / 灣 / ____▁▂▃▄▅▆▇█▇▆▅▄▃▂▁____ ﹀ ﹀ \ ╱ ︽︽︽︽︽︽︽︽︽︽︽︽︽︽︽︽︽︽︽︽︽︽︽︽︽︽︽︽︽︽︽︽︽\/︽︽︽ -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 218.168.82.105