- 百萬在線:大型游戲服務(wù)端開發(fā)
- 羅培羽
- 1970字
- 2021-09-17 17:04:53
1.7 萬物皆Actor
合理分割功能是分布式模型的一大難點,我們需要尋找一種模式,它既能符合游戲邏輯的表達,又能讓計算機高效執(zhí)行。
游戲業(yè)界苦苦追尋著更適合當(dāng)代游戲的服務(wù)端模型,驀然回首,幾十年前就被提出的Actor并發(fā)模型就在燈火闌珊處。如圖1-25所示,在Actor模型中,每個Actor相互隔離,只通過消息通信,具有天然的并發(fā)性。

圖1-25 Actor模型的示意圖
想要借用Actor模型的理念,首先要了解什么是Actor模型。
1.7.1 靈感來自Erlang
Actor模型由來已久。早在1973年,Carl Hewitt提出了Actor并發(fā)計算的理論模型;1991年愛立信推出的編程語言Erlang將Actor模型融入語言里,并應(yīng)用在通信領(lǐng)域里。2009年前后,珠三角的一些游戲公司(四三九九、菲音、明朝網(wǎng)絡(luò))開始大規(guī)模地將Erlang語言應(yīng)用于游戲領(lǐng)域。2012年前后,云風(fēng)(吳云洋的網(wǎng)名)開源了C語言Actor模型框架Skynet,并稱之為游戲服務(wù)端引擎,且將其應(yīng)用在不少商業(yè)游戲上。
1.7.2 對世界的抽象
Actor模型的理念——萬物皆Actor,它是更進一步的面向?qū)ο螅窗咽篱g萬物都當(dāng)作Actor對象。Actor可以代表一個角色、一只動物,也可以代表整個游戲場景,圖1-26展示的是用Actor模型抽象的一個游戲世界,方括號代表Actor的類型,id代表Actor的標(biāo)識,中間文本代表名稱。

圖1-26 萬物皆Actor
說明:Skynet中將Actor對象稱為服務(wù),Erlang中將其稱為進程(不同于操作系統(tǒng)進程),為統(tǒng)一術(shù)語,在解釋Actor模型時,使用“Actor”一詞;在Skynet的語義下,使用“服務(wù)”一詞。
在圖1-27中,每個Actor都會包含自身狀態(tài)(HP、Coin),以及一個信箱(消息隊列),Actor通過給其他Actor“寄信”來實現(xiàn)通信。至于收到信件后的反應(yīng),取決于收信的Actor。

圖1-27 Actor示意圖
整個Actor系統(tǒng)可類比為“郵局”,它負(fù)責(zé)信件的傳送。如圖1-28所示,Actor1001給1002發(fā)送信件,請求“查詢登錄玩家數(shù)量”,寄出的“信件”經(jīng)由郵局,投遞到Actor1002的信箱。由于各個Actor相互獨立,計算機很容易讓它們并行工作。

圖1-28 Actor消息傳遞示意圖
如果理論解釋不易理解,不妨直接看代碼。代碼1-10用Lua定義了Role類型的Actor對象。代碼中的local Role=function()...end用定義方法的方式定義了一個類,這是Lua的特殊語法,只需記住這段代碼定義了Role類,它包含id、coin、hp這三個屬性和dispatch方法即可。dispatch是處理消息的方法,參數(shù)source代表消息發(fā)送方,參數(shù)msg代表消息內(nèi)容。如果收到“work”,角色會努力工作賺錢,并督促發(fā)送方努力工作(發(fā)送“work”給對方);如果收到“eat”,角色會吃美食恢復(fù)健康。
代碼1-10 定義一個Actor對象(Lua語言)
local Role = function() local M = { id = -1, --Actor標(biāo)識 coin = 100, hp = 200, } function M:dispatch(source, msg) if msg == "work" then self.coin = self.coin + 10 print(self.id.." work, coin:"..self.coin) send(self.id, source, "work") elseif msg == "eat" then self.hp = self.hp + 5 print(self.id.." eat, hp:"..self.hp) else --更多消息處理 end end return M end
Actor系統(tǒng)會提供newactor(創(chuàng)建Actor對象)、send(向Actor發(fā)送消息)之類的方法,如代碼1-11中,創(chuàng)建了4個Role類型的Actor,它們的id是從101到104,這里讓101給102發(fā)送“work”,103給104發(fā)送“eat”。
代碼1-11 創(chuàng)建Actor對象并發(fā)送消息(Lua語言)
newactor(101, Role()) newactor(102, Role()) newactor(103, Role()) newactor(104, Role()) send(101, 102, "work") send(103, 104, "eat")
圖1-29是代碼1-11中4個Role對象的運行示意圖,其中101和102這兩個Actor在相互督促工作,努力賺錢;103讓104吃好喝好。圖1-30展示了代碼1-11的運行結(jié)果,盡管是101先向102發(fā)送“work”后,103再向104發(fā)送“eat”,但各個Actor是并行執(zhí)行的,因此消息處理的順序不確定。

圖1-29 代碼1-11運行的示意圖

圖1-30 代碼1-11的運行結(jié)果
1.7.3 為何適用
為什么說Actor模型適用于游戲開發(fā)呢?
回顧1.4.3節(jié)的多進程程序,從某種程度上說,Actor模型和傳統(tǒng)的多進程服務(wù)端結(jié)構(gòu)有很多相似之處。不同的是,一個操作系統(tǒng)進程會占用很多的系統(tǒng)資源,按照1.5.3節(jié)的分析,進程不僅會占用較多的內(nèi)存,操作系統(tǒng)在切換進程(線程)時也會占用較多的CPU時間,一臺物理機只能運行幾百個進程,這會限制游戲的業(yè)務(wù)分割。
舉個例子,假設(shè)要開發(fā)一款斗地主游戲(如圖1-31所示),每局游戲由3名玩家參與。那么,一種處理方式是開啟多個進程,每個進程處理多張桌子的邏輯(如圖1-32所示)。如果每個進程可以處理10張桌子,單臺物理機開啟100個進程就可以支撐3000名玩家。但是,進程A和進程B究竟是什么東西呢?是桌子集合?它其實是計算機系統(tǒng)的概念,因為在現(xiàn)實世界找不到對應(yīng)的事物,所以不容易理解。還有一種方法,即讓每個進程只處理一張桌子的邏輯,然而由于進程會占用很多的系統(tǒng)資源,因此這樣做會導(dǎo)致單臺物理機只能支持幾百名玩家。

圖1-31 《斗地主》游戲示意圖

圖1-32 傳統(tǒng)的多進程模型
Erlang、Skynet通過內(nèi)部處理,讓每個Actor都是輕量級的,可讓每張桌子獨立分開(如圖1-33所示),讓游戲邏輯更符合現(xiàn)實世界場景。同時,Actor與多進程模型同樣具備天然分布式屬性,在圖1-33中,不同的桌子可以運行在不同的物理機上。

圖1-33 Actor模型提供了更多的靈活性
對游戲服務(wù)端而言,Actor并發(fā)模型給游戲業(yè)務(wù)的分割提供了靈活性。
再回顧1.2.3節(jié)的簡單服務(wù)端程序,如果說Node.js提供了“單線事件模型”的運行環(huán)境,那么Skynet提供了Actor模型的運行環(huán)境(如圖1-34所示)。

圖1-34 類比Node.js和Skynet
現(xiàn)在,你已經(jīng)初步了解游戲服務(wù)端開發(fā)所需的關(guān)鍵技術(shù),以及一些需要注意的事項。本書分為“學(xué)以致用”“入木三分”“各個擊破”三大篇章,第一篇“學(xué)以致用”就是要讓讀者能夠以最快的速度做出成品。接下來我們使用Skynet引擎,先把游戲做出來!
- 玩轉(zhuǎn)Scratch少兒趣味編程
- Mastering ServiceStack
- 劍指JVM:虛擬機實踐與性能調(diào)優(yōu)
- 編寫高質(zhì)量代碼:改善C程序代碼的125個建議
- Instant QlikView 11 Application Development
- SAP BusinessObjects Dashboards 4.1 Cookbook
- Mastering Apache Spark 2.x(Second Edition)
- TMS320LF240x芯片原理、設(shè)計及應(yīng)用
- Extreme C
- Python編程:從入門到實踐(第3版)
- IDA Pro權(quán)威指南(第2版)
- 從0到1:HTML5 Canvas動畫開發(fā)
- Visual C++從入門到精通(第2版)
- Python編程入門(第3版)
- Android初級應(yīng)用開發(fā)