如何使用disruptor实现自定义actor邮箱?

时间:2015-01-06 08:04:58

标签: java akka disruptor-pattern

我对LMAX Disruptor有一些经验,我真的很想使用disruptor实现自定义actor邮箱。

有没有指导方针?它甚至可能吗? Akka的演员邮箱有哪些局限性?

1 个答案:

答案 0 :(得分:3)

正如它所说here你只需要实现一些方法 - 当然你应该使用指向环形缓冲区的指针直接写/读消息。你还应该记住:

  • 破坏者通常预先分配大量内存,因此每个玩家使用一个破坏者是个坏主意,你可以使用一个路由器actor(里面有disruptor)和BalancingPool

  • 如果你想要不同的消息类型消费,单独的消费者进行日记,修复等等 - 你应该将不同的RingBufferPointer(类似smthng)实例作为参数传递给你的邮箱(具有相同的日记记录起始值,不同的消息类型的不同起始值),但仍然使用一个Disruptor。所以不同的邮箱会引用一个破坏者。

  • 您将在消息创建,解压缩等方面失去低级别控制权,因此默认情况下不进行批量分配。

  • 您还可以使用响铃中的历史记录来恢复失败的演员的状态(在preRestart或主管中)。

LMAX说:

  

它以与传统方法不同的方式工作,所以你   使用它与你可能习惯的有点不同。例如,   将模式应用于您的系统并不像更换所有模式那么简单   你的队列与魔法环缓冲区。我们有代码示例   引导您,越来越多的博客和文章给出了概述   它是如何工作的,技术论文详细说明了你的情况   期待,性能测试给出了如何使用的例子   干扰器   http://mechanitis.blogspot.com/2011/06/dissecting-disruptor-whats-so-special.html

here是一个简短的队列/干扰器/演员比较

在伪scala代码中,它将类似于:

object MyUnboundedMailbox {
  val buffer = new RingBuffer()

  class MyMessageQueue(val startPointer: Pointer, readerPointer: Pointer, writerPointer: Pointer) extends MessageQueue {

    // these should be implemented; queue used as example
    def enqueue(receiver: ActorRef, handle: Envelope): Unit = {
      writerPointer.allocate(() => handle) //allocate one element and set, if you want different message types - you should allocate big amount of data before and block when it ends (to not interfere with another messages), so it has to be bounded queue then  

    }
    def dequeue(): Envelope = readerPointer.poll()
    def numberOfMessages: Int = writerPointer - readerPointer //should be synchronized
    def hasMessages: Boolean = readerPointer == writerPointer //should be synchronized
    def cleanUp(owner: ActorRef, deadLetters: MessageQueue) { }
  }

  trait MyUnboundedMessageQueueSemantics 

}

class MyUnboundedMailbox(settings: ActorSystem.Settings, config: Config) extends MailboxType
  with ProducesMessageQueue[MyUnboundedMailbox.MyMessageQueue] {

  import MyUnboundedMailbox._
  final override def create(owner: Option[ActorRef],
                            system: Option[ActorSystem]): MessageQueue = {

    val pointer = ring.newPointer
    val read = pointer.copy
    val write = pointer.copy
    new MyMessageQueue(pointer, read, write) 
  }
    // you may use another strategy here based on owner (you can access name and path here), 
    // so for example may allocate same pointers for same prefixes in the name or path 
}

您可以使用未更改的MyMessageQueue.startPointer在故障恢复期间访问消息日志(您也可以查看akka' s Event Sourcing进行类比)。

使用UnboundedQueue方法并不能保证此处的邮件传递,因为如果响铃结束,可能会使用新版本覆盖旧的未传递邮件,因此您可能需要BoundedQueue,如here