- 百萬在線:大型游戲服務端開發
- 羅培羽
- 911字
- 2021-09-17 17:04:58
2.9 使用Skynet的注意事項
使用一套引擎,就要理解它的特性。Skynet最大的特性是“提供同一機器上充分利用多核CPU的處理能力”,由此帶來的時序問題值得特別注意。
2.9.1 協程的作用
Skynet服務在收到消息時,會創建一個協程,在協程中會運行消息處理方法(即用skynet.dispatch設置的回調方法)。這意味著,如果在消息處理方法中調用阻塞API(如skynet.call、skynet.sleep、socket.read),服務不會被卡?。▋H僅是處理消息的協程被卡住),執行效率得以提高,但程序的執行時序將得不到保證。
如圖2-36所示,某個服務的消息隊列存在多條消息,第一條消息的處理函數是OnMsg1,第二條是OnMsg2,OnMsg1調用了阻塞方法skynet.sleep。盡管程序會依次調用OnMsg1、OnMsg2……但當執行到阻塞函數時,協程會掛起。實際執行順序可能是圖2-36中右邊展示的“語句1、skynet.sleep、語句3、語句4、語句2”。

圖2-36 使用阻塞API需要注意時序問題
2.9.2 扣除金幣的Bug
本節展示一種不注意時序問題導致的Bug,假設游戲有“存款”功能,玩家可以把一定數量的金幣存入銀行,獲得利息。相關服務如圖2-37所示,agent服務代表玩家控制的角色,bank服務代表銀行。存款的過程如下:客戶端發起存款請求(階段①),agent向bank轉達請求(階段②),bank會返回操作的結果(階段③)。代碼2-16展示了一種有Bug的寫法。

圖2-37 代碼2-16的示意圖
代碼2-16 agent的消息處理方法(有Bug)
local coin = 20 --角色身上的金幣數 function CMD.deposit(source) if coin < 20 then --假設每次存20金幣 return end local isok = skynet.call(bank, "lua", "deposit", 20); if isok then coin = coin - 20 end end
存在這么一種可能,玩家快速地兩次點擊存款按鈕,消息時序會按圖2-37中①①②③的順序執行。如果角色身上僅剩20金幣,第一次操作時,尚剩余20金幣,第二次操作時,依然剩余20金幣,兩次操作都能成功,玩家總共存入40金幣,剩余“-20”金幣,顯然不合理。
因為角色身上只有20金幣,正常的情況是,無論玩家多么快速地點擊存款按鈕,他都只能成功存入一次。代碼2-17展示了一種解決方法,在阻塞方法skynet.call之前扣除金幣,如果存款失敗,才補上扣除的金幣。
代碼2-17 修復代碼2-16的程序
function CMD.deposit(source) if coin < 20 then --假設每次存20金幣 return end coin = coin - 20 local isok = skynet.call(bank, "lua", "deposit", 20); if not isok then coin = coin + 20 end end
現在,你已掌握了Skynet的特性和基本操作,接下來的一章,會用綜合示例說明怎樣用Skynet去開發真正的游戲項目。這里先打個預防針,下一章的難度頗高,代碼多、流程復雜,但如能掌握,你就擁有勝任“服務端開發工程師”崗位的條件。
- Mastering JavaScript Functional Programming
- Git Version Control Cookbook
- ASP.NET Core 5.0開發入門與實戰
- Leap Motion Development Essentials
- NLTK基礎教程:用NLTK和Python庫構建機器學習應用
- Wireshark Network Security
- Spring Cloud、Nginx高并發核心編程
- GameMaker Programming By Example
- 劍指Java:核心原理與應用實踐
- C語言程序設計教程
- INSTANT Silverlight 5 Animation
- 網絡數據采集技術:Java網絡爬蟲實戰
- Three.js權威指南:在網頁上創建3D圖形和動畫的方法與實踐(原書第4版)
- MonoTouch應用開發實踐指南:使用C#和.NET開發iOS應用
- PHP從入門到精通(微視頻精編版)