什么是actor
在上面一章大致提到了akka使用actor对象来处理任务。那么在真正深入学习akka之前,必须先知道什么是actor。actor在语言层面上来说,就是一个对象,但是从功能层面上来说,也可以把它看成一个容器,它包含了自己的状态,处理任务的行为,接收消息的邮箱,子actor以及对子actor的监管策略等内容。所有的这些东西都被封装在actor里面,而一个actor只能通过它的actor引用来访问。那么什么是actor引用呢?
actor引用
就像在java中每一个对象都通过对象引用来访问一样,actor引用也类似是对一个actor的引用。不过actor引用还有些不一样的地方。为了充分享受actor模型的好处,每一个actor对象都必须与外界隔离出来,actor引用就是这个actor对外的代言人,actor引用可以没有限制的被外界传递和使用。这种方式就像把actor分为内部对象个外部对象,这使得actor内部状态的改变对外界透明。比如一个actor对象重启了,但是这个actor对象的actor引用没有变,所以也不用通知其他使用这个actor的地方去更新对他的引用,再比如通过actor引用访问一个远程主机上的actor对象,甚至给不同的应用发送消息等等,这些都是与java普通对象引用不一样的地方。最重要的是,外部不需要知道一个actor对象的内部情况,除非这个actor非要把自己的内部信息发布出去。
actor状态
actor对象一般都会包含一些状态来反应它可能所处的状态,可能是一个明确的状态机,也可能是一个计数器,监听集合,等待中的请求等等。这些数据正是这个actor对象的价值所在,因此必须保护起来,以免被其他actor对象破坏。而akka恰恰在设计上就使得每一个actor对象都有自己独占的轻量级线程,使得自己完全与外界系统隔离。这种方式使得你不用小心翼翼地思考如何使用锁来同步访问,以及谨慎地考虑加锁引起的活跃性问题,性能问题等等,你只需要设计你的actor对象去完成你的功能就好了,完全不需要考虑并发问题。
akka可以同时在多个真正的线程上运行多个actor对象,而更一般的是多个actor对象共享一个线程,当然也可能一个actor在多个线程上运行。在前面这些场景中,akka都可以确保这些场景的实现细节就像在单线程环境下一样。
由于actor的内部状态对它来说是非常重要的,所以状态的不一致是非常致命的,因此当一个actor运行失败或者被他的监护者重启了,它的状态会跟第一次创建的值一样,这也使得系统有较强的自愈性。
另一个可供选择的是,actor可以自动恢复到重启之前接收到的消息,并在重启之后再继续处理他们。
行为
当一个消息与某个actor的当前行为匹配时,该actor才能处理这个消息。这里行为的意思就是定义的一个处理消息的方法函数。这些行为可能随着时间而变化,比如不同的客户端在不同时间获得的权限不一样,因此行为也可能随之改变。在或者actor在某个时间“不在服务区”,后面有重启回来了。这些改变要么编码进状态变量中,以供行为逻辑读取,要么把行为函数封装进运行时方法,比如“become”和“unbecome”方法。但是,actor对象构造期间定义的初始行为比较特殊,actor在重启时会重置它的行为到初始状态。
邮箱
actor的目的是为了处理消息的,消息从一个actor发送给另一个actor,actor之间的通信也是通过消息来传递的。所以每个actor都有一个邮箱,所有往这个actor传递的信息都放到这个邮箱中。默认情况下邮箱中的消息顺序是按照接收到的时间顺序的。多个actor往同一个actor发送消息,不能保证消息的顺序性,不过如果是一对一通信,那么接收到消息顺序可以保证是发送时的顺序。
默认的邮箱是FIFO的,这通常能满足大多数场景,但是如果你的应用需要优先处理某些优先级更高的消息,你可以使用优先级邮箱,接收到的消息不是排在末尾,而是根据优先级可以排序,可能在队列中的某个位置,设置可以排第一。
akka与其他的actor模型实现不一样的是:actor总是处理邮箱中的下一条消息,而不是扫描邮箱寻找满足匹配条件的消息。因此对不匹配的消息的处理被认为是失败,除非重写了这个行为。
子actor
每一个actor都可能是一个监护者,如果它创建了子actor来处理子任务,那么这个actor会自动监护他们。
子任务列表在actor的上下文context中,actor可以通过上下文访问他们。通过context.actorOf(...)可以创建自子actor,(context.stop(child)) 可以停止子actor。而实际上子actor的创建和停止是已异步的方式来处理的,因此这些行为不会阻塞父actor。
监护策略
每个actor都必须提供处理子actor错误的策略。在akka中错误处理是透明的,并且错误处理是akka框架中比较基础的一个模块。一旦一个actor被创建了,那么它的错误处理策略就不会了。akka提供几种监护和监控策略,后面的章节中将详细介绍。
考虑到对于每一个actor,只有一种监护策略。那么如果需要不同的策略使用于不同的子actor,那么应该把根据子任务把子actor分组,每一组子actor匹配一种策略。
actor终止
一旦一个actor需要终止,比如遇到了重启也不能解决的错误,自动停止,或者被监护者停止等,那么它将释放占用资源,并把他的邮箱中的信息转放到系统的“死信邮箱”中,邮箱中的信息也就变成了死信事件流。同时这个actor的actor引用中的邮箱将被系统邮箱替代,重定向所有发送给这个actor的信息为死信事件流。当然程序会努力去完成这些事情,但是也不能保证一定不会丢失信息,所以不要依赖于这种策略来构建不可丢消息的系统。