All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.zeromq.ZDispatcher Maven / Gradle / Ivy

The newest version!
package org.zeromq;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Dispatcher for ZeroMQ Sockets.
 *
 * Warning:
 * The Dispatcher uses a busy spin loop when waiting on events.
 * This is ideal for low latency applications but not in all situations.
 * It has the side effect of consuming 100% of a CPU when waiting for events.
 *
 * With this dispatcher, you can register ONE handler per socket 
 * and get a Sender for sending ZMsg.
 */
public class ZDispatcher {
    private ConcurrentMap dispatchers = new ConcurrentHashMap();
    private final ExecutorService dispatcherExecutor;

    public ZDispatcher() {
        this.dispatcherExecutor = Executors.newCachedThreadPool();
    }

    public ZDispatcher(ExecutorService dispatcherExecutor) {
        this.dispatcherExecutor = dispatcherExecutor;
    }

    public void registerHandler(ZMQ.Socket socket, ZMessageHandler messageHandler, ZSender sender) {
        registerHandler(socket, messageHandler, sender, Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()));
    }

    public void registerHandler(ZMQ.Socket socket, ZMessageHandler messageHandler, ZSender sender, ExecutorService threadpool) {
        SocketDispatcher socketDispatcher = new SocketDispatcher(socket, messageHandler, sender, threadpool);
        if (dispatchers.putIfAbsent(socket, socketDispatcher) != null) {
            throw new IllegalArgumentException("This socket already have a message handler");
        }
        socketDispatcher.start();
        dispatcherExecutor.execute(socketDispatcher);
    }

    public void unregisterHandler(ZMQ.Socket socket) {
        SocketDispatcher removedDispatcher = dispatchers.remove(socket);
        if (removedDispatcher == null) {
            throw new IllegalArgumentException("This socket doesn't have a message handler");
        }
        removedDispatcher.shutdown();
    }

    public void shutdown() {
        dispatcherExecutor.shutdown();
        for (SocketDispatcher socketDispatcher : dispatchers.values()) {
            socketDispatcher.shutdown();
        }
        dispatchers.clear();
    }

    public interface ZMessageHandler {

        public void handleMessage(ZDispatcher.ZSender sender, ZMsg msg);

    }

    public final static class ZSender {
        private final BlockingQueue out = new LinkedBlockingQueue();

        public final boolean send(ZMsg msg) {
            return out.add(msg);
        }
    }

    private static final class SocketDispatcher implements Runnable {
        private volatile boolean active = false;
        private final CountDownLatch shutdownLatch = new CountDownLatch(1);
        private final ZMQ.Socket socket;
        private final ZMessageHandler handler;
        private final ZSender sender;
        private final ExecutorService threadpool;
        private final BlockingQueue in = new LinkedBlockingQueue();
        private static final int BUFFER_SIZE = 1024;
        private static final ThreadLocal messages = new ThreadLocal() {
            @Override
            protected ZMessageBuffer initialValue() {
                return new ZMessageBuffer();
            }
        };
        private final AtomicBoolean busy = new AtomicBoolean(false);

        public SocketDispatcher(ZMQ.Socket socket, ZMessageHandler handler, ZSender sender, ExecutorService handleThreadpool) {
            this.socket = socket;
            this.handler = handler;
            this.sender = sender;
            this.threadpool = handleThreadpool;
        }

        public void run() {
            while (active) {
                doReceive();
                doHandle();
                doSend();
            }
            threadpool.shutdown();
            shutdownLatch.countDown();
        }

        public void start() {
            this.active = true;
        }

        public void shutdown() {
            try {
                this.active = false;
                this.shutdownLatch.await();

            } catch (InterruptedException e) {
            }
        }

        private void doReceive() {
            ZMsg msg;
            int remainingBuffer = BUFFER_SIZE;
            while (active && remainingBuffer-- > 0 && (msg = ZMsg.recvMsg(socket, ZMQ.DONTWAIT)) != null && msg.size() > 0 && msg.getFirst().hasData()) {
                in.add(msg);
            }
        }

        private void doHandle() {
            if (!in.isEmpty() && busy.compareAndSet(false, true)) {
                threadpool.submit(new Runnable() {
                    @Override
                    public void run() {
                        ZMessageBuffer messages = SocketDispatcher.this.messages.get();
                        messages.drainFrom(in);
                        busy.set(false);
                        for (int i = 0; i <= messages.lastValidIndex; i++) {
                            if (active) {
                                handler.handleMessage(sender, messages.buffer[i]);
                            }
                        }
                    }
                });
            }
        }

        private void doSend() {
            ZMsg msg;
            int remainingBuffer = BUFFER_SIZE;
            while (active && remainingBuffer-- > 0 && (msg = sender.out.poll()) != null) {
                msg.send(socket);
            }
        }

        private static class ZMessageBuffer {
            private final ZMsg[] buffer = new ZMsg[BUFFER_SIZE];
            private int lastValidIndex;

            private void drainFrom(BlockingQueue in) {
                int lastIndex = lastValidIndex = -1;
                ZMsg msg;
                while (++lastIndex < buffer.length && (msg = in.poll()) != null) {
                    buffer[lastIndex] = msg;
                    lastValidIndex = lastIndex;
                }
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy