在LMAX Disruptor中的后续消费者之间传递数据(从非商业逻辑到商业逻辑)?

时间:2017-01-18 14:44:26

标签: disruptor-pattern lmax

https://martinfowler.com/articles/lmax.html中所述,我需要首先使用Unmarchaler然后使用Business Logic Processor处理我的RingBuffer事件。假设它配置为(https://lmax-exchange.github.io/disruptor/docs/com/lmax/disruptor/dsl/Disruptor.html

  Disruptor<MyEvent> disruptor = new Disruptor<MyEvent>(MyEvent.FACTORY, 32, Executors.newCachedThreadPool());
  EventHandler<MyEvent> handler1 = new EventHandler<MyEvent>() { ... };
  EventHandler<MyEvent> handler2 = new EventHandler<MyEvent>() { ... };
 disruptor.handleEventsWith(handler1);
 disruptor.after(handler1).handleEventsWith(handler2);

然后想法是handler1是unmarchaler而handler2使用handler1处理的东西。

问题:我如何准确地编码&#34;解开并重新打破破坏者&#34;部分?我发现了https://groups.google.com/forum/#!topic/lmax-disruptor/q6h5HBEBRUk这个解释,但我并不太明白。假设事件是在 handler1

的回调时到达的
void onEvent(T event, long sequence, boolean endOfBatch) 

(javadoc:https://lmax-exchange.github.io/disruptor/docs/com/lmax/disruptor/EventHandler.html

从游戏中取消一些数据。现在我需要将未经匹配的数据附加到 handler2 的事件中,该事件将处理未经处理的对象。

需要做什么才能更新&#34;事件?正在修改&#34;事件&#34;对象够了吗?

1 个答案:

答案 0 :(得分:2)

这种影响实际上取决于您的特定情况,而且一如既往,如果您处于低延迟之后,您应该尝试两者并进行基准测试。

最直接的是更新“事件”对象,但是根据您的特定方法,这可能会错过破坏者的许多单一作者的好处。我会解释并提供一些选择。

假设您有handler1和handler2,handler1在thread1中运行,handler2在thread2中运行。初始事件发布者位于thread0。

  • Thread0将条目写入插槽1的缓冲区
  • Thread1读取插槽1中的条目并写入插槽1
  • Thread0将一个条目写入第2个插槽的缓冲区
  • Thread2从插槽1读取并写入输出
  • Thread1读取插槽2中的条目并写入插槽2
  • Thread2从插槽2读取并写入输出

如果您考虑物理内存布局,则slot1和slot2有望在内存中彼此相邻。例如,它们可以是字节数组的某个子集。正如您所看到的,您正在从不同的线程(可能是不同的cpu内核)中读取和写入非常相邻的内存块,这可能导致错误的共享/缓存行反弹。最重要的是,您通过内存进行的读写操作可能不是线性的,因此您将错过CPU缓存的一些优点。

其他一些可能更好的选择:

  • 有单独的ringbuffers,其中第一个ringbuffer是原始数据,第二个ringbuffer是unmarshalled事件。这样,数据在存储器中被充分分离以避免这些成本。但是这会产生带宽影响。

  • 让unmarshaller和工作直接在同一个处理程序中完成。根据unmarshaller和处理程序中的工作量,这可能是可行的。