
截取自github的commit提交:
服务端增加类似erlang的Actor分布式消息模型,任何一个实体对象挂上ActorComponent组件,其它任何进程只需要知道实体对象的id都可以对其发送消息。实体对象位置完全是透明的,并且对象可以十分方便的在进程中迁移。
erlang中的actorid是processid,保证是唯一的。与逻辑无关。ET的改动保证与erlang一致。
ActorId就是消息接收者的InstanceId,发消息的时候会设置到消息中去
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)就能发给对方。
注意!
Actor模型是纯粹的服务端消息通信机制,跟客户端是没什么关系的,很多用ET的新人看到ET客户端消息也有Actor接口,以为这是客户端跟服务端通信的机制,其实不是的。ET客户端使用这个Actor完全是因为Gate需要对客户端消息进行转发,我们可以正好利用服务端actor模型来进行转发,所以客户端有些消息也是继承了actor的接口。
形象比喻:
ET的actor跟actor location的比喻
中国有很多城市(进程),城市中有很多人(entity对象)居住,每个人都有身份证号码(Entity.Id)。一个人每到一个市都需要办理居住证,分配到唯一的居住证号码(InstanceId),居住证号码的格式是2个字节市编号+4个字节时间+2个字节递增。身份证号码是永远不会变化的,但是居住证号码每到一个城市都变化的。 现在有个中国邮政(actor)。
假设小明要发信给女朋友小红
小红为了收信,自己必须挂载一个邮箱(MailboxComponent),小红收到消息就会处理。注意这里处理是一个个进行处理的。有可能小红会同时收到很多人的信。但是她必须一封一封的信看,比方说小明跟小宝都发了信给小红,小红先收到小明的信,再收到了小宝的信。小红先读小明的信,小明信中让小红给外婆打个电话(产生协程)再给自己回信,注意这期间小红也不能读下一封信,必须打完电话后才能读小宝的信。当然小红自己可以选择不处理完成就开始读小宝的信,做法是小红开一个新的协程来处理小明的信。
假设小明知道小红的居住证号码,那么邮政(actor)可以根据居住证号码头两位找到小红居住的城市(进程),然后再根据小红的居住证编号,找到小红,把消息投递到小红的邮箱(MailboxComponent)中。这种是最简单的原生的actor模型
ET还支持了一套actor location机制。假设小明不知道小红的居住证号码,但是他知道小红的身份证号码,怎么办呢?邮政开发了一套高级邮政(actor location)想了一个办法,如果一个人经常搬家,它还想收到信,那他到一个新的城市都必须把自己的居住证跟身份证上报到中央政府(location server),这样高级邮政能够通过身份证号码来发送邮件。方法就是去中央政府拿到小红的居住证号码,再利用actor机制发送。
假设小红之前在广州市,小明用小红的身份证给小红发信件了。 高级邮政获取了小红的居住证号码,给小红发信。发信的这个过程中,小红搬家了,从广州搬到了深圳,这时小红在中央政府上报了自己新的居住证。 高级邮政的信送到到广州的时候发现,小红不在广州。那么高级邮政会再次去中央政府获取小红的居住证,重新发送,有可能成功有可能再次失败,这个过程会重复几次,如果一直不成功则告诉小明,信件发送失败了。
高级邮政发信比较贵,而且人搬家的次数并不多,一般小明用高级邮政发信后会记住小红的居住证,下次再发的时候直接用居住证发信,发送失败了再使用高级邮政发信。
高级邮政的信都是有回执的,有两种回执,一种回执没有内容,只表示小红收到了信,一种回执带了小红的回信。小明在发信的时候可以选择使用哪种回执形式。小明给小红不能同时发送两封信,必须等小红的回执到了,小明才能继续发信。
々风恒¢:
一个actor消息流转(拿demo里的Frame_***消息举例)
MailBoxComponent=>
MailboxDispatcherComponent=>
MailboxMessageDispatcherHandler=>
ActorMessageDispatcherComponent=>
IMActorHandler
我的理解是,给Unit插个邮筒,network给这个Unit的邮筒推消息,会自动被邮递员(MailboxDispatcherComponent)分发出去,根据邮筒类型发给对应的邮箱(IMailboxHandler)
Map端的Unit加的是分发类型的邮筒,所以给的是MailboxMessageDispatcherHandler
然后继续根据actor消息的类型,再转发给对应的IMActorHandler
我的问题是
1.为什么要设计成给Unit加邮筒,为什么不是GateSession/Player,就变成每个连接一定要有个Unit用来承载这个消息流转
我说下我的理解,每个Unit都可以有自己的邮筒,用来接收针对这个Unit的消息,这样就实现了客户端可以指定给某个Unit发消息,但想想又不太对,因为要指定给某个Unit,带个id上来,Map端简单Get下就能取到了
我自己已经改成PlayerId来挂载邮筒了,现在只是想知道这个设计有什么特别的原因
2.为什么邮筒不直接对应IMActorHandler,即,把IMailboxHandler和IMActorHandler合并
这里继续分层的理由是啥?
@熊猫
【群主】熊猫:
@ 々风恒¢ 是可以player挂邮箱啊,谁都可以挂邮箱,你想让谁接收消息就让谁挂邮箱就行了

初见:
ET的Actor大体分为两种:
1、Actor的位置会随时变动,比如玩家,有可能打副本,有可能在地图A,地图B,这样的Actor需要有一个地方存放玩家最新的位置,这个就是Location服务器了,每次发送Actor服务器会向Location找到最新地址后,然后再向目标服务器发送,也就是要用AMActorLocationHandler。
2、Actor位置不会变动,比如公会,因为分布式,有可能会把公会放在其他服务器,当你访问公会的时候,会自动转发到公会服务器,因为这个不可能会随便变动Actor位置的,所有不需要Location服务器记录,转发服务器记录下就可以了,所以这个要用AMActorHandler。
根据你这个Actor会不会变动网络位置,来决定是用AMActorLocationHandler或者AMActorHandler
ActorLocation可以理解成一个Actor的位置,当一个玩家在map1服务器,所有人都能他联系,但有一种情况,这个玩家突然去打某一个副本了,这时候这个玩家可能就在copy1服务器。
这种情况下,如果玩家2给玩家1发送消息,就需要知道ActorLocation的位置,然后消息发送到cpoy1里。这样玩家1就可以收到消息了
ET的Actor跟你传统的Actor不同,你就理解为这个Actor是记录玩家的位置,有这个位置,就你可以通过Actor给这个玩家发送消息了。
ET6.0就是这样的了,没有了基本的IRequest了。全都是Actor消息了,因为都会发送到某一个domian上了。
聊天记录
服务器接收客户端的Actor消息, 从Message中 该拿取哪个ID?
熊猫: 要拿UnitId (componentWith.Id), 不建议拿ActorId(客户端可以造假,可能发送到别的unit上去)
