Detailed Disruptor a

This blog will be mainly through a few examples, about the Disruptor simple to use;

A, disruptor Profile

Disruptor is a British foreign exchange LMAX company developed a lock-free framework for high-performance message passing between threads. Now includes Apache Storm, Camel, Log4j2 other well-known projects are using the Disruptor;

Because a very important structure in the Disruptor RingBufferand JDK are ArrayBlockingQueuevery similar, which is an internal circular array, so they often put them together, following is the official website announced the test results

From the figure you can see a huge difference in performance between them obvious;

Further differences can be seen in the performance of the project using Disruptor, for example Log4j

Which Loggers all asyncuses Disruptor, Async Appenderuses ArrayBlockingQueue, Syncsynchronous mode; you can see from the figure, the more the more intense competition in the thread when Disruptor more obvious performance advantages, the reason it is very easy to think, because of ArrayBlockingQueue out by the same lock control, so the competition has a huge impact on their performance;

Also my laptop configured to "i7-8550U 8G", the version used is:

<dependency>
  <groupId>com.lmax</groupId>
  <artifactId>disruptor</artifactId>
  <version>3.4.2</version>
</dependency>

Two, ArrayBlockingQueue performance comparison

By the following demo, demo Disruptor basic usage of a single-threaded, and a ArrayBlockingQueue make a simple comparison;

public class Contrast {
  public static final int count = 50000000;
  public static final int size = 1024;
  private static CountDownLatch latch = new CountDownLatch(1);

  public void testDisruptor() throws InterruptedException {
    long start = System.currentTimeMillis();
    final Disruptor<Event> disruptor = new Disruptor<>(
        () -> new Event(),             // 绑定事件工厂,主要用于初始化 RingBuffer
        size,                          // RingBuffer 大小
        DaemonThreadFactory.INSTANCE,  // 指定生产者线程工厂,也可以直接传入线程池
        ProducerType.SINGLE,           // 指定生产者为单线程,也支持多线程模式
          new YieldingWaitStrategy()   // 等待策略
//        new BlockingWaitStrategy()
    );

    Handler handler = new Handler();
    disruptor.handleEventsWith(handler);  // 绑定事件处理程序
    disruptor.start();

    RingBuffer<Event> ringBuffer = disruptor.getRingBuffer();  // 开始之后 RingBuffer 的所有位置就已经初始化完成
    for (int i = 0; i < count; i++) {
      long seq = ringBuffer.next();       // 获取下一个放置位置
      Event event = ringBuffer.get(seq);  // 等到指定位置的槽
      event.seId(i);                      // 更新事件,注意这里是更新,不是放入新的,所以不会有 GC 产生
      ringBuffer.publish(seq);            // 发布事件
    }

    latch.await();
    System.out.println("time: " + (System.currentTimeMillis() - start));
  }

  private void testQueue() throws InterruptedException {
    long start = System.currentTimeMillis();
    final BlockingQueue<Event> queue = new ArrayBlockingQueue<>(size);
    new Thread(() -> {
      for (int i = 0; i < count; i++) {
        try {
          queue.put(new Event(i));
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }).start();

    new Thread(() -> {
      for (int i = 0; i < count; i++) {
        try {
          Event event = queue.take();
          if (i == count - 1) {
            System.out.println("last: " + event.getLogId());
            latch.countDown();
          }
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }).start();

    latch.await();
    System.out.println("time: " + (System.currentTimeMillis() - start));
  }

  class Event {
    private long id;
    Event() {}
    Event(long id) { this.id = id; }
    public long getLogId() { return id; }
    public void seId(int id) { this.id = id; }
  }

  class Handler implements EventHandler<Event> {
    private int i = 0;

    @Override
    public void onEvent(Event event, long seq, boolean bool) {
      if (++i == count) {
        System.out.println("last: " + event.getLogId());
        latch.countDown();
      }
    }
  }

  public static void main(String[] args) throws InterruptedException {
    Contrast contrast = new Contrast();
    contrast.testDisruptor();
//    contrast.testQueue();
  }
}

Disruptor-YieldingWaitStrategy: 919
Disruptor-BlockingWaitStrategy: 3142
ArrayBlockingQueue : 4307

Which BlockingWaitStrategy waiting strategy and ArrayBlockingQueue approximately acquaintance

Third, more consumers

The above example using a plurality of time consumption, there will be a repetitive consumer, if the consumer wants a message only once, with reference to the following code:

public class MoreConsumer {
  public static final int count = 5000;
  public static final int size = 16;

  public void testDisruptor() {
    long start = System.currentTimeMillis();
    final Disruptor<Event> disruptor = new Disruptor<>(
        () -> new Event(),
        size, DaemonThreadFactory.INSTANCE,
        ProducerType.SINGLE,
        new BlockingWaitStrategy()
    );

    disruptor.handleEventsWithWorkerPool(new Handler("h1"), new Handler("h2"), new Handler("h3"));
    disruptor.start();

    RingBuffer<Event> ringBuffer = disruptor.getRingBuffer();
    for (int i = 0; i < count; i++) {
      long seq = ringBuffer.next();
      Event event = ringBuffer.get(seq);
      event.id = i;
      ringBuffer.publish(seq);
    }

    System.out.println("time: " + (System.currentTimeMillis() - start));
  }

  class Event { public long id; }

  class Handler implements WorkHandler<Event> {
    private String name;

    Handler(String name) { this.name = name; }
      
    @Override
    public void onEvent(Event event) { System.out.println(name + ": " + event.id); }
  }

  public static void main(String[] args) {
    MoreConsumer moreConsumer = new MoreConsumer();
    moreConsumer.testDisruptor();
  }
}

As shown in the code above uses WorkHandlercan be, but also need to pay attention to choose to wait strategies, different strategies may also cause problems with repeated consumption, but the official website is also a need to ensure that duplication of spending only a problem in the code inside;

Fourth, complex business logic

Many business logic also occur following a similar situation, a third of consumers, need to wait for the situation after the completion of the task ahead is to continue; we usually use the lock, synchronization tools and other ways, but they all seemed more trouble, and efficiency is relatively low, if we use the Disruptor here can be very convenient solution;

disruptor.handleEventsWith(c1Handler, c2Handler);
disruptor.after(c1Handler, c2Handler).handleEventsWith(c3Handler);

Thus only two lines of code, will be able to clearly express the relationship above, also for more complex situations;

For more tips you will need to analyze the actual situation, the next blog will mainly analyze Disruptor why so fast;

Guess you like

Origin www.cnblogs.com/sanzao/p/11164967.html