ET服务器框架学习笔记(十三)

ET服务器框架学习笔记(十三)


前言

在ET5.0中协议的类型非常重要,他不仅代表收到协议的Session处理数据的处理(是否需要注册回调),还涉及到不同的派发器的不同处理,同时内网派发组件与外网派发组件对不同的协议类型,也有不同的处理。前篇仅对协议数据的非Actor消息进行简单的分析,这篇围绕Actor消息进行分析。


Actor消息分为外网组件与内网组件进行分开分析。

一、OuterMessageDispatcher的Actor处理

1、OuterMessageDispatcher之IActorLocationRequest处理

OuterMessageDispatcher一般来说主要是Gate服务使用的,在ET5.0中客户端除了登录验证等服务后,都需要登录到GATE上进行真正的游戏通信,客户端跟其他游戏逻辑服务模块,都是通过Gate进行转发完成。进行IActorLocationRequest转发处理有两个前提:1.已经在Gate上进行过登录处理,2.在其他服务如:MAP服务上已经创建过Unit对象。

  • 获取客户端在Gate上已经登录过的Player上的UnitId(这个ID是通过在其他服上创建过Unit对象后,拿到的UnitId),后面会单独记录一篇串联整个通信流程的例子。
  • 获取ActorLocationSenderComponent组件,通过Get方法获取一个ActorLocationSender,这是个异步函数。
  • 进入Get方法查看,创建一个actorLocationSender,同时将传入的Unit当作actorLocationSender的组件ID,同时缓存到ActorLocationSenderComponent组件的ActorLocationSenders字典中,方便下次直接找到这个actorLocationSender。
    -调用 actorLocationSender的异步GetActorId方法,进入GetActorId方法查看
  • await CoroutineLockComponent.Instance.Wait(self.Id) 通过CoroutineLockComponent组件的单例异步方法Wait(long key)查看,针对每个actorLocationSender的ID,会有一个LockQueue队列,结合ETBOOK知道这里使用了锁的机制。
  • 如果有锁,那么就代表请求的对象正在转移,即他的InstanceID正在发生变化,这里会分两种处理方式。
  • 当没有锁的时候,说明对象并没有转移,创建好队列,方便下次使用外,就直接返回;如果有锁,那么就需要等待转移成功,所以返回一个ETTaskCompletionSource,进行异步处理。
  • 如果转移成功或者转移超时,那么就会释放这个CoroutineLock锁,这时会派发解锁消息,这样CoroutineLockComponent就可以设置异步完成,从而让所有异步await async ETTask Wait(long key)的地方继续执行下去。
    (下节单独来分析这个锁的方式,现在先不看他)
  • 回到GetActorId方法里面,await CoroutineLockComponent.Instance.Wait(self.Id)之后,异步调用LocationProxyComponent的Get方法。
  • LocationProxyComponent的Get方法会发送一个协议到Location服务,即:new ObjectGetRequest() { Key = key }
  • 到ObjectGetRequestHandler的处理也比较简单,就是获取LocationComponent,然后返回一个当前ID对应的最新的instanceId即可
  • 返回后,actorLocationSender内部就有与之关联的ActorId(其实他就是某个Entity最新的instanceId,拿到这个instanceId即可找到对应的IP与端口去发送数据)
  • 一步步异步完成后,回调OuterMessageDispatcher的IActorLocationRequest处理中,我们得到了一个包含最新的unitId对应的instanceId的actorLocationSender。
  • 然后调用actorLocationSender内部封装的Send,一步步流转到ActorLocationSender的扩展RunInner方法,调用ActorMessageSenderComponent的Get方法得到一个ActorMessageSender。
  • ActorMessageSender实例化时,会通过IPEndPoint ipEndPoint = StartConfigComponent.Instance.GetInnerAddress(IdGenerater.GetAppId(actorId)); 内部通过actorId得到对应的AppId,然后通过配置拿到这个AppId对应的地址,这样actorMessageSender内部就保存了actorId,以及对应的IPEndPoint
  • 调用actorMessageSender的CallWithoutException方法,内部封装了NetInnerComponent以actorMessageSender内部存放的地址获取一个Session,然后调用Session的CallWithoutException即消息发送出去。
  • 因为这是个异步方法,且是一个IRequest类是协议,所以等到对方回复协议IResponse时,会调用到在Session内注册的tcs.SetResult(response)方法,从而唤醒异步。
  • 即回到了OuterMessageDispatcher的IResponse response = await actorLocationSender.Call(actorLocationRequest);这一行代码里,至此转发协议已经完成,且获得了回复,然后将回复的协议返回出去即可。
  • 需要注意的是,要保存客户端发来的RpcId,因为这个RpcId才能在客户端收到消息时,调用对应的回调方法。要不然在IResponse response里的RpcId,会是Gate转发出去时自己的RpcId。
  • 对session状态进行判断,然后调用session.Reply(response);这样一次完整的IActorLocationRequest处理即完成完毕。

2、OuterMessageDispatcher之IActorLocationMessage处理

相对IActorLocationRequest而言,IActorLocationMessage处理就简单一些,毕竟这个类型的协议,是不需要返回的,直接转发就可以了。代码也比较少:

case IActorLocationMessage actorLocationMessage:
				{
					long unitId = session.GetComponent<SessionPlayerComponent>().Player.UnitId;
					ActorLocationSender actorLocationSender =await Game.Scene.GetComponent<ActorLocationSenderComponent>().Get(unitId);
					actorLocationSender.Send(actorLocationMessage).Coroutine();
					break;
				}

至此,OuterMessageDispatcher的Actor消息转发流程已经完毕,下一节分析InnerMessageDispatcher的Actor消息处理

二、InnerMessageDispatcher之的Actor处理

1.InnerMessageDispatcher之IActorRequest

InnerMessageDispatcher的协议消息主要来源于各个服务模块之间的消息,或者由其他服务模块转发而来的。与外网组件的派发不一样,这里一般只做处理,即收到了Actor消息,基本上都是自己需要处理的。

  • HandleIActorRequest(session, iActorRequest).Coroutine();这里使用了.Coroutine(),在ETBOOK中有解释,避免Actor消息死锁,所以新开协程来处理就行了。(Coroutine方法的意义,需要后续研究,暂时将它当作Unity携程来理解)
  • 进入HandleIActorRequest查看,同样的,首先是等待CoroutineLockComponent锁结束,因为只有锁结束了,才能表明一个Actor的实体是真正稳定下来(即不在转移过程中,也没有产生转移等)。
  • 前面有说过,消息里面的ActorId是对象最新的InstanceID,所以通过EventSystem可以直接得到实体对象。
  • 获取实体对象后,对象不存在,则回复报错消息。如果存在,则获取对象身上的MailBoxComponent组件
  • 异步将这个消息放入mailBoxComponent中,根据ETBOOK介绍,mailBoxComponent处理消息是一个个携程异步处理的。
  • 我们找到对应的Add方法,他先获取到MailboxDispatcherComponent组件,然后调用MailboxDispatcherComponent组件的Handle方法。
  • 在Handle方法之前,可以看到MailboxDispatcherComponentHelper类,扩展了MailboxDispatcherComponent方法,在Load时,会将所有带MailboxHandlerAttribute类的实例(即iMailboxHandler)与MailboxType都存放到MailboxHandlers中。(这个类似EventSystem里面的方式,具体可以回看相关内容)
  • 回到Handle方法,可以看到他会从MailboxHandlers中找到对应的iMailboxHandler类,并调用他们的Handle方法。
  • 全局定位可以看到,MailboxHandlers只有两种类型,一种是GateSession,一种是MessageDispatcher。
  • 先看看GateSession,即其他服务发给Gate的Actor消息。Gate收到这个消息,都是进行转发,在Gate中挂上mailbox的都是与各个客户端连接的Session(在C2G_LoginGateHandler中,可以看到登录Gate时,会给每个Session挂载一个MailBoxComponent:session.AddComponent<MailBoxComponent, string>(MailboxType.GateSession);所以Gate上的MailboxGateSessionHandler就是拿到消息,将entity转为session,然后将消息转发出去即可。
  • 再看看MessageDispatcher类型,这个类型应该是ET各个服务使用,他会获取ActorMessageDispatcherComponent组件,并调用它的Handle方法处理,并将session与消息封装成一个ActorMessageInfo进行处理。
  • 同样的ActorMessageDispatcherComponentHelper中,在Load的时候,会将所有带有ActorMessageHandlerAttribute标记的类实例化并存到ActorMessageDispatcherComponent的ActorMessageHandlers字典中。
  • 拿到对应的ActorMessageHandlers,并调用其Handle方法,即可对应到每个协议的具体处理方法上,最终会。实际上每个真正的处理方法先继承AMActorLocationRpcHandler,而AMActorLocationRpcHandler都会继承IMActorHandler,在AMActorLocationRpcHandler的Handle做了一道中转处理,进行一些通用的日志打印,然后调用到各个具体处理类的Run方法。
    至此,一个InnerMessageDispatcher的IActorRequest消息处理完毕。

2.InnerMessageDispatcher之IActorMessage

从代码可以看到,因为不涉及回调处理,所以与IActorRequest处理方式一模一样,都是获取到entity,然后获取他们身上的MailBoxComponent,然后把消息仍到mailBoxComponent上处理。

总结

本篇简单分析了,ET中内网与外网组件,对Actor消息的不同处理方式。我们需要清楚知道,首先区分内外网派发,然后每个派发组件上,又区分不同的IActorRequest,与IActorMessage处理。然后通过不同的mailbox处理器,对Actor消息进行不同的处理,在Gate上直接走转发给各个客户端。
而其他服务模块上,是先获取ActorMessageDispatcherComponent,然后调用注册好的各个IMActorHandler进行处理,多转了两次弯。
下篇,会通过实例来贯穿这一长串消息处理,终于到了这一篇了,基础的东西好多!!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值