從騰訊QQ升級遊戲之“快速加入遊戲”功能的實現缺陷看C/S之間如何正確分配相關協作
QQ升級遊戲有一個“快速加入遊戲”的功能,方便玩家儘快加入目標牌桌。這本身是個非常人性化的功能,但其實現卻存在一個缺陷,當玩家當前所在房間內,同時執行“快速加入遊戲”功能的使用者數較多時,常常會出現加入失敗的情況。筆者碰到的最糟情形是重複5、6次以上,才最後成功加入,其間獲得的使用者體驗自然是不夠好。
(圖A)“快速加入遊戲”失敗示意
注意:點選圖片可以放大觀看筆者分析這個缺陷的根源,可能是“快速加入遊戲”這一功能之實現在伺服器端與客戶端之間的協作分配方式有問題:
QQgame的伺服器端維護了遊戲大廳中所有房間、和每個房間中所有牌桌,及相關玩家在這些牌桌的佔位情況;而每個登入QQgame的客戶端會實時(因為客戶端數目龐大,伺服器端提供資料更新服務的實際延遲時間會達到秒級以上,並不能真正做到實時)地從伺服器端獲取這些資料;玩家(使用者)進入某個房間後,當其執行“快速加入遊戲”功能時,客戶端會先分析當前房間中所有牌桌的佔位情況,並選擇一個未滿桌的牌桌,然後向伺服器端發出加入此牌桌的請求;伺服器端收到客戶端的加入指定牌桌的請求後,開始嘗試完成加入操作,然而,因為客戶端資料更新的後滯性,造成伺服器端在此前可能已經收到其它客戶端所發相同的(指定了同一牌桌)加入請求,併為其完成加入,使得此牌桌位滿,於是本次加入操作失敗。
(圖一)客戶端執行“加入未滿牌桌”
顯然,解決此缺陷最簡單的做法便是改變伺服器端與客戶端之間的協作分配方式。原來的方式之所以造成可能加入失敗,是因為進行“快速加入遊戲”,必須先選擇一個未滿桌的牌桌,這必然依賴於當前房間中所有牌桌的佔位資料,這些資料是由伺服器端所維護的,如果在客戶端完成這一選擇,就需要從伺服器端實時更新這些資料到客戶端,而資料的更新根本實現不了真正的實時,於是有兩個以上客戶端同時發出向同一牌桌加入的請求就不能避免;那麼,如果“選擇一個未滿桌的牌桌”這個工作不在客戶端進行呢?我們可以由客戶端向伺服器端發出一個自由加入牌桌的請求,這個請求並不指定目標牌桌,伺服器端收到請求後,直接找到一個未滿桌的牌桌分配給客戶端所對應的玩家,並把此結果返回給客戶端;在這種協作方式下,無論多少個併發客戶端同時發出加入請求,因為伺服器端都是排隊按順序來一一加以完成的(可以通過對牌桌的玩家佔位資料加同步鎖以實現多執行緒安全來實現),所以總是不會出現爭位而因滿桌失敗的情形。
(圖二)伺服器端執行“自由加入未滿牌桌”
(圖三)“加入遊戲”場景實現的參與類檢視
如何在伺服器端與客戶端之間正確分配相關的協作,是分散式軟體設計中極其關鍵的環節。由於網路的延遲特性,我們不能將分散式環境下的資料與桌面應用下的等同看待;QQ升級遊戲的開發人員有可能忽略了這一點,而沒有更深入地思考這一協作分配問題。有個所謂“客戶與伺服器端協作分配之資料依賴”原則應當儘量遵守--如果某個功能的實現依賴於某些資料,那麼這個功能的實現最好分配給資料的直接擁有者(不論是客戶端還是伺服器端),這實際上就是GRASP之資訊專家模式的翻版(GRASP模式中資料的擁有者指的是物件)。
當然,因為QQGame在高效能方面的要求極高,QQ升級遊戲開發者可能有其它的考慮因素而選擇目前的做法。例如,每個牌桌在開始進行遊戲時,有複雜的遊戲邏輯要執行,這些遊戲邏輯如果都放在伺服器端執行,恐怕需要的伺服器叢集是個嚇人的數目,騰訊或許不願意投入如此多的資金去購買這些伺服器;那麼玩家的機器同時充當客戶端與伺服器端的做法就是一個很自然的選擇。遊戲開發者可能會這樣設計:第一個加入某個牌桌的使用者,其主機將自動充當本牌桌的遊戲伺服器,此後,其它玩家要加入此牌桌,其加入請求應當發往第一個加入的使用者主機,而非騰訊的伺服器。不管怎樣,這些都涉及到極其複雜的分散式架構設計問題,有興趣的話,筆者將在後續文章中進一步深入探討這些問題。實際上,升級遊戲在可以執行“快速加入遊戲”功能前,還有一個進入某個房間的步驟;而同樣由於資料更新延遲的因素,造成玩家常常選擇加入一個當時顯示人數為未滿的房間時,結果卻為已滿的情形。要強調的是,這一問題是無法通過上述調整協作分配的方法來解決的。
為了改善使用者的操作體驗,避免加入遊戲失敗的次數是必要的,實際上,除了上述的做法,還有其它臨時性的修復方法。例如,可以增加一個所謂“自動快速加入遊戲”的功能,就是客戶端自動重複執行原來的“快速加入遊戲”的功能,直到加入成功為止(不需使用者重複按那個按鈕而已,一笑)。另外,還有一個減少“快速加入遊戲”失敗概率的方法,就是客戶端在選擇目標牌桌時,不援用固定的策略,而是使用某種隨機方式,這樣多個客戶端同時指定加入同一牌桌的概率大幅降低,爭位失敗的情形自然也大幅減少了。當然,上述兩種途徑還可以結合在一起應用。筆者很期待騰訊能儘早修復上述缺陷,給包括筆者在內的玩家們一個更好的操作體驗。