架构师必看-架构之美第15章伸缩性架构设计
? ? ? 在設計系統架構時,一個比較有趣的問題就是確保系統在伸縮時的彈性。隨著越來越多的系統運行在網絡上或在互聯網上提供訪問,伸縮性正變得越來越重要。對于這樣的系統,如果你希望誤差的范圍在幾個數量級以內,那么容量規劃的想法顯然是荒謬的。如
果你架起一個網站,然后它火了,你可能會突然發現有幾百萬的用戶訪問你的站點。同樣容易出現的情況是,你架起了一個網站,卻發現沒有人感興趣,你投入的所有設備都閑置著,消耗著能源和管理成本,浪費錢財(這同樣是一種災難)。在網絡世界里,一個站點可以在幾分鐘內從其中一種狀態轉變成另一種狀態。
? ? ? ?只要是將系統連接到網絡上,每個人都會遇到伸縮性問題,但是“大型多人在線游戲”(MMO)和虛擬世界特別關注這一點。這些系統必須具備伸縮性,以滿足大量的用戶。Web服務器的用戶常常讀取的是靜態的內容,而且彼此之間沒有交互,但MMO中的玩家或虛擬世界中的居民則不同,他們既需要與所處的世界進行交互(這改變了世界的基本信息),也需要彼此之間的交互。這些交互行為使得這類系統基礎設施的伸縮性問題變得更復雜,因為用戶與系統的交互幾乎是獨立的(除了那些不獨立的情況),而且不會讓世界的狀態改變太多。對于一個世界里的任意兩個參與者,他們在某個時刻進行交互的幾率是非常小的。但是,幾乎所有玩家在所有時候都在與他人交互。結果是這種系統并行程度非常高,但只有少數的交互是互相依賴的。
? ? ? ?由于這些系統所培育起來的文化,MMO和虛擬世界的伸縮性問題進一步復雜化了。MMO和虛擬世界都源自于視頻游戲產品。這是一種從PC游戲和游戲機游戲傳統中成長起來的文化,在這種傳統中,程序員會假定游戲運行在一臺獨立的機器或游戲機上。在這樣的環境中,機器所有的資源都受游戲程序控制,程序的問題也限于單個用戶玩游戲的情況(實際上,缺陷或奇怪的行為常常會被認為是游戲本身邏輯的一部分)。
? ? ? ? 這些游戲和編寫、出品、擴展它們的公司,都屬于娛樂行業。編寫游戲的團隊由一個出品人領導,有劇本和背景故事。游戲的目標是刺激、打動人,最重要的一點,要好玩。可靠性很好,但不一定必需。可擴展性是游戲的特性,讓游戲在升級時能夠加入新的故事情節和主題,但可擴展性不是代碼的特性,不必讓代碼能以新的、不同的方式使用。
? ? ? ?在線游戲和虛擬世界的興起將這種文化帶入了一個新的環境,在這個環境中,需求與企業應用開發者所面對的類似。多個用戶通過網絡在服務器上交互時,由于一個玩家的意外動作而導致的服務器崩潰將影響許多其他玩家。隨著這些世界發展出自己的經濟(有些經濟與現實世界的經濟有關系),在線世界的穩定性和一致性就超出了一個游戲的要求。隨著這些世界中玩家或居民的人數達到百萬級,伸縮的能力就成為了任何架構的首要需求。
? ? ? Darkstar項目(本章后面將簡稱為Darkstar)是對這些游戲和虛擬世界創建者的需求挑戰的回答。這個項目由Sun公司實驗室的一個研究小組承擔,它將在架構的伸縮性領域不斷探索。這個項目特別有趣之處在于,它是針對MMO和虛擬世界的創建者,這些程序員與我們(作為系統設計者)所熟悉的那些程序員相比,有著非常不同的需求。得到的架構看起來似乎很眼熟,但如果你仔細查看,會發現它的不同之處,它與你的經驗有所不同。得到的架構有著屬于它自己的美麗,同時它也是一堂實踐課,說明了不同的需求如何改變你所想到的構建系統的方式。
3.2 背景
? ? ? ? 像一座建筑或一個城市的物理架構一樣,系統的架構必須適應環境,利用該架構創建的工件將存在于該環境之中。對于物理架構來說,這個環境包括工作的歷史環境、它所處位置的氣候、本地工人的技能、可以獲得的建筑材料,以及建筑的使用意圖。對于軟件架構,這個環境不僅包括使用該架構的應用程序,也包括那些要使用該架構的程序員,以及由此受到的系統約束。
? ? ? ?在創建Darkstar架構時,我們(注1)意識到的第一件事就是所有針對伸縮性而設計的架構都需要包含多臺機器。我們不清楚,就算是最大的大主機系統是否能夠滿足今天的一些在線游戲的要求(例如《魔獸世界》,據報道它有500萬注冊用戶,幾十萬的同時在線
用戶)。就算單臺機器能夠處理這種負載,我們也不能在一開始就假定游戲會取得如此成功,需要這樣的硬件投資。這在經濟上是不可行的。這種應用需要能夠從很小的系統開始,然后隨著用戶數的增長而增加處理能力,最后隨著大家對游戲興趣的衰退而降低處理能力。這與分布系統的特點相符,在分布式系統中,我們可以隨著請求增長而添加(合理的小)機器,當請求下降時移走機器。所以我們從一開始就知道,總體架構必須是一個分布式系統。
? ? ? ? 我們也知道系統利用了芯片架構的當前趨勢。MMO和虛擬世界(在較小程度上)曾經針對伸縮性利用過摩爾定律。隨著處理器的速度倍增,可以創造的世界會在復雜度、豐富程度和互動性方面倍增。沒有其他計算領域像游戲世界這樣探索過處理器速度的增長所帶來的好處。為游戲而設計的個人計算機總是將CPU速度、內存和圖形性能推向極致。游戲機更激進地將這些方面推向極致,它們包含的圖形系統遠遠超過了高端工作站上的圖形系統,整個機器完全是為了游戲玩家的特殊需要而打造的。
? ? ? ?芯片演進方面最近的變化是從不斷增加的時鐘速度轉為實現多核處理器,這已經對游戲中能做的事情產生了影響。新芯片的設計目標不是將一件事做得更快,而是同時做多件事。如果在芯片上運行的多項任務實際上可以同時執行,那么在芯片層面上引入并發執行將帶來更好的總體性能。在不改變時鐘速度的情況下,4核的芯片應該能比單核的芯片多做3倍的事情。實際上,這種增長不是呈線性的,因為系統的其他一些部分沒有以同樣的方式實現并發。但是可以通過并發來實現系統總體性能的增長,而且制造這種并發的芯片比制造增加時鐘速度的芯片要容易得多。
? ? ? ?基于這一事實,MMO和虛擬世界應該是多核芯片和分布式系統的理想候選者。在MMO或虛擬世界中發生的大多數事情就像在真實世界中發生的大多數事情一樣,與該世界中發生的其他事情是無關的。玩家繼續他們的搜索或裝飾他們的房間。他們與怪物交戰或設計衣服。即使他們與該世界中的另一個玩家或居民發生交互,也只是與該世界的很少一部分居民發生交互。這正是令人為難的并行計算任務的特點,也正是多核和多機系統應該擅長處理的那種任務。
? ? ? ?盡管這些系統中的任務的并行度讓人為難,但為這樣的系統編程的程序員卻沒有接受過分布式計算或并發編程方面的訓練,也沒有這方面的經驗。這是極為微妙的領域,即使是在這個領域接受過訓練的人和對這些技術相當有經驗的人也會感到困難。要讓大多數游戲程序員來開發高度并發的、分布式游戲服務器,就是要求他們做超出自己的專長和經驗的事。
3.2.1 首要目標
? ? ? ? ?這樣的背景為我們確立了該架構的首要目標。對伸縮性的需求表明,系統應該是分布式的、并發的,但我們需要為游戲開發者提供簡單得多的編程模型。簡而言之,目標就是游戲程序員應該把該系統視為一臺單機,運行著一個線程,所有允許部署到多線程和多計算機上的機制都應該由Darkstar項目的基礎設施來考慮。在一般的情況下,對應用程序隱藏分布式和并發是不可能的。但MMO和虛擬世界不是一般的情況。我們試圖實現的這種隱藏,其代價就是必需一種非常特別的、嚴格限制的編程模型。幸運的是,這種模型恰好非常適合游戲服務器和虛擬世界已經采用的編程方式。
? ? ? ? ?Darkstar項目要求的一般編程模型是反應式的,在這種編程模型中,游戲的服務器端寫成了事件監聽器,監聽客戶端生成的事件(客戶端就是游戲玩家使用的機器,通常是一臺PC或游戲機)。如果檢測到事件,游戲服務器就應該生成一項任務,這個任務是一個短期的計算序列,包括操作虛擬世界中的信息,并與最初生成事件的客戶端或其他一些客戶端進行通信。任務也可以由游戲服務器自己生成,要么是響應某些內部的變化,要么是周期性地根據時間來生成任務。在這種情況下,游戲服務器可以在游戲或虛擬世界中生成一些角色,這些角色是不受外部玩家控制的。
? ? ? ? 這種編程模型非常適合游戲和虛擬世界,但它也應用于一些企業級的架構中,如J2EE和Web服務。之所以需要創建一個不同于這些企業計算機制的架構,是因為MMO和虛擬世界存在的環境非常不一樣。這種環境幾乎剛好和經典企業環境相反,這意味著如果你接受過企業環境方面的訓練,你知道的所有事情在這個新世界中幾乎都是錯的。經典的企業環境可以描述為一個“瘦”客戶端連接到一個“胖”服務器(這個服務器又常常連接到一個更“胖”的數據庫服務器)。服務器將保存客戶端需要的絕大部分信息,在最理想的情況下,客戶端內存不多,沒有自己的硬盤,它是服務器的一個稱職的顯示設備,絕大多數真正的工作在服務器上完成。
3.2.2 游戲世界
? ? ? ? MMO和虛擬世界的環境始于一個非常胖的客戶端:它通常是頂級的PC、具有最強勁的CPU、很大的內存,以及本身計算能力就很強的顯卡。它也可以是一臺游戲機,專門為圖形密集的、高度交互的任務而設計。只要有可能,數據就會存放在這些客戶端,特別是那些不會改變的信息,如地理信息、材質貼圖和規則集。服務器保持盡可能的簡單,通常只保存非常抽象的世界表示和其世界中的實體的表示。而且,服務器的設計目標是盡可能少地進行計算。絕大部分的計算留給了客戶端。服務器的真正工作是保存共享的世界真實狀態,確保不同客戶端對世界的看法差異可以根據需要得到糾正。真實狀態需要由服務器來保存,因為控制客戶端的玩家很有興趣讓他們的表現變成最強,所以可能會受到誘惑,根據他們的喜好修改共享的真實(如果他們可以)。在一般情況下,如果有可能,玩家就會作弊,所以服務器必須是共享真實的最終來源。
? ? ? ? MMO和虛擬世界的數據訪問模式也和企業中看到的情況有著很大的區別。企業中的一般經驗法則是90%的數據訪問都是只讀的,大多數任務會讀取大量數據,然后再改寫少量數據。在MMO和虛擬世界的環境中,大多數任務只訪問服務器上少量的狀態數據,但在它們訪問的數據中,大約一半會被改寫。
3.2.3 延遲是敵人
? ? ? ? ?但是,這兩種環境中最大的不同要追溯到用戶所做的事情的不同。在企業環境中,目標是管理業務,如果總吞吐量得到改進,在處理中有一點延遲是可以接受的。在MMO和虛擬世界的環境中,目標是開心,而延遲是開心的敵人。所以MMO或虛擬世界的基礎設施需要圍繞著盡可能限定延遲的需求來設計,即便以吞吐量為代價也在所不惜。在線游戲和虛擬世界顯然已經找到了辦法來實現伸縮性,以應對數量巨大的用戶。目前的方法可以分成兩大類。第一類實質上是基于地理位置來實現的。游戲設計成包含一組不同的區域,每個區域運行在一臺服務器上。它可能是虛擬世界的一個島或房間,也可能是在線游戲中的一個小鎮或山谷。游戲設計試圖讓每個區域無關,限定地理區域的大小,這樣服務器不會因太多用戶進入這個區域而擁塞。在實踐中,這樣的區域常常能實現自我限制,因為當服務擁塞時,游戲就會變得響應比較慢,趣味性下降。因此,玩家就會轉向更有趣的區域,這使得以前擁塞的區域人數減少,響應時間得到改進。
? ? ? ? 將不同地理區域分配給不同服務器來實現伸縮性的方法有一個問題,即必須在游戲編寫時決定哪些區域應該放到一臺服務器上。雖然在游戲或虛擬世界中添加新的區域相當容易,但是改變已經分配給服務器的區域卻可能需要改動代碼。決定哪些區域組成一個伸縮性單位,這必須是開發工作的一部分。
? ? ? ? 第二種處理游戲或虛擬世界中擁塞區域的方法叫做分區(sharding)。一個分區是該區域的一份副本,運行在它自己的服務器上,獨立于其他的分區,它代表了游戲中相同的部分,即原來的區域。這樣,分區可能代表了某個房間或村莊的不同副本,允許成倍的玩
家進入到世界的這個部分中。分區的缺點是它們不允許處于不同分區的玩家彼此之間進行交互。隨著游戲和虛擬世界變成更多的社交體驗,而不僅僅是玩游戲,這種缺點就明顯了。玩家的目標不只是要出現在虛擬世界中,而是要和他們的朋友(真實的和虛擬的)一起進入虛擬世界。分區阻礙了這個目標的實現。
? ? ? ? 因此,Darkstar架構的另一個主要目標就是支持隨時伸縮,同時不要求游戲邏輯受到伸縮的影響。這個架構應該支持游戲動態地響應負載,而不是讓這種響應成為游戲設計工作的一部分。
3.3 架構
? ? ? ? Darkstar由一組獨立的服務構成,這些服務可以在游戲或虛擬世界的服務器端的地址空間內獲得。每個服務都定義為一個小的編程接口。盡管不是出于本意,但Darkstar項目提供的一些基本服務很像經典操作系統的服務,它們支持游戲或虛擬世界的服務器端訪問持久存儲,調度并執行任務,與游戲或虛擬世界的客戶端進行通信。
? ? ? ? 用一組相互聯系的服務來構建這個系統,顯然是開始了“分而治之”的過程,分而治之是設計所有大型計算機系統的基本方法。每種服務都可以用一個接口來描述,這讓使用該服務的程序不會受到底層實現變更的影響,同時也支持這些實現可以獨立地完成。對一個服務的實現進行變更不應該影響到另一個服務的實現,即使其他的服務會利用到這個變更的服務(假定接口和接口的語義沒有變更)。
? ? ? ? 我們采用服務分解的方法還有其他的原因。從一開始,Darkstar項目就設計成一個開放源代碼的項目,希望我們能夠放大核心團隊的工作,支持其他社區成員創建更多的服務,豐富核心的功能。維護一個開源社區在任何情況下都是復雜的,我們相信在組成架構的
服務之間擁有最大程度的隔離,將支持在不同服務實現層次之間的更高級的隔離。此外,當時并不清楚是否存在單一一組服務能夠適合所有的MMO和虛擬世界。將基礎設施設計為一組獨立的服務,使得這些服務的不同組合可以在不同的情況下使用,這由使用該基礎設施的具體項目的需求來決定。Darkstar棧中具體包含哪些服務可以由一個配置文件來設置。
3.3.1 宏觀結構
? ? ? ? 圖3-1展示了基于Darkstar項目基礎設施的游戲或虛擬世界的基本結構。一些服務器構成了游戲或虛擬世界的后端。每個服務器運行著一組選定服務的副本(稱為Darkstar棧)和游戲邏輯的副本。客戶端將連接到其中一個服務器,與服務器保存的該世界的抽象表示進行交互。
? ? ? ? 與大多數的復制策略不同,游戲邏輯的不同副本不需要處理相同的事件。每個副本可以獨立地與客戶端進行交互。在這個設計中,復制主要用于支持伸縮性,而不是確保容錯(雖然我們后面會看到,容錯也實現了)。而且,游戲邏輯本身不知道、也不需要知道在
其他機器上運行著服務器的其他副本。游戲程序員編寫的代碼就像在一臺機器上執行一樣,不同副本之間的協作由Darkstar項目的基礎設施來完成。實際上,如果游戲的容量只需要一臺服務器,基于Darkstar的游戲就能夠在一臺服務器上運行。
? ? ? ? 客戶端連接到游戲邏輯使用的通信機制是基礎設施的一部分。這些機制支持客戶端到服務器的直接通信,也支持一種“發布-訂閱”通道,任何發往通道的消息都會送達該通道的所有訂閱者。
? ? ? ? ?Darkstar棧由一組元服務來協調,這是一組網絡訪問服務,游戲或虛擬世界的程序員是不可見的。這些元服務支持棧的各個副本之間進行協作,共同運營整個游戲。例如,這些元服務將所有獨立的副本持續工作,如果某個副本失效,就會發起失效恢復。這些元服務還會跟蹤各副本的負載,在需要的時候重新分配負載,或者隨時添加新的服務器,增加總體容量。由于這些服務對于Darkstar項目的用戶來說是完全隱藏的,所以它們可以隨時改變或移除,或者添加新的服務,這都不需要修改游戲或虛擬世界的代碼。
? ? ? ? ?對于在Darkstar項目環境中創建游戲或虛擬世界的程序員來說,可見的架構就是棧中包含的一組服務。服務的全集是可以改變和配置的,但4個基本服務必須存在,它們構成
了運營環境的核心,如圖3-2所示。
3.3.2 基本服務
? ? ? ? 在這些棧層面的服務中,最基本的服務就是“數據服務”(Data Service),游戲或虛擬世界用它來保存、讀取和操作所有持久數據。這里的持久概念可能比其他系統中的持久概念更寬泛。對于在Darkstar項目環境中編寫的游戲或虛擬世界,任何存在時間超過一個任務的數據都被視為持久的,必須在“數據服務”中保存。我們曾假定在這種編程模型中任務的時間是短暫的(這也是需求),所以幾乎所有用于表示游戲或虛擬世界的服務器端的數據都需要持久。“數據服務”也將運行在不同服務器上的游戲或虛擬世界的副本聯系在一起,因為所有這些副本都共享同一個(概念上的)“數據服務”實例。所有的副本都會訪問相同的數據,所有的副本都可以根據需要讀取或改變存儲在“數據服務”中的數據。
? ? ? ? ?雖然“數據服務”看起來像是使用一個數據庫的好地方,但是存儲的需求實際上與通常條件下對標準數據庫的需求有著很大的差別。存儲的對象之間靜態的關系很少,游戲中也不需要對存儲的內容進行復雜的查詢。相反,簡單的命名策略就足夠了,包括在編程語言層面上對對象的引用。“數據服務”也必須針對延遲進行優化,而不是針對吞吐量來優化。特定服務要訪問的對象個數可能很少(我們初步的測算基于一些游戲和虛擬世界的原型,這些測算表明每個任務大約訪問一打對象),在這些訪問的對象中,大約一半會在任務執行中改變。
? ? ? ? ?第二個棧層面的服務是“任務服務”(Task Service),它用于調度或執行任務。這些任務要么是響應從客戶端收到的某個事件,要么是由游戲或虛擬世界服務器本身的內部邏輯發起的。絕大部分任務是一次性事件,是由于客戶端的某種動作產生的,它們從“數據服務”中讀取一些數據,操作這些數據,可能還進行一些通信,然后結束。任務也可能生成其他的任務,或者生成定期任務,在特定的時間執行或以特定的時間間隔執行。所有任務的執行時間必須很短,執行一項任務的最大時間是一個可配置的值,但默認值是100毫秒。
? ? ? ? 游戲或虛擬世界的程序員會看到因事件或服務器邏輯本身而生成的單個任務,但在底層,Darkstar的基礎設施正盡其所能調度最多的任務。特別地,由服務器邏輯生成的任務與響應客戶發起的事件而生成的任務是并行執行的,就像響應不同客戶端而生成的任務一樣。這樣的并發執行可能導致數據競爭。要處理這種競爭,就需要“任務服務”和“數據服務”協作。在底層,在服務器程序員不可見的地方,“任務服務”調度的每個任務都包裝在一個事務中。這個事務確保了任務中的所有操作要么全部完成,要么都不完成。此外,所有改變“數據服務”中對象的值的操作都由該服務作為中介。如果有多個任務試圖改變相同的數據對象,只有一個任務會執行,其他任務都會中止,并安排在稍后執行。執行的那個任務會運行到結束。當執行的任務結束時,其他的任務就可以執行了。雖然服務器程序員可以說明訪問的數據將被修改,但這不是必需的。如果數據對象先被讀取,然后被修改,“數據服務”會在任務提交之前檢測到這種修改。在讀取時就說明打算進行修改,這是一種優化,能夠更早地檢測到沖突,但是不事先說明修改的意圖也不會影響程序的正確性。
? ? ? ? ?將任務包裝到一個事務中意味著通信機制也必須支持事務,只有當包裝了消息發送任務的事務提交時,消息才會發出。這是通過Darkstar棧中余下兩項核心服務來完成的。
3.3.3 通信服務
第一個服務是“會話服務”(Session Service),它是客戶端和游戲或虛擬世界服務器之
間通信的中介。在登錄和認證后,客戶端與服務器之間就會建立起一個會話。服務器通
過會話監聽客戶端發出的消息,解析消息的內容,確定生成怎樣的任務來響應該消息。
客戶端通過會話接收來自服務器的響應。這些會話隱藏了客戶端和服務器的真實端點,
這一點對于Darkstar的多機伸縮性策略是很重要的。會話也負責確保維持消息的順序。
如果來自某個客戶端的前一條消息所引發的任務還沒有完成,后一條消息就不會提交。
在“會話服務”對任務進行這樣的排序之后,“任務服務”就得到了極大的簡化。“任務
服務”可以假定它在任何時候收到的任務在本質上都是并發的。對來自特定客戶端的消
息排序是Darkstar框架中唯一的消息排序保證機制,外部觀察者看到的多個客戶端之間
的消息順序,與游戲或虛擬世界內看到的順序有很大不同。
3.3.3 通信服務
? ? ? ? ?第一個服務是“會話服務”(Session Service),它是客戶端和游戲或虛擬世界服務器之間通信的中介。在登錄和認證后,客戶端與服務器之間就會建立起一個會話。服務器通過會話監聽客戶端發出的消息,解析消息的內容,確定生成怎樣的任務來響應該消息。客戶端通過會話接收來自服務器的響應。這些會話隱藏了客戶端和服務器的真實端點,這一點對于Darkstar的多機伸縮性策略是很重要的。會話也負責確保維持消息的順序。如果來自某個客戶端的前一條消息所引發的任務還沒有完成,后一條消息就不會提交。在“會話服務”對任務進行這樣的排序之后,“任務服務”就得到了極大的簡化。“任務服務”可以假定它在任何時候收到的任務在本質上都是并發的。對來自特定客戶端的消息排序是Darkstar框架中唯一的消息排序保證機制,外部觀察者看到的多個客戶端之間的消息順序,與游戲或虛擬世界內看到的順序有很大不同。
? ? ? ? ?會話和通道的復雜性有多種原因,其中之一就是它們必須遵守任務的事務語義。因此,會話連接或通道上的實際消息傳送不能夠在調用相應的send()方法時發生,它只能夠在該方法所處的任務提交時才能發生。這些通信機制為我們實現伸縮性機制的第二部分奠定了基礎。既然所有通信都必須通過Darkstar會話或通道的抽象層,而這些抽象層又不暴露客戶端或服務器通信的真實端點,那么在實體通信和通信起止端的實際位置之間就存在著一個抽象層。這意味著我們可以在Darkstar系統中將服務器通信的端點從一臺機器移到另一臺機器,同時不會改變客戶端對這次通信的感覺。從游戲或虛擬世界的視角來看,通信也是經過一個會話或通道。但是底層的基礎設施可以隨著時間的推移和負載的變化,根據負載平衡的需要,將會話或通道從一臺機器移到另一臺機器。
3.3.4 任務的可移動性
? ? ? ? ?要實現負載均衡的能力,其關鍵之處在于,對于我們要求的編程模型和必須使用的基本棧服務,響應客戶端事件或游戲內部事件的任務可以從任何一臺運行著Darkstar棧和游戲或虛擬世界副本的機器上移動到另一臺同樣的機器上。任務本身是用Java編寫的(注2),這意味著只要(物理)機器的運行時棧中包含相同的Java虛擬機,任務就能夠運行。任務讀取和操作的所有數據必須從“數據服務”獲得,“數據服務”是所有機器上的游戲或虛擬世界的實例和Darkstar棧所共享的。通信由“會話服務”或“通道”來實現中介,它們抽象了通信的真實端點,而且支持特定的會話和通道從一個服務器移動到另一個服務器上。因此,所有任務都可以運行在任意一個游戲服務器的實例上,同時不改變任務的語義。
? ? ? ?這使得Darkstar的基本伸縮機制看起來很簡單。如果有一臺機器超載了,只要將一些任務從這臺機器移到另一臺負載較小的機器就行了。如果所有的機器都超載了,就向運行著Darkstar棧和游戲/虛擬世界的集群中添加新的機器。底層的負載平衡軟件會將負載分發給新的機器。
? ? ? ? 對單臺機器的負載進行監控并在需要時重新分配負載,這是元服務的工作。這些元服務是網絡層面的服務,對于游戲或虛擬世界的程序員是不可見的,但是它們對Darkstar棧中的服務是相互可見的。例如,這些元服務會監控哪些機器正在運行(并監控是否有機器失效),哪些用戶與某臺機器有關,不同的機器當前的負載。由于元服務對于游戲或虛擬世界的程序員是不可見的,所以它們在任何時候都可以改變,不會影響到游戲邏輯的正確性。這讓我們能夠嘗試不同的策略和方法,實現系統的動態負載平衡,也讓我們能夠豐富基礎設施所需的元服務集合。
? ? ? ? ?同樣,我們使用實現多機伸縮機制來實現系統的高容錯。由于任務和通信機制所使用的數據是與具體機器無關的,所以很明顯,我們可以將任務從一臺機器移到另一臺機器上。但是如果機器失效,我們如何恢復在那臺機器上執行的任務呢?答案很簡單:任務本身
也是持久對象,保存在系統的“數據服務”中。因此,如果一臺機器失效,該機器上正在執行的所有任務都被視為中斷的事務,會重新調度在不同的機器上執行。盡管這種重新調度比在一臺機器上重新調度中斷的任務的延遲要長,但系統的正確性是不變的。系統的用戶(游戲玩家或虛擬世界的居民)頂多會注意到響應時間暫時有點延長。這樣的延遲讓人有點不舒服,但總好過現在其他游戲或虛擬世界環境中服務器崩潰所造成的影響。在那些環境中,會導致玩家掉線,還可能導致相當一部分游戲狀態的丟失。
3.4 關于架構的思考
? ? ? ? 也許所有人關于架構及其實現的第一個問題就是它的性能。雖然未經深思熟慮就對架構進行優化是諸多罪惡之源,但是我們也可能設計出一個架構,而它的實現根本不可能達到好的性能。由于Darkstar架構的一項基本選擇,這種擔心是真實的。而且由于游戲行業的特點,確定服務器基礎設施的性能是很難的。
? ? ? ? 要確定游戲或虛擬世界服務器基礎設施的性能,其難度源自一個簡單的事實:沒有針對大規模MMO或虛擬世界的性能測試標準或共同接受的例子。缺少性能測試標準并不奇怪,因為絕大多數游戲或虛擬世界的服務器組件都是針對特定的游戲或虛擬世界從頭開發的。只有少數的通用基礎設施可以作為可復用的構建塊,這些組件一般是事后從特定的游戲或虛擬世界中提取出來的,提供給其他構建類似游戲的人使用。也許是因為游戲行業還比較年輕,或是因為娛樂業中出現重要技術的偶然性,結果是沒有共同接受的性能測試標準用于測試新的基礎設施,也無法對不同的基礎設施進行比較。
? ? ? ? ?關于游戲或虛擬世界的預期計算、數據操作和通信負載也基本上沒有什么資料,所以很難創建性能測試標準程序。部分原因是已有的服務器都是定制的。每臺服務器都是為特定的游戲或虛擬世界設計的,所以考慮的是那款游戲或虛擬世界的具體負載特征。而且,這種狀況也是因為游戲業的強烈的保密性。在游戲業中,關于一款游戲開發的信息是嚴格保密的,而且發布游戲的實現方式也是嚴格保密的。同時,行業中的許多人對這些信息是不感興趣的。大量的思考和討論集中在藝術設計、故事情節或玩家交互模式上,這
些令新游戲更有趣、更好玩,而不是游戲服務器的設計方式和游戲為支持并發玩家(這個數字也是嚴格保密的)所采取的伸縮性技術。所以,獲得現有的游戲和虛擬世界的這種服務器負載信息都很困難。
? ? ? ? 根據我們的經驗,即使我們能夠找來開發者,談論他們的游戲或虛擬世界加在服務器上的負載,他們也常常會報告錯誤的信息。這不是因為他們想保持商業優勢而故意錯誤報告他們服務器的情況,而是因為他們真的自己不也了解。在游戲服務器上基本不會加入一些手段,讓他們收集有關服務器真實性能或完成事務的信息。對于這種服務器的分析一般最多是經驗性的。程序員在服務器上工作,直到它讓游戲玩起來有趣,這是一種迭代式的工作方式,而不是仔細對代碼本身進行測量。在這些系統中,更多的是手工技術活,而不是科學測定。
? ? ? ? 這并不是說,這些游戲或虛擬世界后面的服務器是一些粗制濫造的代碼,也不是說它們做得不好。實際上,許多代碼是效率杰作,展示了聰明的編程技巧,也展示了針對高要求的應用構造一次性、專門目的服務器的優勢。但是,為每個游戲或虛擬世界構建一個新服務器的習慣意味著人們不太注意積累構建這種服務器所需的知識,也沒有共同接受的機制來比較不同的基礎設施。
3.4.1 并行與延遲
? ? ? ? 缺少有關服務器可接受的性能的資料,這一點引起了Darkstar團隊的特別關注,因為我們所做的一些關鍵決定,都是圍繞著如何能夠從游戲或虛擬世界服務器中獲得好的性能。也許Darkstar架構和一般實踐之間的最大區別就在于,Darkstar架構拒絕在服務器的主內存中存放任何重要的信息。所有生存周期超過一次具體任務的數據都需要持久在“數據服務”中,這是實現Darkstar基礎設施功能的核心。這讓基礎設施能夠檢測到并發問題,反過來又讓系統能夠對程序員隱藏這些問題,同時讓服務器能夠利用多核架構。它也是實現整體伸縮性的關鍵組件,支持任務從一臺服務器移動到另一臺服務器,從而在一組機器上實現負載平衡。
? ? ? ? 在游戲和虛擬世界服務器領域,任何時候都持久保存游戲狀態是一種異端邪說,因為人們普遍很關心延遲。在編寫這種服務器時,大家的觀點是只有將所有信息都放在內存中才能讓延遲足夠小,達到要求的響應時間。可以偶爾保存狀態的快照,但對交互速度的要求表明,這種長時間的操作只能夠偶爾進行,而且要在后臺進行。所以,從表面上看,我們的架構似乎絕不可能達到足夠好的性能,從而服務于它的目標應用。雖然要求數據持久肯定是這個架構的主要不同之處,而且要求通過“數據服務”來訪問數據會在架構中引入一定的延遲,但我們相信我們所采取的方式更具有競爭力,原因有幾點。首先,我們相信能夠讓訪問內存數據和訪問“數據服務”中的數據之間的差異遠遠小于一般人們的看法。雖然在概念上每個生命周期超出一次任務的對象都需要從持久存儲中讀出,并寫入持久存儲,但實現這種存儲可以利用人們在數據庫緩存和一致性方面多年的研究成果,從而減少因這種方式而導致的數據訪問延遲。
? ? ? ? 如果我們能夠將訪問局限在一個特定服務器上的幾組特定對象,就更是如此了。如果用到一組特定對象的那些任務都運行在一臺服務器上,那么就可以利用該服務器的緩存,達到接近內存的對象讀寫速度(受到需要滿足的持久性約束的影響)。我們可以識別任務屬于哪些玩家或虛擬世界的哪些用戶。這樣,我們就可以利用基礎設施中服務所接收到的數據訪問和通信請求,來收集特定時刻游戲或虛擬世界中數據訪問模式和通信模式的信息。有了這些信息,我們相信能夠非常準確地估計哪些玩家應該與另一些玩家放在一起。因為我們可以根據需要將玩家移動到任何服務器上,所以能夠根據觀察到的運行
? ? ? ? 這聽起來非常像目前在大規模游戲和虛擬世界中為實現伸縮性而采用的地理區域分解技術。在這種技術中,服務器開發者將世界分解成一些區域,將它們指派給一些服務器,不同的區域就成為用戶分區的機制。同一區域的玩家比不同區域的玩家進行交互的可能性更大,所以這種集中在一個服務器上的優勢就體現出來了。不同之處在于,目前的地理區域分解是在游戲開發過程中進行的,被編入源代碼,放到服務器上。而我們的位置集中基于運行時的信息,可以根據游戲中發生的實際玩法和交互模式來實現動態調整。這類似于編譯時優化和運行時優化之間的區別。前一種方法試圖針對程序所有可能的運行進行優化,而后一種方法試圖針對當前的運行進行優化。
? ? ? ? 我們不相信我們能夠消除內存訪問和持久訪問之間的差別,而且我們也不認為有必要這樣做,最后讓這種架構比使用內存的架構性能更好。要知道,通過讓所有的數據持久,我們可以支持在服務器上使用多線程(從而也支持多核)。盡管我們不相信并發是完美的(即對于每個添加的核,我們都能充分利用),但我們確實相信在游戲和虛擬世界中可以使用大量的并行運算(初步的結果也證實了這種看法)。如果可使用的并行運算超過我們可能引入的延遲,那么游戲或虛擬世界的總體性能就會更好。
3.4.2 賭未來
? ? ? ? 我們對多核處理器中多線程的信心基本上是在賭處理器將來的發展方向。目前服務器的處理器提供2~32個核,我們相信將來的芯片設計將集中向更多的核發展,而不是讓現有的核以更高的時鐘頻率運行。當我們在幾年前開始這個項目時,這種賭博似乎比現在更具投機性。那時候,我們在做展示時常常說這種設計是一種“假定”的演練,說我們正嘗試一種架構,如果芯片性能更好地支持多線程而不是單線程的時鐘速度,這種架構將是可行的。這就是在研究實驗室中進行這類項目的好處之一,可以接受設計方法中存在很高的風險,探索一個將來也許在商業上可行的領域。我們決定構建一個以多線程為中心的架構,與這個決定做出時相比,目前芯片設計的趨勢讓我們看得更清楚。(注3)即使我們只能得到50%的完美并發,如果我們能把使用持久存儲的延遲控制在使用內存的延遲的2~16倍,就能夠在性能上持平。我們相信在并發方面以及減少訪問持久狀態和全內存方案之間的差異方面都可以做得更好。但是結果主要取決于構建于這個基礎設施之上的應用的使用模式(我們曾提到,這一點很難發現)。
? ? ? ? 我們也不應認為減少延遲就是這個基礎設施的唯一目標。通過將游戲或虛擬世界服務器端的對象全部保存在“數據服務”中,我們把因服務器失效而導致的數據丟失減到了最小。實際上,在大多數情況下,服務器失效時用戶只會注意到延時有一點增加,因為任務(它們本身也是持久對象)從失效的服務器移到了另一臺服務器上。沒有數據會丟失。
? ? ? ? 一些緩存機制可能導致丟失幾秒鐘的游戲成果,但即使是這樣,也比在線游戲和虛擬世界目前使用的機制好得多,它們只是將偶爾進行內存快照作為主要的持久方式。在它們的基礎設施中,如果服務器在不巧的時間崩潰,可能會造成數小時的游戲成果丟失。只要延遲是可以接受的,Darkstar所使用持久機制的可靠性更高,這對于在這個基礎設施上構建系統的開發者和系統的用戶來說都是優點。
3.4.3 簡化程序員的工作
? ? ? ? 實際上,如果在支持伸縮性的同時減少延遲是服務器開發者的唯一目標,那么開發者最好的方法就是專門針對特定的游戲,編寫自己的分布式和多線程基礎設施。但這要求服務器開發者處理復雜的分布式和并發編程。在為速度需求而過度煩惱之前,我們應該想到Darkstar的第二個同樣重要的目標,即在支持多線程、分布式游戲產品的同時,為程序員提供一個單機單線程的開發模型。
? ? ? ?在相當大的程度上,我們已經實現了這一目標。通過將所有任務封裝到事務中,并在“數據服務”中檢測數據沖突,程序員就能夠享受到多線程的好處,又不必在他們的代碼中引入鎖協議、同步和信號量。程序員不必擔心如何將玩家從一臺服務器移到另一臺服務器,因為Darkstar為他們提供了透明的負載平衡。編程模型雖然有自己的風格和限制,但社區中的早期成員認為,這對他們開發的那種游戲和虛擬世界來說是比較自然的。不幸的是,我們發現我們不能夠向程序員隱藏所有的東西。當在Darkstar上編寫的第一個游戲表現出極少的并行度(以及意料之外的糟糕性能)時,這一點就明確了。通過檢查源代碼,我們很快就發現了原因。游戲中的數據結構設計導致了游戲中所有的狀態改變都只涉及一個對象,并由它來協調所有的工作。使用這個對象實際上使得游戲中所有動作序列化執行,這使得基礎設施不能夠發現并利用并發計算。
? ? ? ?當我們發現這一點時,我們與游戲開發者進行了長時間的討論,主題是在設計對象時需要考慮到并發訪問。通過對游戲中數據對象進行審查,我們發現了一些類似的情況:數據設計方案排除了并發的可能性(并非出自設計者本意)。當這些對象重新設計之后,系統的整體性能增加了好幾個數量級。
? ? ? ? 這告訴我們,不可能讓使用Darkstar的開發者完全不知道系統底層的并發和分布式實質。但是,他們對系統這方面特點的知識不需要包括并發控制、鎖,以及在系統的各個分布式部分之間的通信。實際上,他們只需要在設計活動中確保他們的數據對象定義能夠充
分利用并發。這種設計一般只需要確保對象定義是自包含的,它們的操作不需要依賴其他對象的屬性。這一點對于任何系統來說,都不是不好的設計原則
? ? ? ?關于Darkstar架構,我們還有許多方面沒有測試,或者說我們并未完全理解。雖然我們已經得到了一個系統,使得多臺機器能夠利用多線程運行一個游戲或虛擬世界,同時對服務器程序員(幾乎)保持透明,但是我們還沒有通過添加核心服務之外的其他服務,來檢驗該架構的能力。由于Darkstar任務的事務本質,這可能比我們開始設想的要復雜得多,但我們希望這些添加的服務不需要參與到核心服務的事務中。我們已經開始試驗通過不同的方式來收集系統負載的信息和實現負載平衡。幸運的是,因為實現這種負載平衡的機制對于使用系統的程序員是完全不可見的,所以我們可以移除老的方式,引入新的方式,同時又不影響Darkstar的用戶。
? ? ? ? 作為一個架構,Darkstar展示了一些創新的方法,這使它變得很有趣。它試圖構造一個游戲或虛擬世界的基礎設施,使其具有企業級軟件一樣的可靠性,同時又滿足游戲行業對延遲、通信和伸縮性的要求。它是目前為數不多的這類嘗試之一。通過利用更多機器和更多線程來實現效率,我們希望能夠抵消因使用持久存儲機制而導致的延遲增加。最后,游戲和虛擬世界環境中極為不同的情況,即客戶端的處理很多而服務器端的處理很少,與我們常見的高并發、分布式系統環境形成了鮮明的對比。現在說這個架構是否成功還為時尚早,但我們相信它已經很有趣了。
總結
以上是生活随笔為你收集整理的架构师必看-架构之美第15章伸缩性架构设计的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何阻止程序联网
- 下一篇: winamp 珍藏_Winamp发生了什