繁体   English   中英

LMAX Disruptor作为阻塞队列?

[英]LMAX Disruptor as a blocking queue?

我有什么办法可以在一个结构中同时拥有两者-

  1. BlockingQueue的语义,即-非阻塞窥视,阻塞轮询和阻塞放置。 多个提供商一个消费者。
  2. RingBuffer有效地用作对象池,因此我不想在环形缓冲区中放置新对象,而是想在那里重复使用现有对象,复制状态。 因此,基本上,LMAX干扰器的功能是开箱即用的。

已经有类似的东西了吗? 我想我可以尝试并使用Disruptor,如果我理解正确的话,我已经可以将其用作带有阻塞put(如果环形缓冲区为“满”)的阻塞队列。 它已经具有我需要的“可重用对象”语义。 因此,唯一的问题是如何创建一个能够拉对象(而不是使用回调)的客户端,因此我对内部Disruptor结构并不十分熟悉-能做到吗? 使用所有这些音序器,创建新的EventProcessor还是类似的东西?

不,在客户端具有阻塞队列并从中获得阻塞队列的明显解决方案不是理想的解决方案,因为它破坏了使用干扰对象池的整个要点-您现在需要拥有一个新池,或者只是在放入该阻塞队列等之前在回调中创建一个新对象,而我根本不想创建任何垃圾。

那么有没有办法使用Disruptor或任何其他面向性能/无垃圾的Java库来实现呢?

我们在今年早些时候开源了Conversant Diruptor,其中包括DiruptorBlockingQueue。 您可以在github上找到代码

Conversant Disruptor几乎可以包含在几乎所有项目中,因为它支持BlockingQueue api,并且在Maven Central上发布。

出于好奇,我无法从Disruptor本身获取“ blocking pull”语义,但是向非阻塞pull添加“ blocking”功能当然是微不足道的。 “窥视”功能本身是可能的,但效率不高(您需要在每次窥视中一次又一次地复制项目),并且可以通过仅缓存“轮询”的结果来代替。

因此,最小的原始解决方案仅实现了我需要的方法:

public class DisruptorMPSCQueue<T extends ICopyable<T>> {

    private final RingBuffer<T> ringBuffer;
    private final EventPoller<T> eventPoller;
    private T tempPolledEvent;

    private EventPoller.Handler<T> pollerHandler = new EventPoller.Handler<T>() {
        @Override
        public boolean onEvent(final T event, final long sequence, final boolean endOfBatch) throws Exception {
            tempPolledEvent.copyFrom(event);
            return false;
        }
    };

    public DisruptorMPSCQueue(EventFactory<T> typeConstructor, int size) {
        ringBuffer = RingBuffer.createMultiProducer(typeConstructor, size);
        eventPoller = ringBuffer.newPoller();
        ringBuffer.addGatingSequences(eventPoller.getSequence());
    }

    /**
     * Blocking, can be called from any thread, the event will be copied to the ringBuffer
     */
    public void put(final T event) {
        long sequence = ringBuffer.next(); // blocked by ringBuffer's gatingSequence
        ringBuffer.get(sequence).copyFrom(event);
        ringBuffer.publish(sequence);
    }

    /**
     * Not blocking, can be called from any thread, the event will be copied to the ringBuffer
     *
     * @throws IllegalStateException if the element cannot be added at this time due to capacity restrictions
     */
    public void offer(final T event) {
        long sequence;
        try {
            sequence = ringBuffer.tryNext();
        } catch (InsufficientCapacityException e) {
            throw new IllegalStateException(e); // to mimic blockingQueue
        }
        ringBuffer.get(sequence).copyFrom(event);
        ringBuffer.publish(sequence);
    }

    /**
     * Retrieve top of the queue(removes from the queue). NOT thread-safe, can be called from one thread only.
     *
     * @param destination top of the queue will be copied to destination
     * @return destination object or null if the queue is empty
     */
    public T poll(final T destination) {
        try {
            tempPolledEvent = destination;  // yea, the poller usage is a bit dumb
            EventPoller.PollState poll = eventPoller.poll(pollerHandler);
            if (poll == EventPoller.PollState.PROCESSING) {
                return tempPolledEvent;
            } else {
                return null;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM