ET介紹——分散式Actor模型

来源:https://www.cnblogs.com/flamesky/archive/2023/05/19/17414884.html
-Advertisement-
Play Games

Actor模型 Actor介紹 在討論Actor模型之前先要討論下ET的架構,游戲伺服器為了利用多核一般有兩種架構,單線程多進程跟單進程多線程架構。兩種架構本質上其實區別不大,因為游戲邏輯開發都需要用單線程,即使是單進程多線程架構,也要用一定的方法保證單線程開發邏輯。ET採用的是單線程多進程的架構, ...


Actor模型

Actor介紹

在討論Actor模型之前先要討論下ET的架構,游戲伺服器為了利用多核一般有兩種架構,單線程多進程跟單進程多線程架構。兩種架構本質上其實區別不大,因為游戲邏輯開發都需要用單線程,即使是單進程多線程架構,也要用一定的方法保證單線程開發邏輯。ET採用的是單線程多進程的架構,而傳統Actor模型一般是單進程多線程的架構,這點是比較大的區別,不能說誰更好,只能說各有優勢。優劣如下:

  1. 邏輯需要單線程這點都是一樣的,erlang進程邏輯是單線程的,skynet lua虛擬機也是單線程的。ET中一個進程其實相當於一個erlang進程,一個skynet lua虛擬機。
  2. 採用單線程多進程不需要自己再寫一套profiler工具,可以利用很多現成的profiler工具,例如查看記憶體,cpu占用直接用top命令,這點erlang跟skynet都需要自己另外搞一套工具。
  3. 多進程單線程架構還有個好處,單台物理機跟多台物理機是沒有區別的,單進程多線程還需要考慮多台物理機的處理。
  4. 多進程單線程架構一點缺陷是消息跨進程需要進行序列化反序列化,占用一點資源。另外發送網路消息會有幾毫秒延時。一般這些影響可以忽略。

最開始Actor模型是給單進程多線程架構使用的,這是有原因的,因為多線程架構開發者很容易隨意的訪問共用變數,比方說一個變數a, 線程1能訪問,線程2也能訪問,這樣兩個線程在訪問變數a的時候都需要加鎖,共用變數多了之後鎖到處都是,會變得無法維護,框架肯定不能出現到處是線程共用變數的情況。為了保證多線程架構不出問題,必須提供一種開發模型保證多線程開發簡單又安全。erlang語言的併發機制就是actor模型。erlang虛擬機使用多線程來利用多核。erlang設計了一種機制,它在虛擬機之上設計了自己的進程。最簡單的,每個erlang進程都管理自己的變數,每個erlang進程的邏輯都跑在一個線程上,erlang進程跟進程之間邏輯完全隔離,這樣就不存在兩個線程訪問同一變數的情況了也就不存在多線程競爭的問題。接下來問題又出現了,既然每個erlang進程都有自己的數據,邏輯完全是隔離的,兩個erlang進程之間應該怎麼進行通信呢?這時Actor模型就登場了。erlang設計了一種消息機制:一個進程可以向其它進程發送消息,erlang進程之間通過消息來進行通信,看到這會不會感覺很熟悉?這不就是操作系統進程間通信用的消息隊列嗎?沒錯,其實是類似的。erlang裡面拿到進程的id就能給這個進程發送消息。

如果消息只發給進程其實還是有點不方便。比如拿一個erlang進程做moba戰隊進程,戰鬥進程中有10個玩家,如果使用erlang的actor消息,消息只能發送給戰鬥進程,但是很多時候消息是需要發送給一個玩家的,這時erlang需要根據消息中的玩家Id,把消息再次分發給具體的玩家,這樣其實多繞了一圈。

ET的Actor

ET根據自己架構得特點,沒有完全照搬erlang的Actor模型,而是提供了Entity對象級別的Actor模型。這點跟erlang甚至傳統的Actor機制不一樣。ET中,Actor是Entity對象,Entity掛上一個MailboxComponent組件就是一個Actor了。只需要知道Entity的InstanceId就可以發消息給這個Entity了。其實erlang的Actor模型不過是ET中的一種特例,比如給ET服務端Game.Scene當做一個Actor,這樣就可以變成進程級別的Actor。Actor本質就是一種消息機制,這種消息機制不用關心位置,只需要知道對方的InstanceId(ET)或者進程的Pid(erlang)就能發給對方。

語言ETErlangSkynet
架構 單線程多進程 單進程多線程 單進程多線程
Actor Entity erlang進程 lua虛擬機
ActorId Entity.InstanceId erlang進程Id 服務地址

ET的Actor的使用

普通的Actor,我們可以參照Gate Session。map中一個Unit,Unit身上保存了這個玩家對應的gate session。這樣,map中的消息如果需要發給客戶端,只需要把消息發送給gate session,gate session在收到消息的時候轉發給客戶端即可。map進程發送消息給gate session就是典型的actor模型。它不需要知道gate session的位置,只需要知道它的InstanceId即可。MessageHelper.cs中,通過GateSessionActorId獲取一個ActorMessageSender,然後發送。

// 從Game.Scene上獲取ActorSenderComponent,然後通過InstanceId獲取ActorMessageSender
ActorSenderComponent actorSenderComponent = Game.Scene.GetComponent<ActorSenderComponent>();
ActorMessageSender actorMessageSender = actorSenderComponent.Get(unitGateComponent.GateSessionActorId);
// send
actorMessageSender.Send(message);

// rpc
var response = actorMessageSender.Call(message);

 

問題是map中怎麼才能知道gate session的InstanceId呢?這就是你需要想方設法傳過去了,比如ET中,玩家在登錄gate的時候,gate session掛上一個信箱MailBoxComponent,C2G_LoginGateHandler.cs中

session.AddComponent<MailBoxComponent, string>(MailboxType.GateSession);

 

玩家登錄map進程的時候會把這個gate session的InstanceId帶進map中去,C2G_EnterMapHandler.cs中

M2G_CreateUnit createUnit = (M2G_CreateUnit)await mapSession.Call(new G2M_CreateUnit() { PlayerId = player.Id, GateSessionId = session.InstanceId });

 

Actor消息的處理

首先,消息到達MailboxComponent,MailboxComponent是有類型的,不同的類型郵箱可以做不同的處理。目前有兩種郵箱類型GateSession跟MessageDispatcher。GateSession郵箱在收到消息的時候會立即轉發給客戶端,MessageDispatcher類型會再次對Actor消息進行分發到具體的Handler處理,預設的MailboxComponent類型是MessageDispatcher。自定義一個郵箱類型也很簡單,繼承IMailboxHandler介面,加上MailboxHandler標簽即可。那麼為什麼需要加這麼個功能呢,在其它的actor模型中是不存在這個特點的,一般是收到消息就進行分發處理了。原因是GateSession的設計,並不需要進行分發處理,因此我在這裡加上了郵箱類型這種設計。MessageDispatcher的處理方式有兩種一種是處理對方Send過來的消息,一種是rpc消息

    // 處理Send的消息, 需要繼承AMActorHandler抽象類,抽象類第一個泛型參數是Actor的類型,第二個參數是消息的類型
    [ActorMessageHandler(AppType.Map)]
    public class Actor_TestHandler : AMActorHandler<Unit, Actor_Test>
    {
        protected override ETTask Run(Unit unit, Actor_Test message)
        {
            Log.Debug(message.Info);
        }
    }

    // 處理Rpc消息, 需要繼承AMActorRpcHandler抽象類,抽象類第一個泛型參數是Actor的類型,第二個參數是消息的類型,第三個參數是返回消息的類型
    [ActorMessageHandler(AppType.Map)]
    public class Actor_TransferHandler : AMActorRpcHandler<Unit, Actor_TransferRequest, Actor_TransferResponse>
    {
        protected override async ETTask Run(Unit unit, Actor_TransferRequest message, Action<Actor_TransferResponse> reply)
        {
            Actor_TransferResponse response = new Actor_TransferResponse();

            try
            {
                reply(response);
            }
            catch (Exception e)
            {
                ReplyError(response, e, reply);
            }
        }
    }

 

我們需要註意一下,Actor消息有死鎖的可能,比如A call消息給B,B call給C,C call給A。因為MailboxComponent本質上是一個消息隊列,它開啟了一個協程會一個一個消息處理,返回ETTask表示這個消息處理類會阻塞MailboxComponent隊列的其它消息。所以如果出現死鎖,我們就不希望某個消息處理阻塞掉MailboxComponent其它消息的處理,我們可以在消息處理類裡面新開一個協程來處理就行了。例如:

    [ActorMessageHandler(AppType.Map)]
    public class Actor_TestHandler : AMActorHandler<Unit, Actor_Test>
    {
        protected override ETTask Run(Unit unit, Actor_Test message)
        {
            RunAsync(unit, message).Coroutine();
        }

        public ETVoid RunAsync(Unit unit, Actor_Test message)
        {
            Log.Debug(message.Info);
        }
    }

 

相關資料可以谷歌一下Actor死鎖的問題。

ET開源地址地址:egametang/ET: Unity3D Client And C# Server Framework (github.com)   qq群:474643097


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Apache JMeter是Apache組織開發的基於Java的壓力測試工具。用於對軟體做壓力測試,它最初被設計用於Web應用測試,但後來擴展到其他測試領域。 它可以用於測試靜態和動態資源,例如靜態文件、Java 小服務程式、CGI 腳本、Java 對象、資料庫、FTP 伺服器, 等等。JMeter ...
  • 基於java的職工管理系統設計與實現,員工管理系統,企業員工管理系統,公司員工管理系統,企業人事管理系統,基於java職工管理系統,前後端分離,員工考勤管理系統,職工獎懲管理系統,職員合同管理,HR管理系統,人事HR管理系統。 ...
  • Author Alex Zhang Category SpreadJS Tags SpreadJS,前端電子錶格,實時數據,RealTime Data 前言 數據(包括股票、天氣和體育比分)在不斷更新為新信息時最為有用。SpreadJS是一個非常通用的 JavaScript 電子錶格組件,它還可以輕 ...
  • C語言教程——翁凱老師、赫斌 翁愷老師是土生土長的浙大碼農,從本科到博士都畢業於浙大電腦系,後來留校教書,一教就是20多年。 翁愷老師的c語言課程非常好,講解特別有趣,很適合初學者學習。 郝斌老師的思路是以初學者的思路來思考的,非常適合小白,你不理解的問題,基本上他都會詳細說一下。 C++——侯捷 ...
  • 前言: 自從使用了 AsyncLocal 後,就發現 AsyncLocal 變數像個臭蟲一樣,在有 AsyncLocal 變數的線程中啟動的 Task 、或者 Thread 都會附帶 AsyncLocal 變數。 在項目使用 AsyncLocal 實現了全局、局部 工作單元 ,但是就無法在後續作業中 ...
  • # 前言 SQLite是一種輕量級的關係型資料庫管理系統,支持跨平臺操作。它可以嵌入到程式中,無需單獨的伺服器進程或者配置文件,減少了資料庫維護的負擔和運行的複雜性。SQLite的數據存儲在單個文件中,方便備份、傳輸和分享,也容易進行版本管理。SQLite擁有良好的性能、可靠的穩定性和豐富的功能,成 ...
  • 類似魔獸世界,moba這種技能極其複雜,靈活性要求極高的技能系統,必須需要一套及其靈活的數值結構來搭配。數值結構設計好了,實現技能系統就會非常簡單,否則就是一場災難。比如魔獸世界,一個人物的數值屬性非常之多,移動速度,力量,怒氣,能量,集中值,魔法值,血量,最大血量,物理攻擊,物理防禦,法術攻擊,法 ...
  • Actor Location Actor模型只需要知道對方的InstanceId就能發送消息,十分方便,但是有時候我們可能無法知道對方的InstanceId,或者是一個Actor的InstanceId會發生變化。這種場景很常見,比如:很多游戲是分線的,一個玩家可能從1線換到2線,還有的游戲是分場景的 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...