概述
ET框架的訊息機制貫徹始終,包含Entity訊息(Awake,Update ...),自定義(Customer)訊息,網路訊息等。而ET系統的程式包含了客戶端、Gate等各種型別的伺服器,程式包含各種伺服器客戶端之間透過網路訊息進行通訊進行工作。
ET框架訊息結構
-
結構圖為了更加明確整體關係,進行了一定程度的簡化,剔除掉了一些訊息解包等一些細節
-
NetCompontent網路元件有Client客戶端、Server伺服器(Gate)、Inner內網服務等多型,程式業務使用網路元件進行通訊,網路元件會對連線建立一個Secsiom會話物件,封裝連線資訊和相關操作
-
Service和Channel實現負責NetCompontent和Session的功能,有Tcp長連線、Web網頁、KCP無連線可靠協議的多型(若不熟悉KCP協議,可以參考之前寫的文章 跳轉連結: KCP協議淺析)。
- Channel對應一個Session連線,封裝了對底層tcp等協議庫的操作。
- Service對應一個NetCompent元件,對應NetCompent管理多個Session,Server管理和排程一堆Channel的工作。
-
NetServices負責排程多個Service物件的在網路執行緒和主執行緒工作任務,ET開了網路執行緒處理網路相關,某些程式需要多個網路元件(比如Gate伺服器同時需要Server、Inner網路元件,接收轉發客戶端訊息)。
-
NetComponentOnReadEvent是不同型別網路元件的訊息處理器。
- NetClientComponentOnReadEvent對應客戶端Client,處理普通訊息和RPC呼叫。
- NetInnerComponentOnReadEvent對應伺服器內網型別Inner,處理Actor訊息
- NetServerComponentOnReadEvent對應服務外網,處理Actor訊息、RPC呼叫、普通訊息
訊息通訊
以典型的TCP協議型別為例,其他實現類似,看圖相信都能理解,不多贅述。有一些要注意的點:
- 如上述,ET開了一個執行緒處理網路相關,一些工作使用了“生產-消費”Task任務這種方式。
- 使用了非同步Socket
TCP發訊息
TCP收訊息
多程式呼叫
ET框架在基礎網路訊息通訊基礎使用了Actor模型、PRC等相關技術思想(某些思想和實現有調整)進行了擴充,提供多個程式的互相呼叫機制。
先對相關技術做一個簡介:
- Actor模型
在電腦科學中,Actor模型(Actor model)是一種併發運算上的模型。“Actor”是一種程式上的抽象概念,被視為併發運算的基本單元:當一個Actor接收到一則訊息,它可以做出一些決策、建立更多的Actor、傳送更多的訊息、決定要如何回答接下來的訊息。Actor可以修改它們自己的私有狀態,但是隻能透過訊息間接的相互影響(避免了基於鎖的同步)
這是維基百科中對於Actor模型的描述,簡單理解它就是提供了一種訊息機制避免了基於鎖的同步。一些經典的應用場景是多執行緒,在ET框架中它的應用場景是多程式,類似的它提供了一種機制:直接透過ID發訊息,不用關心例項在哪個程式。
- RPC
分散式計算中,遠端過程呼叫(英語:Remote Procedure Call,RPC)是一個計算機通訊協議。該協議允許執行於一臺計算機的程式呼叫另一個地址空間(通常為一個開放網路的一臺計算機)的子程式,而程式設計師就像呼叫本地程式一樣,無需額外地為這個互動作用程式設計(無需關注細節)。RPC是一種伺服器-客戶端(Client/Server)模式,經典實現是一個透過傳送請求-接受回應進行資訊互動的系統
RPC機制透過一些手段抹平了不同程式的差異,使得程式間的呼叫可以和本地非同步呼叫一樣處理。
瞭解了這兩種技術,下面來看ET框架對其的應用和如何實現多程式呼叫的。
訊息協議型別
如上圖所示訊息型別分為三種:
- Message 訊息,無需應答
- Request 請求,對應一個Response應答
- Response 應答,對應一個請求
注意Request和Response一定成對定義,且其Message一定包含一個RpcId欄位
訊息型別可以被字首修飾,修飾有三種:
- None 客戶端與伺服器(Gate)之間不需要轉發的訊息。(注意這裡不是修飾字元不是"None",而是""表示沒有)
- Actor 伺服器內網之間的訊息
- ActorLocation 客戶端與伺服器需要轉發的訊息。
修飾字元可以修飾任意訊息型別,組合起來一共有9種訊息。
如這個登入到Gate的協議:
//ResponseType G2C_LoginGate
message C2G_LoginGate // IRequest
{
int32 RpcId = 1;
int64 Key = 2; // 帳號
int64 GateId = 3;
}
message G2C_LoginGate // IResponse
{
int32 RpcId = 1;
int32 Error = 2;
string Message = 3;
int64 PlayerId = 4;
}
- C2G_LoginGate 在訊息名後面註明了訊息型別,並在訊息名上面註明了應答包的訊息型別,並且包含一個RpcId的欄位,這些都必須的,表示這是個不需要轉發的、需要應答的請求訊息。而訊息名字首只是方便理解,起到註釋的作用:表示這是Client程式傳送給Gate伺服器程式的訊息。
- G2C_LoginGate 同上,是上述C2G_LoginGate請求的應答訊息。
Rpc呼叫過程
如圖所示,進行Rpc呼叫時,生成一個新的RpcID並帶入請求包中,同時把呼叫資訊存起來。對方應答時,會把請求包的RpcID傳入到應答包中。在收到訊息時如果是Resp型別訊息會呼叫OnResp方法,透過應答包的RpcID取出RpcInfo,透過RpcInfo取消呼叫RpcCall函式的非同步阻塞。
public static void OnResponse(this Session self, IResponse response)
{
if (!self.requestCallbacks.TryGetValue(response.RpcId, out var action))
{
return;
}
self.requestCallbacks.Remove(response.RpcId);
if (ErrorCore.IsRpcNeedThrowException(response.Error))
{
action.Tcs.SetException(new Exception($"Rpc error, request: {action.Request} response: {response}"));
return;
}
action.Tcs.SetResult(response);
}
action.Tcs.SetResult會取消非同步的阻塞,執行await後面的語句,詳情見之前寫的文章 跳轉連結: C#非同步程式設計
Actor模型實現
在ET框架的設計中,Actor其實是一個帶有MailboxComponent的元件。其具體的實現方式,ET有比較詳細的文件說明,有實現思路和使用方式的介紹,這裡貼出來,不做贅述。
跳轉連結: ET Actor模型 官方介紹