(75)遊戲服務器開發的基本體系與服務器端開發的一些建議

轉自:http://www.jb51.net/article/119274.htm   侵權刪程序員

近年來,我身邊的朋友有不少都從web轉向了遊戲開發。他們之前都沒有作過遊戲服務器開發,更談不上什麼經驗,而從網上找的例子或遊戲方面的知識,又是那麼的少,那麼的零散。當他們進入遊戲公司時,顯得一臉茫然。若是是大公司還好點,起碼有人帶帶,能學點經驗,可是有些人是直接進入了小公司,甚至這些小公司只有他一個後臺。他們一肩扛起了公司的遊戲後端的研發,也扛起了公司的成敗。他們也很是盡力,他們也想把遊戲的後端作好。但是就是由於沒什麼經驗,剛開始時覺得作遊戲服務器和作web差很少,可是通過一段時間以後,才發現代碼太多,太亂了,一看代碼都想重構,都是踩着坑往前走。這裏我把一些遊戲開發方面的東西整理一下,但願能對那些想作遊戲服務器開發的朋友有所幫助。web

      首先,要明確一點,作遊戲服務器開發和作傳統的web開發有着本質的區別。遊戲服務器開發,若是沒有經驗,一開始根本沒有一個明確清析的目標,不像web那樣,有些明確的MVC架構,每每就是爲了儘快知足策劃的需求,儘快的實現功能,儘快能讓遊戲跑起來。可是隨着功能愈來愈多,在老代碼上面修改的愈來愈頻繁,遊戲測試時暴露出來的一堆bug,更讓人以爲一籌莫展,這個時候咱們想到了重構,想到了架構的設計。算法

   遊戲的構架設計很是重要,好的構架代碼清析,責任明確,擴展性強,易調試。這些會爲咱們的開發省去很多時間。那要怎麼樣設計遊戲的構架呢?可能每一個遊戲都不同,可是本質上仍是差很少的。數據庫

   對於遊戲服務器的構架設計,咱們首先要了解遊戲的服務器構架都有什麼組成的?一款遊戲到上線,須要具有哪些功能?有些人可能會說,只要讓遊戲跑起來,訪問服務器不出問題不就好了嗎?答案是不行的,遊戲構架自己表明的是一個體系,它包括:編程

   1,系統初始化  
   2,遊戲邏輯
   3,數據庫系統
   4,緩存系統。
   5,遊戲日誌
   6,遊戲管理工具
   7,公共服務組件後端

這一系統的東西都是不可少的,它們共同服務於遊戲的整個運營過程。咱們一點點來介紹各個系統的功能。設計模式

一,系統初始化api

      系統初始化是在沒有客戶端鏈接的時候,服務器啓動時所須要作的工做。基本上就是配置文件的讀取,初始化系統參數。可是咱們必需要考慮的是,系統初始化須要的參數配置在哪兒,是配置在本地服務器,仍是配置在數據庫,服務器啓的時候去數據庫取。配置的修改需不須要重啓服務器等。數組

二,遊戲邏輯緩存

      遊戲邏輯是遊戲的核心功能實現,也是整個遊戲的服務中心,它被開發的好壞,直接決定了遊戲服務器在運行中的性能。那在遊戲邏輯的開發中咱們要注意些什麼呢?

   (1)網絡通訊

        遊戲是一種網絡交互比較強的業務,好的底層通訊,能夠最大化遊戲的性能,增長單臺服務器處理的同時在線人數,給遊戲帶來更好的體驗,至少不容易出現由於網絡層致使的數據交互卡頓的現象。在這裏我推薦使用Netty,它是目前最流行的NIO框架,它的用法能夠在我以前的文章中查看,這裏再也不多說了。

         有人疑問,代碼也須要分層次?這個是固然了,不一樣的代碼,表明了不一樣的功能實現。如今的開發語言都是面向對象的,若是咱們不加思考,不加整理的把功能代碼亂堆一塊兒,起始看起來是快速實現了功能,可是到後期,若是要修改需求,或在原來的代碼上增長新的需求,那真是被本身戰勝了。因此代碼必定要分層,主要有如下幾層:

      a,協議層,也叫先後臺交互層,它主要負責與前臺交互協議的解析和返回數據。在這一層基本上沒有什麼業務邏輯實現。與前臺交互的數據都在這一層開始,也在這一層終止。好比你使用了Netty框架,那麼Netty的ChannelHandlerContext即Ctx只能出如今這一層,他不能出現到遊戲業務邏輯代碼的實現中,接收到客戶端的請求,在這一層把須要的參數解析出來,再把參數傳到業務邏輯方法中,業務邏輯方法處理完後,把要返回給客戶端的數據再返回到這一層,在這一層組織數據,返回給客戶端,這樣就能夠把業務邏輯和網絡層分離,業務邏輯只關心業務實現,並且也方便對業務邏輯進行單元測試。

       b,業務邏輯層,這裏處理真正的遊戲邏輯,該計算價格計算價格,該通關的通關,該計時的計時。該保存數據的保存數據。可是這一層不直接操做緩存或數據庫,只是處理遊戲邏輯計算。由於業務邏輯層是整個遊戲事件的處理核心,因此他的處理是否正確直接決定遊戲的正確性。因此這一層的代碼要儘可能使用面向對角的方法去實現。不要出現重複代碼或類似的功能進行復制粘貼,這樣修改起來很是不方便,多是修改了某一處,而忘記了修改另外一樣的代碼。還要考慮每一個方法都是可測試的,一個方法的行數最好不要超過一百行。另外,能夠多看看設計模式的書,它能夠幫助咱們設計出靈活,整潔的代碼。

 三,數據庫系統

      數據庫是存儲數據庫的核心,可是遊戲數據在存儲到數據庫的時候會通過網絡和磁盤的IO,它的訪問速度相對於內存來講是很慢的。通常來講,每次訪問數據庫都要和數據庫創建鏈接,訪問完成以後,爲了節省數據庫的鏈接資源,要再把鏈接斷開。這樣無形中又爲服務器增長了開銷,在大量的數據訪問時,可能會更慢,而遊戲又是要求低延時的,這時該怎麼辦呢?咱們想到了數據庫鏈接池,即把訪問數據庫的鏈接放到一個地方管理,用完我不斷開,用的時候去那拿,用完再放回去。這樣不用每次都創建新的鏈接了。可是若是要咱們本身去實現一套鏈接池管理組件的話,須要時間不說,對技術的把控也是一個考驗,還要再通過測試等等,幸虧互聯網開源的今天,有一些現成的可使用,這裏推薦Mybatis,即實現了代碼與SQL的分離,又有足夠的SQL編寫的靈活性,是一個不錯的選擇。

 四,緩存系統

  遊戲中,客戶端與服務器的交互是要求低延遲的,延遲越低,用戶體驗越好。像以前說過的同樣,低延遲就是要求服務器處理業務儘可能的快,客戶端一個請求過來,要在最短的時間內響應結果,最低不得超過500ms,由於加上來回的網絡傳輸耗時,基本上就是600ms-到700ms了,再長玩家就會以爲遊戲卡了。若是直接從數據庫中取數據,處理完以後再存回數據庫的話,這個性能是跟不上的。在服務器,數據在內存中處理是最快的,因此咱們要把一部分經常使用的數據提早加載到內存中,好比說遊戲數據配置表,常常登錄的玩家數據等。這樣在處理業務時,就不用走數據庫了,直接從內存中取就能夠了,速度更快。遊戲中常見的緩存有兩種,1,直接把數據存儲在jvm或服務器內存中,2,使用第三方的緩存工具,這裏推薦Redis,詳細的用法能夠本身去查詢。

 五,遊戲日誌

      日誌是個好東西呀,一個遊戲中更不能少了日誌,並且日誌必定要記錄的詳細。它是玩家在整個遊戲中的行爲記錄,有了這個記錄,咱們就能夠分析玩家的行爲,查找遊戲的不足,在處理玩家在遊戲中的問題時,日誌也是一個良好的憑證和快速處理方式。
      在遊戲中,日誌分爲:1,系統日誌,主要記錄遊戲服務器的系統狀況。好比:數據庫可否正常鏈接,服務器是否正常啓動,數據是否正常加載;2,玩家行爲日誌,好比玩家發送了什麼請求,獲得了什麼物品,消費了多少貨幣等等;3,統計日誌,這種日誌是對遊戲中全部玩家某種行爲的一種統計,根據這個統計來分析大部分玩家的行爲,得出一些共性或不一樣之處,以方法運營作不一樣的活動吸引用戶消費。
      在構架設計中,日誌記錄必定要作爲一種強制行爲,由於不強制的話,可能因爲某種緣由某個功能忘記加日誌了,那麼當這個功能出問題了,或者運營跟咱們要這個功能的一些數據庫,就傻眼了。又得加需求,改代碼了。日誌必定要設計一種良好的格式,日誌記錄的數據要容易讀取,分解。日誌行爲能夠用枚舉描述,在功能最後的處理方法裏面加上這個枚舉作爲參數,這樣無論誰在調用這個方法時,都要去加參數描述。
      俗話說,工欲善其事,必先利其器。遊戲管理工具是對遊戲運行中的一系列問題處理的一種工具。它不只是給開發人員用,大多數是給運營使用。遊戲上線後,咱們須要針對線上的問題進行不一樣的處理。不可能把全部問題都讓程序員去處理吧,因而程序員們想到了一個辦法,給大家作一個工具,大家愛誰處理誰處理去吧。

六, 遊戲管理工具

 遊戲管理工具是一個不斷增漲的系統,由於它不少時候是伴隨着遊戲中遇到的問題而實現的。可是根據經驗,有一些功能是必須有的,好比:服務器管理,主要負責服務器的開啓,關閉,服務器配置信息,玩家信息查詢,玩家管理,好比踢人,封號;統計查詢,玩家行爲日誌查詢,統計查詢,次留率查詢,郵件服務,修改玩家數據等,根據遊戲的不一樣要求,凡是能夠能過工具實現的,都作到遊戲管理工具裏面。它是針對全部服務器的管理。一個好的,全的遊戲管理工具,能夠提升遊戲運營中遇到問題處理的效率,爲玩家提供更好的服務。

 七,公共組件

      公共組件是爲遊戲運行中提供公共的服務,好比,充值服務器,咱們沒必須一個服用一個充值,並且你也不能對外提供多個充值服務器地址,和第三方公司對接,他們絕對不幹,這是要瘋呀;還有運營搞活動時的禮包碼,還有註冊用戶的管理,玩家一個註冊帳號能夠進不一樣的區等。這些都是針對全部區服提供的服務,因此要單獨作,與遊戲邏輯分開,這樣方便管理,部署和負載均衡。還有SDK的登錄驗證,如今手遊比較多,與渠道對接裏要進行驗證,這每每是不少http請求,速度慢,因此這個也要拿出來單獨作,不要在遊戲邏輯中去驗證,由於網絡IO的訪問時間是不可控制的,http是阻塞的請求。
 因此,綜上來看,一個遊戲服務器起碼有幾個大的功能模塊組成:a,遊戲邏輯工程;b,日誌處理工程;c,充值工程;d,遊戲管理工具工程;e,用戶登錄工程;f,公共活動工程等,根據遊戲的不一樣須要,可能還有其它的。所在在構架的設計中,必定要考慮到系統的分佈式部署,儘可能把公共的功能拆出來作,這樣能夠加強系統的可擴展性。

服務器端開發的一些建議

本文做爲遊戲服務器端開發的基本大綱,是遊戲實踐開發中的總結。第一部分專業基礎,用於指導招聘和實習考覈, 第二部分遊戲入門,講述遊戲服務器端開發的基本要點,第三部分服務端架構,介紹架構設計中的一些基本原則。但願能幫到你們

一 專業基礎

1.1 網絡

1.1.1 理解TCP/IP協議
網絡傳輸模型
滑動窗口技術
創建鏈接的三次握手與斷開鏈接的四次握手
鏈接創建與斷開過程當中的各類狀態
TCP/IP協議的傳輸效率
思考

1)請解釋DOS攻擊與DRDOS攻擊的基本原理
2)一個100Byte數據包,精簡到50Byte, 其傳輸效率提升了50%
3)TIMEWAIT狀態怎麼解釋?
1.1.2 掌握經常使用的網絡通訊模型
Select
Epoll,邊緣觸發與平臺出發點區別與應用
Select與Epoll的區別及應用
1.2 存儲
計算機系統存儲體系
程序運行時的內存結構
計算機文件系統,頁表結構
內存池與對象池的實現原理,應用場景與區別
關係數據庫MySQL的使用
共享內存
1.3 程序
對C/C++語言有較深的理解
深入理解接口,封裝與多態,而且有實踐經驗
深入理解經常使用的數據結構:數組,鏈表,二叉樹,哈希表
熟悉經常使用的算法及相關複雜度:冒泡排序,快速排序
二 遊戲開發入門

2.1防護式編程
不要相信客戶端數據,必定要檢驗。做爲服務器端你沒法肯定你的客戶端是誰,你也不能假定它是善意的,請作好自我保護。(這是判斷一個服務器端程序員是否入門的基本標準)
務必對於函數的傳人蔘數和返回值進行合法性判斷,內部子系統,功能模塊之間不要太過信任,要求低耦合,高內聚
插件式的模塊設計,模塊功能的健壯性應該是內建的,儘可能減小模塊間耦合
2.2 設計模式
道法天然。不要迷信,迷戀設計模式,更不要生搬硬套
簡化,簡化,再簡化,用最簡單的辦法解決問題
借大寶一句話:設計本天成,妙手偶得之
2.3 網絡模型
自造輪子: Select, Epoll, Epoll必定比Select高效嗎?
開源框架: Libevent, libev, ACE
2.4 數據持久化
自定義文件存儲,如《夢幻西遊》
關係數據庫: MySQL
NO-SQL數據庫: MongoDB
選擇存儲系統要考慮到因素:穩定性,性能,可擴展性
2.5 內存管理
使用內存池和對象池,禁止運行期間動態分配內存
對於輸入輸出的指針參數,嚴格檢查,寧濫勿缺
寫內存保護。使用帶內存保護的函數(strncpy, memcpy, snprintf, vsnprintf等),嚴防數組下標越界
防止讀內存溢出,確保字符串以'\0'結束
2.6 日誌系統
簡單高效,大量日誌操做不該該影響程序性能
穩定,作到服務器崩潰是日誌不丟失
完備,玩家關鍵操做必定要記日誌,理想的狀況是經過日誌能重建任什麼時候刻的玩家數據
開關,開發日誌的要加級別開關控制
2.7 通訊協議
採用PDL(Protocol Design Language), 如Protobuf,能夠同時生成先後端代碼,減小先後端協議聯調成本, 擴展性好
JSON,文本協議,簡單,自解釋,無聯調成本,擴展性好,也很方便進行包過濾以及寫日誌
自定義二進制協議,精簡,有高效的傳輸性能,徹底可控,幾乎無擴展性
2.8 全局惟一Key(GUID)
爲合服作準備
方便追蹤道具,裝備流向
每一個角色,裝備,道具都應對應有全局惟一Key
2.9 多線程與同步
消息隊列進行同步化處理
2.10 狀態機
強化角色的狀態
前置狀態的檢查校驗
2.11 數據包操做
合併, 同一幀內的數據包進行合併,減小IO操做次數
單副本, 用一個包儘可能只保存一份,減小內存複製次數
AOI同步中減小中間過程無用數據包
2.12 狀態監控
隨時監控服務器內部狀態
內存池,對象池使用狀況
幀處理時間
網絡IO
包處理性能
各類業務邏輯的處理次數
2.13 包頻率控制
基於每一個玩家每條協議的包頻率控制,癱瘓變速齒輪
2.14 開關控制
每一個模塊都有開關,能夠緊急關閉任何出問題的功能模塊
2.15 反外掛反做弊
包頻率控制能夠消滅變速齒輪
包id自增校驗,能夠消滅WPE
包校驗碼能夠消滅包攔截篡改
圖形識別嗎,能夠踢掉99%非人的操做
魔高一尺,道高一丈
2.16 熱更新
核心配置邏輯的熱更新,如防沉迷系統,包頻率控制,開關控制等
代碼基本熱更新,如Erlang,Lua等
2.17 防刷
關鍵系統資源(如元寶,精力值,道具,裝備等)的產出記日誌
資源的產出和消耗盡可能依賴兩個或以上的獨立條件的檢測
嚴格檢查各項操做的前置條件
校驗參數合法性
2.18 防崩潰
系統底層與具體業務邏輯無關,能夠用大量的機器人壓力測試暴露各類bug,確保穩定
業務邏輯建議使用腳本
系統性的保證遊戲不會崩潰
2.19 性能優化
IO操做異步化
IO操做合併緩寫 (事務性的提交db操做,包合併,文件日誌緩寫)
Cache機制
減小競態條件 (避免頻繁進出切換,儘可能減小鎖定使用,多線程不必定因爲單線程) 多線程不必定比單線程快
減小內存複製
本身測試,用數聽說話,別猜
2.20 運營支持
接口支持:實時查詢,控制指令,數據監控,客服處理等
實現考慮提供Http接口
2.21 容災與故障預案

三 服務器端架構

3.1 什麼是好的架構?
知足業務要求
能迅速的實現策劃需求,響應需求變動
系統級的穩定性保障
簡化開發。將複雜性控制在架構底層,下降對開發人員的技術要求,邏輯開發不依賴於開發人員自己強大的技術實力,提升開發效率
完善的運營支撐體系
3.2 架構實踐的思考
簡單,知足需求的架構就是好架構
設計性能,抓住重要的20%, 不必從程序代碼裏面去摳性能
熱更新是必須的
人不免會犯錯,儘量的用一套機制去保障邏輯的健壯性
遊戲服務器的設計是一項很有挑戰性的工做,遊戲服務器的發展也由之前的單服結構轉變爲多服機構,甚至出現了bigworld引擎的分佈式解決方案,最近了解到Unreal的服務器解決方案atlas也是基於集羣的方式。

負載均衡是一個很複雜的課題,這裏暫不談bigworld和atlas的這類服務器的設計,更多的是基於功能和場景劃分服務器結構。

首先說一下思路,服務器劃分基於如下原則:

分離遊戲中佔用系統資源(cpu,內存,IO等)較多的功能,獨立成服務器。
在同一服務器架構下的不一樣遊戲,應儘量的複用某些服務器(進程級別的複用)。
以多線程併發的編程方式適應多核處理器。
寧肯在服務器之間多複製數據,也要保持清晰的數據流向。
主要按照場景劃分進程,若需按功能劃分,必須保持整個邏輯足夠的簡單,並知足以上1,2點。
服務器結構圖:

各個服務器的簡要說明:

Gateway 是應用網關,主要用於保持和client的鏈接,該服務器須要2種IO,對client採用高併發鏈接,低吞吐量的網絡模型,如IOCP等,對服務器採用高吞吐量鏈接,如阻塞或異步IO。

網關主要有如下用途:

分擔了網絡IO資源
同時,也分擔了網絡消息包的加解密,壓縮解壓等cpu密集的操做。
隔離了client和內部服務器組,對client來講,它只須要知道網關的相關信息便可(ip和port)。
client因爲一直和網關保持常鏈接,因此切換場景服務器等操做對client來講是透明的。
維護玩家登陸狀態。
World Server 是一個控制中心,它負責把各類計算資源分佈到各個服務器,它具備如下職責:

管理和維護多個Scene Server。
管理和維護多個功能服務器,主要是同步數據到功能服務器。
複雜轉發其餘服務器和Gateway之間的數據。
實現其餘須要跨場景的功能,如組隊,聊天,幫派等。
Phys Server 主要用於玩家移動,碰撞等檢測。

全部玩家的移動類操做都在該服務器上作檢查,因此該服務器自己具有全部地圖的地形等相關信息。具體檢查過程是這樣的:首先,Worldserver收到一個移動信息,WorldServer收到後向Phys Server請求檢查,Phys Server檢查成功後再返回給world Server,而後world server傳遞給相應的Scene Server。

Scene Server 場景服務器,按場景劃分,每一個服務器負責的場景應該是能夠配置的。理想狀況下是能夠動態調節的。

ItemMgr Server 物品管理服務器,負責全部物品的生產過程。在該服務器上存儲一個物品掉落數據庫,服務器初始化的時候載入到內存。任何須要產生物品的服務器均與該服務器直接通訊。

AIServer 又一個功能服務器,負責管理全部NPC的AI。AI服務器一般有2個輸入,一個是Scene Server發送過來的玩家相關操做信息,另外一個時鐘Timer驅動,在這個設計中,對其餘服務器來講,AIServer就是一個擁有不少個NPC的客戶端。AIserver須要同步全部與AI相關的數據,包括不少玩家數據。因爲AIServer的Timer驅動特性,可在很大程度上使用TBB程序庫來發揮多核的性能。

把網絡遊戲服務器分拆成多個進程,分開部署。這種設計的好處是模塊天然分離,能夠單獨設計。分擔負荷,能夠提升整個系統的承載能力。

缺點在於,網絡環境並不那麼可靠。跨進程通信有必定的不可預知性。服務器間通信每每難以架設調試環境,並很容易把事情攪成一團糨糊。並且正確高效的管理多鏈接,對程序員來講也是一項挑戰。

前些年,我也曾寫過好幾篇與之相關的設計。這幾天在思考一個問題:若是咱們要作一個底層通用模塊,讓後續開發更爲方便。到底要解決怎樣的需求。這個需求應該是單一且基礎的,每一個應用都須要的。

正如 TCP 協議解決了互聯網上穩定可靠的點對點數據流通信同樣。遊戲世界實際須要的是一個穩定可靠的在遊戲系統內的點對點通信須要。

咱們能夠在一條 TCP 鏈接之上作到這一點。一旦實現,能夠給遊戲服務的開發帶來極大的方便。

能夠把遊戲系統內的各項服務,包括並不限於登錄,拍賣,戰鬥場景,數據服務,等等獨立服務當作網絡上的若干終端。每一個玩家也能夠是一個獨立終端。它們一塊兒構成一個網絡。在這個網絡之上,終端之間能夠進行可靠的鏈接和通信。

實現能夠是這樣的:每一個虛擬終端都在遊戲虛擬網絡(Game Network)上有一個惟一地址 (Game Network Address , GNA) 。這個地址能夠預先設定,也能夠動態分配。每一個終端均可以經過遊戲網絡的若干接入點 ( GNAP ) 經過惟一一條 TCP 鏈接接入網絡。接入過程須要經過鑑權。

鑑權過程依賴內部的安全機制,能夠包括密碼證書,或是特別的接入點區分。(例如,玩家接入網絡就須要特定的接入點,這個接入點接入的終端都必定是玩家)

鑑權經過後,網絡爲終端分配一個固定的遊戲域名。例如,玩家進入會分配到 player.12345 這樣的域名,數據庫接入可能分配到 database 。

遊戲網絡默認提供一個域名查詢服務(這個服務能夠經過鑑權的過程註冊到網絡中),讓每一個終端都能經過域名查詢到對應的地址。

而後,遊戲網絡裏全部合法接入的終端均可以經過其地址相互發起鏈接並通信了。整個協議創建在 TCP 協議之上,工做於惟一的這個 TCP 鏈接上。和直接使用 TCP 鏈接不一樣。遊戲網絡中每一個終端之間相互發起鏈接都是可靠的。不只玩家能夠向某個服務發起鏈接,反過來也是能夠的。玩家之間的直接鏈接也是可行的(是否容許這樣,取決於具體設計)。

因爲每一個虛擬鏈接都是創建在單一的 TCP 鏈接之上。因此減小了互連網上發起 TCP 鏈接的各類不可靠性。鑑權過程也是一次性惟一的。而且咱們提供域名反查服務,咱們的遊戲服務能夠清楚且安全的知道鏈接過來的是誰。

系統能夠設計爲,遊戲網絡上每一個終端離網,域名服務將廣播這條消息,通知全部人。這種廣播服務在互聯網上難以作到,但不管是廣播仍是組播,在這個虛擬遊戲網絡中都是可行的。

在這種設計上。在邏輯層面,咱們可讓玩家直接把聊天信息從玩家客互端發送到聊天服務器,而不須要創建多餘的 TCP 鏈接,也不須要對轉發處理聊天消息作多餘的處理。聊天服務器能夠獨立的存在於遊戲網絡。也可讓廣播服務主動向玩家推送消息,由服務器向玩家發起鏈接,而不是全部鏈接請求都是由玩家客互端發起。

虛擬遊戲網絡的構成是一個獨立的層次,徹底能夠撇開具體遊戲邏輯來實現,並可以單獨去按承載量考慮具體設計方案。很是利於剝離出具體遊戲項目來開發並優化。

最終,咱們或許須要的一套 C 庫,用於遊戲網絡內的通信。api 能夠和 socket api 相似。額外多兩條接入與離開遊戲網絡便可。

相關文章
相關標籤/搜索