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

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


前言

前篇记录了抽象类NetworkComponent,本篇介绍ET真正加载使用的类:NetInnerComponent与NetOuterComponent。

一、NetInnerComponent与NetOuterComponent

NetInnerComponent称为内网组件,NetOuterComponent称为外网组件。基本上所有服务都需要内网组件,而外网组件不是所有服务都需要,可以看到:Location服务,Map服务是没有外网组件的,说明这两个服务,都是通过内网进行转发出去的。
下面再看看具体的逻辑。

二、两者的区别

1.NetOuterComponent

通过NetOuterComponent几个相关的System代码可以看到,基本上与NetworkComponent使用上没有太多变动,主要是对NetworkComponent的初始化工作,消息解析器用的ProtobufPacker,派发器用的OuterMessageDispatcher。

self.Awake(self.Protocol, address);
			self.MessagePacker = new ProtobufPacker();
			self.MessageDispatcher = new OuterMessageDispatcher();

主要看看派发器,从上篇文章,可以看到当Session收到数据后,进行一系列处理后,如果不是IResponse协议,就会调用NetworkComponent的MessageDispatcher的Dispatch方法:

if (!(message is IResponse response))
			{
				this.Network.MessageDispatcher.Dispatch(this, opcode, message);
				return;
			}

如果是外网组件,则调用的OuterMessageDispatcher的Dispatch。

1.OuterMessageDispatcher的DispatchAsync方法

先看代码:

public async ETVoid DispatchAsync(Session session, ushort opcode, object message)
		{
			// 根据消息接口判断是不是Actor消息,不同的接口做不同的处理
			switch (message)
			{
				case IActorLocationRequest actorLocationRequest: // gate session收到actor rpc消息,先向actor 发送rpc请求,再将请求结果返回客户端
				{
					long unitId = session.GetComponent<SessionPlayerComponent>().Player.UnitId;
					ActorLocationSender actorLocationSender = await Game.Scene.GetComponent<ActorLocationSenderComponent>().Get(unitId);

					int rpcId = actorLocationRequest.RpcId; // 这里要保存客户端的rpcId
					long instanceId = session.InstanceId;
					IResponse response = await actorLocationSender.Call(actorLocationRequest);
					response.RpcId = rpcId;

					// session可能已经断开了,所以这里需要判断
					if (session.InstanceId == instanceId)
					{
						session.Reply(response);
					}
					
					break;
				}
				case IActorLocationMessage actorLocationMessage:
				{
					long unitId = session.GetComponent<SessionPlayerComponent>().Player.UnitId;
					ActorLocationSender actorLocationSender =await Game.Scene.GetComponent<ActorLocationSenderComponent>().Get(unitId);
					actorLocationSender.Send(actorLocationMessage).Coroutine();
					break;
				}
				case IActorRequest actorRequest:  // 分发IActorRequest消息,目前没有用到,需要的自己添加
				{
					break;
				}
				case IActorMessage actorMessage:  // 分发IActorMessage消息,目前没有用到,需要的自己添加
				{
					break;
				}
				default:
				{
					// 非Actor消息
					Game.Scene.GetComponent<MessageDispatcherComponent>().Handle(session, new MessageInfo(opcode, message));
					break;
				}
			}
		}

代码有些长,一步步分析:

  1. 首先这是个异步无返回值的方法,意味着可以异步await调用,但貌似没看到await调用的地方,走的是同步调用。
  2. 首先传入的参数是:一个session,一个协议号,一个协议数据。
  3. 根据消息内容做不同处理,首先是IActorLocationRequest类消息。个人理解:这类消息主要是客户端向Map等服务发送消息时,都是走的Gate转发,之前也提到了MAP并没有外网组件,因此与Map等服务进行通讯时,都使用的Gate服务进行转发。这里需要了解ET的ACTOR机制,下篇单独来梳理一下ACTOR机制,如果想提前了解的话,可以参考ET的BOOK,ETBOOK下面两篇文章看看在这里插入图片描述

可以理解为ET可以分为多进程服务,当客户端向Map发消息时,他发送一个Actor消息,这样当Gate接收到这个消息后,通过Actor的方式向Map发消息并等待回复,然后再回复给客户端。具体等下篇来说明

  1. IActorLocationMessage actorLocationMessage:与上面一样,下篇详细梳理,这里理解为不需要带回复消息的actor类消息,只负责发送就好。
  2. IActorRequest actorRequest,与IActorMessage actorMessageET并没有使用,也归到Actor消息里面下篇记录
  3. 非Actor消息,理解为在本服务中,直接处理调用到服务的MessageDispatcherComponent的Handle进行处理。
  4. Handle方法,根据传入的消息类型协议号,得到所有相关协议的处理类实例,然后调用他们的Handle即完成了一次对协议数据的处理。

这里需要区分的是两种协议类型,一种是带返回的,一种是不带返回的。不带返回的,才能在session中进行派发,走到派发器。如果是返回数据,则肯定之前是有回调的处理,则交给回调处理了。

2.NetInnerComponent

NetInnerComponent比NetOuterComponent组件要多一些处理,即Dictionary<IPEndPoint, Session> adressSessions的处理。内网组件用于处理各个服务之间的通信,他们的session很少发生变化,所以通过adressSessions,可以做一些缓存处理。通过Session Get(IPEndPoint ipEndPoint),要么从缓存中拿到对应服务连接的Session,要么通过Create的方式主动连接对方的服务。

public override void Load(NetInnerComponent self)
		{
			self.MessagePacker = new MongoPacker();
			self.MessageDispatcher = new InnerMessageDispatcher();
		}

可以看到内网使用的序列化方式与外网不同,使用的MongoPacker进行序列化与反序列化。这样做的好处是方便对数据进行持久化处理,如果是通过proto的话,可能还需要二次处理(个人理解),最典型的就是将一个map服上的unit(玩家游戏对象)迁移到另外一个Map服时,可直接将unit对象本身给序列化传给另外的Map服上,这样就太方便了。
注意点:当使用mongo序列化的时候,可以使用[BsonIgnore]对字段进行忽略。这里有个重要的特性就是如下代码,所有component会忽略InstanceId字段,当使用Bson反序列化时,会调用component的无参构造函数对InstanceId重新赋值。这样当将一个对象迁移到另外一个服务(进程)上时,会重新给InstanceId赋值,这样保证了InstanceId在新的服上是唯一的。然后发送ACTOR消息时就需要通过LOCATION的方式,进行定位。

public abstract class Component : Object, IDisposable
	{
		[BsonIgnore]
		public long InstanceId { get; private set; }

1.OuterMessageDispatcher的Dispatch

public void Dispatch(Session session, ushort opcode, object message)
		{
			// 收到actor消息,放入actor队列
			switch (message)
			{
				case IActorRequest iActorRequest:
				{
					HandleIActorRequest(session, iActorRequest).Coroutine();
					break;
				}
				case IActorMessage iactorMessage:
				{
					HandleIActorMessage(session, iactorMessage).Coroutine();
					break;
				}
				default:
				{
					Game.Scene.GetComponent<MessageDispatcherComponent>().Handle(session, new MessageInfo(opcode, message));
					break;
				}
			}
		}

可以看到与外网组件不同,内网派发组件Dispatch并不是异步支持的。
内网派发组件,确认所有消息都是服务器内部之间进行的通信,所以处理比较简单。
1.IActorRequest类型的消息,调用HandleIActorRequest(Session session, IActorRequest message)进行处理。首先通过CoroutineLockComponent锁定Actor,这个也放到下篇详细分析。
2.IActorMessage类型消息,也与上面相同放到下篇。
3.其他消息同外网派发组件,交给MessageDispatcherComponent直接处理。

总结

本篇简单说了下内网与外网组件,其中核心设计,都是对MessagePacker即协议解析类,以及MessageDispatcher消息派发类进行实例化,不同的是内网组件会缓存各个服务模块IP对应的session。
不同的MessageDispatcher类型,核心思想都是通过消息类型进行派发。外网OuterMessageDispatcher消息类型分为:IActorLocationRequest,IActorLocationMessage以及其他类型,ActorLocation类型基本上都是需要转发处理的类型,而且需要定位系统的帮助。而内网InnerMessageDispatcher类型,都是处理内部服务通信服务协议的,所以分为IActorRequest,IActorMessage,以及其他类型。
其中其他类型都是交给自己这边的MessageDispatcherComponent进行直接处理,IActorLocationRequest,IActorLocationMessage,IActorRequest,IActorMessage放到下篇进行详细讨论。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值