使用Disruptor 遇到問題,eventHander可能讀不到事件

abaddoncoder發表於2015-05-25
程式碼使用disruptor 3.2.1, 使用多生產者單消費者模式,目的是為把併發寫透過disruptor轉成單寫

剛開始沒問題, 執行一會後,publish event 後,evenHandler不會被呼叫了,不知是disruptor問題,還是我程式碼的問題,但我把disruptor 換成jdk的ArrayBlockingQueue,沒出現問題。定位了很久,沒找出原因,以下是程式碼,沒有多少,就兩個類,希望有disruptor 使用經驗的道友幫忙分析一下

啟動disruptor且釋出事件的類:

public class CommandDisruptorServiceImpl implements CommandHandleService {
    private static final EventTranslatorVararg<CommandEvent> TRANSLATOR = new EventTranslatorVararg<CommandEvent>() {
            @Override
            public void translateTo(CommandEvent event, long sequence,
                Object... args) {
                if (args.length >= 3) {
                    event.setCommandExecutor((CommandExecutor) args[0]);
                    event.setCommand((Command<?>) args[1]);
                    event.setCommandResult((CommandResult) args[2]);
                }
            }
        };

    private Logger log = Logger.getLogger(CommandDisruptorServiceImpl.class);
    private volatile Disruptor<CommandEvent> disruptor;
    private volatile boolean started = false;

    @Override
    public void start(int bufSize) {
        if (null == disruptor) {
            synchronized (this) {
                if (null == disruptor) {
                    disruptor = new Disruptor<CommandEvent>(new CommandEventFactory(),
                            bufSize,
                            Executors.newCachedThreadPool(new ThreadFactory() {
                                @Override
                                public Thread newThread(Runnable r) {
                                    Thread s = Executors.defaultThreadFactory()
                                                        .newThread(r);
                                    s.setName("command-handle-thread");
                                    s.setDaemon(true);
                                    return s;
                                }
                            }));

                    disruptor.handleEventsWith(new CommandEventHandler());
                    disruptor.start();
                    started = true;
                    log.info("disruptor started!");
                }
            }
        }
    }

    @Override
    public void stop() {
        if (disruptor != null) {
            disruptor.shutdown();
            started = false;
            disruptor = null;
            log.info("disruptor stopted");
        }
    }

    @Override
    public <T> T postCommandAndWaitResult(CommandExecutor next,
        Command<T> command) {
        final RingBuffer<CommandEvent> ringBuffer = disruptor.getRingBuffer();
        final CommandResult commandResult = new CommandResult();
        ringBuffer.publishEvent(TRANSLATOR, next, command, commandResult);
        log.info("Command pulish to ringbuffer! cursor:" +
            disruptor.getCursor());
        return waitCommandResult(commandResult, 2000);
    }

    @SuppressWarnings("unchecked")
    private <T> T waitCommandResult(CommandResult commandResult, long timeOut) {
        synchronized (commandResult) {
            try {
                while (!commandResult.isCommandIsFinish()) {
                    log.info("Command waiting for result!");
                    commandResult.wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        log.debug("commandResult:" + commandResult.getResult());
        return (T) commandResult.getResult();
    }

    @Override
    public boolean isStarted() {
        return started;
    }
}


<p class="indent">


eventHandler 類:

public class CommandEventHandler implements EventHandler<CommandEvent> {
    private Logger log = Logger.getLogger(CommandEventHandler.class);

    @Override
    public void onEvent(CommandEvent event, long sequence, boolean endOfBatch)
        throws Exception {
        log.info("enter commandHandler! sequence:" + sequence + " endOfBatch:" +
            endOfBatch);

        Command<?> command = event.getCommand();
        CommandExecutor cmdInterceptor = event.getCommandExecutor();
        final CommandResult commandResult = event.getCommandResult();

        try {
            commandResult.setResult(cmdInterceptor.execute(command));
        } finally {
            synchronized (commandResult) {
                commandResult.setCommandIsFinish(true);
                log.info("to notify wait command!");
                commandResult.notify();
            }
        }
    }
}

<p class="indent">


我也看了一下disruptor 中 BatchEventProcessor 的 run方法原始碼
onEvent 不被執行有兩種可能
1. sequenceBarrier.waitFor(nextSequence); 一直在等待
2. nextSequence <= availableSequence 這個條件一直不被滿足

run方法原始碼:

@Override
     /**
     * It is ok to have another thread rerun this method after a halt().
     *
     * @throws IllegalStateException if this object instance is already running in a thread
     */
    @Override
    public void run() {
        if (!running.compareAndSet(false, true)) {
            throw new IllegalStateException("Thread is already running");
        }

        sequenceBarrier.clearAlert();
        notifyStart();
        T event = null;
        long nextSequence = sequence.get() + 1L;

        try {
            while (true) {
                try {
            /**
             * onEvent 不被執行有兩種可能
             *1. sequenceBarrier.waitFor(nextSequence); 一直在等待
             *2. nextSequence <= availableSequence 這個條件一直不被滿足
             */
                    final long availableSequence = sequenceBarrier.waitFor(nextSequence);

                    while (nextSequence <= availableSequence) {
                        event = dataProvider.get(nextSequence);
                        eventHandler.onEvent(event, nextSequence,
                            nextSequence == availableSequence);
                        nextSequence++;
                    }

                    sequence.set(availableSequence);
                } catch (final TimeoutException e) {
                    notifyTimeout(sequence.get());
                } catch (final AlertException ex) {
                    if (!running.get()) {
                        break;
                    }
                } catch (final Throwable ex) {
                    exceptionHandler.handleEventException(ex, nextSequence,
                        event);
                    sequence.set(nextSequence);
                    nextSequence++;
                }
            }
        } finally {
            notifyShutdown();
            running.set(false);
        }
    }
<p class="indent">


[該貼被abaddoncoder於2015-05-25 19:10修改過]

[該貼被abaddoncoder於2015-05-25 19:15修改過]

[該貼被abaddoncoder於2015-05-25 19:19修改過]

[該貼被abaddoncoder於2015-05-25 19:21修改過]

相關文章