ET消息傳輸流程
結(jié)構(gòu)模型
- NetworkComponent【NetOuterComponent、NetInnerComponent】
- Session
- AService【KService、TService胳赌、WService】
- Socket
- PacketParser
- CircularBuffer
- AChannel【KChannel、TChannel昵慌、WChannel】
- IMessagePacker【MongoPacker盐茎、ProtobufPacker】
- IMessageDispatcher【InnerMessageDispatcher、OuterMessageDispatcher(服務(wù)端)日矫、OuterMessageDispatcher(客戶端)】
- OpcodeTypeComponent
- MessageDispatcherComponent
- IMHandler【AMHandler、AMRpcHandler】
服務(wù)器獨(dú)占
- MailboxDispatcherComponent
- IMailboxHandler【MailboxGateSessionHandler绑榴、MailboxMessageDispatcherHandler】
- ActorMessageDispatcherComponent
- IMActorHandler【AMActorHandler哪轿、AMActorRpcHandler、AMActorLocationHandler翔怎、AMActorLocationRpcHandler】
- LocationProxyComponent
- LocationComponent
- ActorMessageSenderComponent
- ActorMessageSender
- ActorLocationSenderComponent
- ActorLocationSender
客戶端建立Session
-
添加
NetOuterComponent
因?yàn)?code>NetOuterComponent外網(wǎng)組件繼承自
NetworkComponent網(wǎng)絡(luò)組件
窃诉,所以在Init
類中為Scene
添加NetOuterComponent
也就等于添加了NetworkComponent
杨耙,同時(shí)NetOuterComponent
還指定了網(wǎng)絡(luò)組件以什么協(xié)議溝通;- 考慮到服務(wù)端Hotfix與Model分離規(guī)則飘痛,所以
NetOuterComponent
的事件驅(qū)動(dòng)與組件不在一個(gè)類中珊膜。 - 在
NetOuterComponent
的Awake
事件中創(chuàng)建了TService、ProtobufPacker宣脉、OuterMessageDispatcher
车柠,并保存引用到自身- TService:TCPService,繼承自
AService
- ProtobufPacker:Protobuf打包器塑猖,繼承自
IMessagePacker
,主要用于調(diào)用ProtobufHelper
對(duì)消息進(jìn)行打包處理 - OuterMessageDispatcher:外部消息分發(fā)組件竹祷,繼承自
IMessageDispatcher
,主要用于調(diào)用MessageDispatcherComponent
對(duì)外部消息進(jìn)行分發(fā)處理
- TService:TCPService,繼承自
- 考慮到服務(wù)端Hotfix與Model分離規(guī)則飘痛,所以
-
創(chuàng)建
Model.Session
通過外網(wǎng)組件間接調(diào)用網(wǎng)絡(luò)組件的
Create
方法羊苟,先通過TService
的ConnectChannel
方法傳入遠(yuǎn)端IP地址創(chuàng)建了一個(gè)TChannel
,然后將TChannel
作物初始化參數(shù)創(chuàng)建了一個(gè)Model.Session
塑陵,并調(diào)用Session
的Start
方法啟動(dòng)Session。-
TChannel:TCP通道蜡励,繼承自:
AChannel
猿妈,在構(gòu)造函數(shù)中創(chuàng)建了Socket、PacketParser
巍虫,并設(shè)置了各種流處理相關(guān)屬性- Socket:套接字
- PacketParser:包解析器
- CircularBuffer:緩沖區(qū)
-
Session
在其Awake方法中彭则,將
NetworkComponent.Remove
自身方法作為委托傳入了TChannel
的ErrorCallback
中,還將自身的OnRead
方法作為委托傳入了TChannel
的ReadCallback
中Session的
Start
方法調(diào)用了TChannel
的Start
方法建立遠(yuǎn)程鏈接
-
開始循環(huán)異步接收消息
注意占遥,到這里Model.Session已經(jīng)創(chuàng)建完成俯抖,并且開始接收消息,以下是收到消息后的處理
- 當(dāng)一條消息讀取完畢后瓦胎,先調(diào)用
PacketParser
包解析器芬萍,進(jìn)行解包 - 再通過委托調(diào)用
Session
的OnRead
方法對(duì)消息進(jìn)行分發(fā)處理
- 當(dāng)一條消息讀取完畢后瓦胎,先調(diào)用
-
-
創(chuàng)建
Hotfix.Session
將上一步生成的
Model.Session
作為初始化參數(shù),創(chuàng)建一個(gè)Hotfix.Session
搔啊,該類的Dispose
方法會(huì)自動(dòng)調(diào)用Model.Session
的Dispose
在該類的
Awake
事件中柬祠,會(huì)給Model.Session
添加SessionCallbackComponent熱更層Session回調(diào)組件
;該組件持有兩個(gè)委托-
MessageCallback
用于Model.Session
通過委托調(diào)用Hotfix.Session
的Run
方法 -
DisposeCallback
用于Model.Session
通過委托調(diào)用Hotfix.Session
的Dispose
方法
-
發(fā)送消息
-
Hotfix.Session
-
發(fā)送消息
發(fā)送普通消息负芋,調(diào)用
Hotfix.Session
的Send
方法發(fā)送請(qǐng)求消息漫蛔,調(diào)用
Hotfix.Session
的Call
方法,創(chuàng)建一個(gè)異步完成的委托回調(diào)旧蛾,用于消息答復(fù)時(shí)觸發(fā)ETTask返回
通過
OpcodeTypeComponent
對(duì)應(yīng)的消息碼調(diào)用
Model.Session
的Send
方法
-
-
Model.Session
- 通過
NetOuterComponent
類的ProtobufPacker
序列化消息 - 寫入操作碼
- 調(diào)用
TChannel
的Send
方法
- 通過
-
Tchannel
- 將消息寫入緩沖區(qū)
CircularBuffer
- 通過
TService
的MarkNeedStartSend
方法將自生標(biāo)記為待發(fā)送 -
TService
的Update
事件將調(diào)用Tchannel
的StartSend
方法發(fā)送消息 -
StartSend
調(diào)用SendAsync
向遠(yuǎn)端發(fā)送一條消息
- 將消息寫入緩沖區(qū)
接收消息
TChannel
的Start
方法將開啟循環(huán)接收消息莽龟。下面演示接收到一條消息后的處理流程
-
TChannel
- 當(dāng)一條消息讀取完畢后,先調(diào)用
PacketParser
包解析器锨天,進(jìn)行解包 - 通過委托調(diào)用
Session
的OnRead
方法對(duì)消息進(jìn)行分發(fā)處理
- 當(dāng)一條消息讀取完畢后,先調(diào)用
-
Session
獲取操作碼
-
通過
OpcodeHelper.IsClientHotfixMessage
檢測(cè)是否是熱更層消息如果是熱更層消息毯盈,則通過
SessionCallbackComponent
委托調(diào)用熱更層Session處理消息,后續(xù)hotfix和Model處理流程是一樣的 通過
OpcodeTypeComponent操作碼-類型組件
獲取操作碼對(duì)應(yīng)的消息類實(shí)例通過
NetOuterComponent
類的ProtobufPacker
反序列化數(shù)據(jù)-
檢測(cè)消息是否是響應(yīng)消息
- 不是響應(yīng)消息病袄,直接通過
NetOuterComponent
類的OuterMessageDispatcher
調(diào)用MessageDispatcherComponent
處理消息 - 是響應(yīng)消息搂赋,通過消息的RpcId查找請(qǐng)求隊(duì)列中對(duì)應(yīng)的請(qǐng)求赘阀,并激活完成事件
- 不是響應(yīng)消息病袄,直接通過
服務(wù)端Actor模型建立
-
登錄服務(wù)器
- 添加
NetInnerComponent、NetOuterComponent
內(nèi)網(wǎng)組件和外網(wǎng)組件來為當(dāng)前服務(wù)器建立基本網(wǎng)絡(luò)收發(fā)配置 - 分配服務(wù)器網(wǎng)關(guān)地址脑奠,在登錄驗(yàn)證通過后基公,通過
RealmGateAddressComponent
隨機(jī)網(wǎng)關(guān)地址組件獲取內(nèi)網(wǎng)
網(wǎng)關(guān)地址 - 通過向
NetInnerComponent.Get
方法傳入獲取到的網(wǎng)關(guān)地址,創(chuàng)建一個(gè)與網(wǎng)關(guān)服務(wù)器鏈接的內(nèi)網(wǎng)Session - 通過Session在
網(wǎng)關(guān)服務(wù)器
上創(chuàng)建一個(gè)登錄Key捺信,然后將Key和客戶端地址保存到GateSessionKeyComponent
網(wǎng)關(guān)登錄Key組件中,注意:該Key會(huì)在20秒后自動(dòng)銷毀
- 添加
-
網(wǎng)關(guān)服務(wù)器
客戶端通過從登錄服務(wù)器獲取的
外網(wǎng)
網(wǎng)關(guān)地址欠痴,建立一個(gè)鏈接網(wǎng)關(guān)的Session通過
GateSessionKeyComponent
驗(yàn)證網(wǎng)關(guān)登錄Key和Value是否匹配迄靠,不匹配返回登錄失敗將Value作為初始參數(shù)創(chuàng)建一個(gè)
Plyaer
-
為當(dāng)前鏈接客戶端的
Session
添加SessionPlayerComponent
Session綁定Player組件,并綁定Player該類在Session銷毀時(shí)喇辽,自身的Destroy事件會(huì)被調(diào)用掌挚。這里的Destroy還沒寫完
-
同時(shí)為當(dāng)前
Session
添加郵箱組件,并設(shè)置類型為網(wǎng)關(guān)郵箱(網(wǎng)關(guān)郵箱收到的信息會(huì)直接轉(zhuǎn)發(fā)給鏈接當(dāng)前Session的客戶端)掛上這個(gè)組件表示該Entity是一個(gè)Actor,接收的消息將會(huì)隊(duì)列處理
到這一步菩咨,算是為對(duì)象建立Actor模型完成
-
進(jìn)入Map服務(wù)器
通過
StartConfigComponent
初始配置管理組件獲取一個(gè)map服務(wù)器內(nèi)網(wǎng)
地址吠式,并根據(jù)地址使用NetInnerComponent.Get
方法創(chuàng)建一個(gè)鏈接Map服務(wù)器的內(nèi)網(wǎng)Session通過Session發(fā)送在Map服務(wù)器上創(chuàng)建Unit的請(qǐng)求,并傳入當(dāng)前網(wǎng)關(guān)服務(wù)鏈接客戶端的Session的唯一Id
-
Map服務(wù)器
創(chuàng)建Unit
為Unit添加
UnitGateComponent
Unit網(wǎng)關(guān)組件抽米,并將傳入的網(wǎng)關(guān)Session的唯一Id綁定到組件中為Unit添加
MailBoxComponent
郵箱組件-
調(diào)用
MailBoxComponent
郵箱組件的AddLocation
方法調(diào)用
LocationProxyComponent
位置代理組件的Add
方法創(chuàng)建一獲取地址服務(wù)器的
內(nèi)網(wǎng)
地址創(chuàng)建一個(gè)鏈接地址服務(wù)器的內(nèi)網(wǎng)Session通過內(nèi)網(wǎng)Session將Unit的Id和唯一Id發(fā)動(dòng)到Location服務(wù)器
-
Location服務(wù)器
-
接收到Map服務(wù)器發(fā)送的
ObjectAddRequest
請(qǐng)求后特占,在LocationComponent
位置組件中保存?zhèn)魉瓦^來的Unit的Id與InstanceId到這一步,算是為對(duì)象建立了LocationActor模型完成云茸,其實(shí)Actor模型和LocationActor模型代碼上步驟分支主要在于添加郵箱組件時(shí)分配的郵箱類型和有沒有調(diào)用
AddLocation
方法
-
發(fā)送Actor消息
參考服務(wù)端MessageHelper
(客戶端ActorLocation消息參考OperaComponent
是目,服務(wù)端參考SessionPlayerComponentSystem
)
獲取Unit的
UnitGateComponent
Unit網(wǎng)關(guān)組件,并得到組件中網(wǎng)關(guān)Session對(duì)象的唯一Id通過網(wǎng)關(guān)Session的唯一Id提取網(wǎng)關(guān)服務(wù)器設(shè)備Id
通過
ActorMessageSenderComponent
Actor消息發(fā)送器管理組件創(chuàng)建一個(gè)ActorMessageSender
Actor消息發(fā)送器标捺,并通過上一步獲取的網(wǎng)關(guān)服務(wù)器設(shè)備Id查找到對(duì)應(yīng)的網(wǎng)關(guān)服務(wù)器的內(nèi)網(wǎng)地址
懊纳,賦值到Actor消息發(fā)送器中。-
調(diào)用Actor消息發(fā)送器的
Send
方法使用
NetInnerComponent
內(nèi)網(wǎng)組件和網(wǎng)關(guān)服務(wù)器地址亡容,創(chuàng)建一個(gè)鏈接網(wǎng)關(guān)服務(wù)器的內(nèi)網(wǎng)Session嗤疯,然后使用該Session發(fā)送消息。由于創(chuàng)建Session時(shí)使用的時(shí)內(nèi)網(wǎng)地址闺兢,所以會(huì)有網(wǎng)關(guān)服務(wù)器的內(nèi)網(wǎng)組件接收Session傳來的消息茂缚,由于內(nèi)網(wǎng)組件使用的消息分發(fā)器和外網(wǎng)組件不同,所以在解析消息的時(shí)候Actor消息和普通消息相比會(huì)有更多步驟
接收Actor消息
和普通消息前期收取規(guī)則是一樣的屋谭,產(chǎn)生差異在于Session調(diào)用消息分發(fā)器分發(fā)消息時(shí)阱佛。
-
InnerMessageDispatcher
內(nèi)網(wǎng)消息分發(fā)解析消息是否是Actor消息,非Actor消息調(diào)用
MessageDispatcherComponent
消息分發(fā)組件處理消息Actor消息會(huì)通過
EventSystem
查找發(fā)送過來的ActorID(也就是對(duì)象的InstanceId)對(duì)應(yīng)對(duì)象是否存在戴而,不存在則直接返回獲取Actor失敗異常
獲取對(duì)象身上的
MailBoxComponent
郵箱組件凑术,(如果獲取不到則返回獲取郵箱失敗異常
)將Session和Actor消息封裝成ActorMessageInfo
存入郵箱組件中郵箱組件會(huì)在循環(huán)中讀取消息并調(diào)用
MailboxDispatcherComponent
郵箱分發(fā)組件的Handle
方法進(jìn)行二次分發(fā)消息-
Handle
方法根據(jù)前面的郵箱組件類型分發(fā)給MailboxGateSessionHandler或MailboxMessageDispatcherHandler
的Handle
方法做處理MailboxGateSessionHandler
直接將第三步獲取的對(duì)象轉(zhuǎn)換成Session
向客戶端發(fā)送消息-
MailboxMessageDispatcherHandler
通過ActorMessageDispatcherComponent
Actor消息分發(fā)組件的Handle
方法,將消息進(jìn)行最終分發(fā)處理ActorMessageDispatcherComponent
Handle方法會(huì)查找注冊(cè)在組件中消息操作碼對(duì)應(yīng)的消息處理實(shí)例所意,有該實(shí)例完成消息最終的處理
-
OuterMessageDispatcher
外網(wǎng)消息分發(fā)解析消息是否是ActorLocation消息淮逊,非ActorLocation消息調(diào)用
MessageDispatcherComponent
消息分發(fā)組件處理消息ActorLocation消息通過Session綁定的
SessionPlayerCompoennt
組件獲取Player催首,再通過Player獲取UnitId;-
通過
ActorLocationSenderComponent
ActorLocation消息發(fā)送器管理組件創(chuàng)建ActorLocationSender
ActorLocation消息發(fā)送器泄鹏,指定該對(duì)象的Id為UnitId郎任,并將UnitId作為Key和ActorLocation消息發(fā)送器一起保存到字典中由于可能產(chǎn)生Bug或者進(jìn)程掛掉而導(dǎo)致ActorLocationSender沒有返回,所以在ActorLocationSenderComponent的Start事件中開啟了一個(gè)攜程备籽,用于清理超時(shí)1分鐘沒有返回的ActorLocation消息發(fā)送器
由于對(duì)象可能進(jìn)行跨服轉(zhuǎn)移舶治,所以消息的接收不能直接使用對(duì)象身上掛在組件,需要生成一個(gè)代理對(duì)象來收發(fā)消息车猬、對(duì)消息進(jìn)行阻塞霉猛,也就是
ActorLocationSender
- 在
ActorLocationSender
的Start事件中,通過向LocationProxyComponent
位置代理組件傳入U(xiǎn)nitId調(diào)用Get
方法獲取唯一ID - 在
Get
方法中建立一個(gè)鏈接位置服務(wù)器的內(nèi)網(wǎng)Session珠闰,并通過該Session發(fā)送ObjectGetResponse
請(qǐng)求惜浅,傳入U(xiǎn)nitID - 在位置服務(wù)器中,通過接收到的UnitId向
LocationComponent
位置組件GetAsync
異步獲取方法獲取Unit的唯一Id - 在
ActorLocationSender
的Start事件中伏嗜,還開啟了一個(gè)攜程循環(huán)坛悉,用于讀取ActorTask
隊(duì)列
- 在
調(diào)用
ActorLocationSender
ActorLocation消息發(fā)送器的Call或Send
方法,創(chuàng)建一個(gè)ActorTask
Actor消息任務(wù)承绸,并保存到隊(duì)列中-
在
ActorLocationSender
ActorLocation消息發(fā)送器的UpdateAsync
方法中裸影,會(huì)循環(huán)讀取隊(duì)列中的ActoTask
Actor消息任務(wù),并調(diào)用RunTask
方法發(fā)送消息RunTask
方法流程- 先通過
ActorMessageSenderComponent
Actor消息發(fā)送器管理組件的Get
方法獲取一個(gè)ActorMessageSender
Actor消息發(fā)送器
-
Get
方法會(huì)使用傳入的Unit的唯一ID提取對(duì)應(yīng)的服務(wù)器設(shè)備ID军熏,再通過設(shè)備ID獲取對(duì)應(yīng)服務(wù)器內(nèi)網(wǎng)地址- 將Unit唯一ID和服務(wù)器地址作為初始參數(shù)創(chuàng)建一個(gè)
ActorMessageSender
Actor消息發(fā)送器
- 將Unit唯一ID和服務(wù)器地址作為初始參數(shù)創(chuàng)建一個(gè)
使用
ActorMessageSender.CallWithoutException
不拋出異常的方式發(fā)送消息空民,這里后續(xù)邏輯就接入了前面所說的InnerMessageDispatcher
內(nèi)網(wǎng)消息分發(fā)-
檢測(cè)上一步消息是否發(fā)送成功
如果發(fā)送成功,則完成ActorLocation消息的收發(fā)流程
如果是獲取Actor失敗異常羞迷,則等待半秒界轩,然后重新從上面第五步開始
UpdateAsync
方法開始執(zhí)行,注意該流程會(huì)重復(fù)5次衔瓮,超過五次會(huì)將ActorLocationSender
自身銷毀浊猾,并拋出異常如果是獲取郵箱失敗異常,則直接將自身銷毀热鞍,并拋出異常
- 先通過
對(duì)象跨服跳轉(zhuǎn)
參考Actor_TransferHandler
-
Map服務(wù)器A
- 調(diào)用
LocationProxyComponent
位置代理組件的Lock
方法(傳入上鎖對(duì)象ID和上鎖時(shí)長(zhǎng))葫慎,建立與位置服務(wù)器鏈接的內(nèi)網(wǎng)Session,并發(fā)送上鎖消息ObjectLockRequest
- 調(diào)用
-
Location服務(wù)器
- 調(diào)用
LocationComponent
的Lock
方法薇宠,在Lock方法中會(huì)將上鎖對(duì)象ID和唯一ID保存到容器中偷办,建立一個(gè)計(jì)時(shí)器,超時(shí)后從容器中去除對(duì)象
- 調(diào)用
-
Map服務(wù)器A
- 從
EventSystem
中刪除對(duì)象澄港,保存對(duì)象原有唯一ID - 通過轉(zhuǎn)移的目標(biāo)服務(wù)器設(shè)備ID獲取目標(biāo)服務(wù)器地址椒涯,并建立內(nèi)網(wǎng)Session
- 通過內(nèi)網(wǎng)Session,向目標(biāo)服務(wù)器發(fā)送轉(zhuǎn)移對(duì)象回梧。
- 從
-
Map服務(wù)器B
- 將收到的對(duì)象反序列化(注意這里反序列化會(huì)重新生成對(duì)象的唯一ID)
- 將對(duì)象加入
EventSystem
事件系統(tǒng)中 - 重新添加
MailBoxComponent
郵箱組件废岂,來構(gòu)建Actor模型 - 返回給服務(wù)器A新的唯一ID
-
Map服務(wù)器A
- 將對(duì)象銷毀
- 調(diào)用
LocationProxyComponent
位置代理組件的UnLock
方法(傳入上鎖對(duì)象ID和新的唯一ID)祖搓,建立與位置服務(wù)器鏈接的內(nèi)網(wǎng)Session,并發(fā)送上鎖消息ObjectUnLockRequest
-
Location服務(wù)器
- 調(diào)用
LocationComponent
的UnLockAndUpdate
方法 - 在UnLockAndUpdate方法中會(huì)將上鎖對(duì)象的唯一ID進(jìn)行更新
- 調(diào)用
UnLock
方法湖苞,從上鎖容器中刪除對(duì)象拯欧,然后將上鎖期間收到堵塞的Location消息重新進(jìn)行發(fā)送
- 調(diào)用
總結(jié)
消息分類:
-
S2C:
- IMessage
- IRequest
-
C2S:
IMessage
IRequest
-
IActorLocationMessage
前提是服務(wù)端對(duì)鏈接客戶端的Session綁定的對(duì)象進(jìn)行了ActorLocation綁定處理(掛載了郵箱組件,并注冊(cè)到位置服務(wù)器中)财骨,使用Session按正常方式發(fā)送消息即可
IActorLocationRequest
-
S2S:
IMessage
IRequest
-
IActorMessage
前提是知道接收對(duì)象的唯一ID镐作,且接收對(duì)象掛在了郵箱組件
通過
ActorMessageSenderComponent
組件傳入對(duì)象的唯一ID,建立ActorMessageSender
隆箩,通過ActorMessageSender
發(fā)送消息由于Actor是走內(nèi)網(wǎng)組件所以只能在服務(wù)器內(nèi)部傳輸使用该贾,如果希望外網(wǎng)也能調(diào)用,需要自己手動(dòng)擴(kuò)展
OuterMessageDispatcher
IActorRequest
-
IActorLocationMessage
前提是接收對(duì)象有綁定郵箱組件摘仅,并在地址服務(wù)中進(jìn)行了注冊(cè)靶庙。
使用
ActorLocationSenderComponent
組件傳入對(duì)象Id问畅,獲取ActorLocationSender
娃属,通過ActorLocationSender
發(fā)送消息 IActorLocationRequest
登錄流程:
- Client建立一個(gè)鏈接Realm的Session
- Client向Realm發(fā)送登錄請(qǐng)求,驗(yàn)證登錄賬成功后
- Realm向Gate獲取登錄Gate的key和Gate的地址
- Realm返回給Client登錄Gate的key和Gate的地址
- Client通過Gate地址建立一個(gè)新的Session护姆,并銷毀之前的Session
- Client向Gate發(fā)送登錄Gate請(qǐng)求矾端,驗(yàn)證之前返回Key成功后在Gate創(chuàng)建Player
- 將Client地址保存到Player中,方便后續(xù)對(duì)Client建立Session
- 給當(dāng)前Gate鏈接Client的Session添加郵箱組件卵皂,將Session注冊(cè)成Actor
- Gate返回給Client Player的ID秩铆,在客戶端建立Player
- Client向Gate發(fā)送登錄Map請(qǐng)求
- Gate向Map發(fā)送創(chuàng)建Unit請(qǐng)求
- Map創(chuàng)建Unit,并返回UnitId
- 給Unit添加郵箱組件灯变,注冊(cè)成Actor
- 再調(diào)用郵箱組件的AddLocation方法殴玛,將Unit注冊(cè)到Location服務(wù)器中
- Gate返回給Client在Map返回的UnitId
- Client通過UnitId建立Unit