绝对全网首发,利用Disruptor EventHandler实现在多线程下顺序性执行任务

disruptor有两种事件处理器,一个是EventHandler ,另一个是WorkHandler.
EventHandler可以彼此独立消费同一个队列中的事件,WorkHandler可以共同竞争消费同一个队列中的事件。
举例说明:
前提:环形事件队列中有a、b、c、d三个事件。
如果disruptor中有两个EventHandler 类型的处理器eventHandler1eventHandler2,在这种情况下这两个处理器会分别独立去处理这4个事件,也就是说每个事件会被处理两次,一共处理8次事件。
而如果disruptor中两个WorkHandler类型的workHandler1workHandler2,在这种情况下那么每个任务只会被其中一个处理器处理,这两个处理器的处理事件数加起来是4次。workHandler1处理了a、c事件,那么workHandler2就只会处理b、d事件。
了解了这些,就可以能理解使用EventHandler为啥能实现多线程顺序处理了。
可以给事件定义一个hash函数,根据哈希取余的槽位下标和当前处理器的下标比较,判断出此事件是否应被当前的处理器处理,不该其处理的事件直接忽略掉,避免重复消费

定义的哈希函数接口和统一的基础父类

public interface Hashed<K> {

    /**
     * 哈希码计算所需key
     *
     * @return key值
     */
    K getKey();


    /**
     * 计算哈希码
     *
     * <p>
     *     默认实现是使用{@link  java.util.HashMap HashMap}的哈希计算公式</br>
     *     建议对此计算做个缓存,进一步提高性能
     * </p>
     *
     * @param key 哈希码生成的输入key,此入参是{@link #getKey()}方法的返回值
     * @return 哈希码
     * @see #getKey()
     */
    default int hash(K key) {
        if (key == null) {
            throw new NullPointerException("key must not be null");
        }
        int h;
        return (h = key.hashCode()) ^ (h >>> 16);
    }

}

public abstract class PartitionEventHandler<E extends Hashed<K>, K> implements EventHandler<E>, Cloneable {

    protected final Logger log = LoggerFactory.getLogger(getClass());

    private final static int NO_INIT_VALUE = -1;

    private int index;

    private int indexMask;

    public final int getIndex() {
        return index;
    }

    public final int getIndexMask() {
        return indexMask;
    }

    @Override
    public final void onEvent(E event, long sequence, boolean endOfBatch) throws Exception {
        if (index == NO_INIT_VALUE || indexMask == NO_INIT_VALUE) {
            throw new IllegalStateException("this should be inited");
        }
        int position = event.hash(event.getKey()) & indexMask;
        if (index != position) {
            if (log.isTraceEnabled()) {
                log.trace("The event with key [{}] should be handled on slot [{}],but current slot is [{}], it will be ignored.",
                        event.getKey(), position, index);
            }
            return;
        }
        if (log.isDebugEnabled()) {
            log.debug("The event with key [{}] is being handled on slot [{}].", event.getKey(), position);
        }
        doOnEvent(event, sequence, endOfBatch);

    }

    protected abstract void doOnEvent(E event, long sequence, boolean endOfBatch) throws Exception;

    @SafeVarargs
    public static <E extends Hashed<K>, K> PartitionEventHandler<E, K>[] initHandlers(final PartitionEventHandler<E, K>... handlers) {
        if (handlers == null) {
            throw new NullPointerException("handlers must not be null");
        }
        if (handlers.length == 0) {
            throw new IllegalArgumentException("handlers length must be more than zero");
        }
        if (Integer.bitCount(handlers.length) != 1) {
            throw new IllegalArgumentException("handlers count must be a power of 2");
        }
        int indexMask = handlers.length - 1;
        for (int i = 0; i < handlers.length; i++) {
            PartitionEventHandler<E, K> handler = handlers[i];
            handler.indexMask = indexMask;
            handler.index = i;
        }
        return handlers;
    }

    @Override
    public PartitionEventHandler<E, K> clone() {
        try {
            @SuppressWarnings("unchecked")
            PartitionEventHandler<E, K> handler = (PartitionEventHandler<E, K>) super.clone();
            handler.index = NO_INIT_VALUE;
            handler.indexMask = NO_INIT_VALUE;
            return handler;
        } catch (CloneNotSupportedException e) {
            //never occur
            throw new RuntimeException("can not clone " + getClass(), e);
        }
    }

    public PartitionEventHandler<E, K>[] clones(int count) {
        if (count < 0) {
            throw new IllegalArgumentException("count must be more then zero");
        }
        @SuppressWarnings("unchecked")
        PartitionEventHandler<E, K>[] result = new PartitionEventHandler[count];
        for (int i = 0; i < count; i++) {
            result[i] = clone();
        }
        return result;
    }
}

测试代码

public class DisruptorTest05 {

    @Test
    public void test1() {
        CustomizableThreadFactory threadFactory = new CustomizableThreadFactory("event-handler-");
        threadFactory.setDaemon(false);

        Disruptor<ChatGroupEvent> disruptor = new Disruptor<>(new ChatGroupEventFactory(), 1024, threadFactory);
        ChatGroupEventHandler handler = new ChatGroupEventHandler();
        PartitionEventHandler<ChatGroupEvent, String>[] handlers = handler.clones(8);
        disruptor.handleEventsWith(PartitionEventHandler.initHandlers(handlers));
        disruptor.start();
        RingBuffer<ChatGroupEvent> ringBuffer = disruptor.getRingBuffer();
        Random rdm = new Random();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 1000; i++) {
            long seq = ringBuffer.next();
            ChatGroupEvent chatGroupEvent = ringBuffer.get(seq);
            int rdmInt = rdm.nextInt(12);
            chatGroupEvent.setGroupId(String.format("groupId-%02d", rdmInt));
            chatGroupEvent.setGroupOwner(String.format("owner-%03d", i));
            String type = i % 2 == 0 ? "add" : "exit";
            chatGroupEvent.setChangeType(type);
            ringBuffer.publish(seq);
        }

        disruptor.shutdown();
        long spent = System.currentTimeMillis() - start;
        System.out.println("total time " + spent);
        System.out.println();
    }
    static class ChatGroupEventFactory implements EventFactory<ChatGroupEvent> {
        @Override
        public ChatGroupEvent newInstance() {
            return new ChatGroupEvent();
        }
    }

    static class ChatGroupEventHandler extends PartitionEventHandler<ChatGroupEvent, String> {

        @Override
        protected void doOnEvent(ChatGroupEvent event, long sequence, boolean endOfBatch) throws Exception {
            Thread.sleep(50);
            log.info("event key={},owner={},changeType={}, index={}", event.getKey(), event.getGroupOwner(), event.getChangeType(), getIndex());
        }
    }
    static class ChatGroupEvent implements Hashed<String> {
        private volatile  int hashcode=-1;

        private String groupId;

        private String changeType;

        private String groupOwner;

        @Override
        public String getKey() {
            return groupId;
        }

        @Override
        public int hash(String key) {
            if (hashcode == -1) {
                synchronized (this){
                    if (hashcode == -1) {
                        int h;
                        hashcode = (h = key.hashCode()) ^ (h >>> 16);
                        return hashcode;
                    }
                }
            }
            return hashcode;
        }

        public String getGroupId() {
            return groupId;
        }

        public void setGroupId(String groupId) {
            this.groupId = groupId;
        }

        public String getChangeType() {
            return changeType;
        }

        public void setChangeType(String changeType) {
            this.changeType = changeType;
        }

        public String getGroupOwner() {
            return groupOwner;
        }

        public void setGroupOwner(String groupOwner) {
            this.groupOwner = groupOwner;
        }
    }
}

  • 6
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值