Evan Chan分享的《Akka in Production: Our Story》是一篇非常务实的工程实践分享,里面有很多可借鉴的点子,其中的一个对消息接受的包装逻辑主要预留了扩展点:
trait ActorStack extends Actor {
def wrappedReceive: Receive // Receive类型是一个偏函数
def receive: Receive = {
case x => if (wrappedReceive.isDefinedAt(x)) wrappedReceive(x) else unhandled(x)
}
}
大部分人在实现业务逻辑时可能如下
class MyActor extends ActorStack {
def wrappedReceive: Receive = {
case Something => balabala
}
}
这种情况下会有个小小的性能浪费,每次接收消息的时候,至少要创建一次偏函数对象。如果消息又正好在wrappedReceive
里定义了的话,创建了2次偏函数对象,因为调用了2次wrappedReceive
方法;这个是可以避免的,用一个成员把这个偏函数对象保存起来,避免每次都创建:
trait ActorStack extends Actor {
def wrappedReceive: Receive
private val logic = wrappedReceive
def receive: Receive = {
case x => if (logic.isDefinedAt(x)) logic(x) else unhandled(x)
}
}
或许看客们又好奇,那么Akka对receive
这个函数返回的偏函数又是怎么处理的?是否也存在每次都生产一下(同样的性能浪费),还是保存在某个地方? 没错,它在底层实现里是被放到behaviorStack
这个行为栈里的,每次处理逻辑的时候从行为栈里取出这个偏函数进行调用。