游戏服务器框架 Actor模型
Actor模型是什么?
在计算机科学中,演员模型(英语:Actor model)是一种并发运算上的模型。“演员”是一种程序上的抽象概念,被视为并发运算的基本单元:当一个演员接收到一则消息,它可以做出一些决策、建立更多的演员、发送更多的消息、决定要如何回答接下来的消息。演员可以修改它们自己的私有状态,但是只能通过消息间接的相互影响(避免了基于锁的同步)。1
按我的理解,每个Actor是独立的计算单元,可并行运行。Actor之间可以发消息互相通信。彼此之间没有共享状态,避免了锁竞争。在CPU频率涨幅变慢,但核数越来越多下,非常适合构建大型分布式系统。
在实现分布式Actor模型时,我遇到了以下问题。
ActorId如何设计?
ActorId的设计涉及到如何寻址。在分布式环境下,ActorId要通过解析,最终能路由到一个进程中的唯一Actor。
例如:
var player = cluster.GetActor<PlayerActor>("player-123");
player.Tell(new Login());
ActorId就是标识符,不需要考虑Actor的物理位置。
一些框架把进程号/线程号、时间戳合在一起,生成一个ActorId。通过进程号即可从配表里拿到IP和端口,即可给对应进程发消息,进程再去分发消息到对应的Actor。
游戏服务器中,如果玩家是Actor。小服情况下,一般一个服的玩家在同一个进程,分发消息时ActorId一定能路由到同一个进程。
大服情况下,ActorId里。。。。。。。。。。。。。。
未完待续
Actor的调度
Actor模型需要保证顺序处理邮箱中的消息,调度的粒度不应该是消息,而是一整个Actor。
假如一个Actor的消息堆积过多,还会造成其他Actor的消息不能及时处理,造成Actor饥饿。
有一种任务窃取的调度器,将Actor绑定到线程。Actor的执行任务使用双端队列来存储,工作线程从头获取Actor,窃取线程从尾部获取Actor。
这个方法需要处理竞争问题,当窃取线程越多,队列尾部竞争越大,反而会对CPU造成资源浪费。常见解决方法当没有窃取到任务,则等待更长的时间。
窃取线程也是工作线程转变的,工作线程处理完绑定自身的Actor的任务,去窃取其他工作线程的任务。如果在等待过程中,自身的Actor有消息需要处理,需要立刻打断这个等待。
但是实际上,假如一个玩家是一个Actor,根本不可能从玩家的消息队列的队尾去先执行后接收的消息,这样消息会无序。所以游戏服务器中并不能在Actor粒度是玩家的情况下实现任务窃取。
其他
Orleans提出了虚拟Actor。 首先定义粗粒度的行为接口,之后便可以以本地调用的方式调取远程Actor的方法,非常方便。