/* * Copyright 2003-2009 the original author or authors. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.jdon.async.disruptor; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import com.jdon.async.disruptor.pool.DisruptorPoolFactory; import com.jdon.container.ContainerWrapper; import com.jdon.container.annotation.type.ConsumerLoader; import com.jdon.container.finder.ContainerCallback; import com.jdon.container.pico.Startable; import com.jdon.domain.message.DomainEventDispatchHandler; import com.jdon.domain.message.DomainEventHandler; import com.jdon.domain.message.consumer.ConsumerMethodHolder; import com.jdon.util.Debug; import com.lmax.disruptor.BlockingWaitStrategy; import com.lmax.disruptor.WaitStrategy; import com.lmax.disruptor.dsl.Disruptor; import com.lmax.disruptor.dsl.EventHandlerGroup; import com.lmax.disruptor.dsl.ProducerType; /** * SLEEPING is a better option when you have a large number of event processors * and you need throughput when you don't mind a 1ms latency hit in the worse * case. BLOCKING has the lowest throughput of all the strategies but it does * not have the 1ms latency spikes of SLEEPING. It uses no CPU when idle but it * does not scale up so well with increasing numbers of event processors because * of the contention on the lock. YIELDING and BUSY_SPIN have the best * performance for both throughput and latency but eat up a CPU. YIELDING is * more friendly in allowing other threads to run when cores are limited. It * would be nice if Java had access to the x86 PAUSE instruction to save power * and further reduce latency that gets lost due to the wrong choices the CPU * can make with speculative execution of busy spin loops. In all cases where * you have sufficient cores then all the wait strategies will beat pretty much * any other alternative such as queues. * * @author banq * */ public class DisruptorFactory implements Startable { public final static String module = DisruptorFactory.class.getName(); protected final ConcurrentHashMap<String, TreeSet<DomainEventHandler>> handlesMap; private String RingBufferSize; private ContainerWrapper containerWrapper; private DisruptorPoolFactory disruptorPoolFactory; public DisruptorFactory(DisruptorParams disruptorParams, ContainerCallback containerCallback, DisruptorPoolFactory disruptorPoolFactory) { this.RingBufferSize = disruptorParams.getRingBufferSize(); this.containerWrapper = containerCallback.getContainerWrapper(); this.handlesMap = new ConcurrentHashMap<String, TreeSet<DomainEventHandler>>(); this.disruptorPoolFactory = disruptorPoolFactory; this.disruptorPoolFactory.setDisruptorFactory(this); } public DisruptorFactory() { // @todo configure in xml this.RingBufferSize = "8"; this.containerWrapper = null; this.handlesMap = new ConcurrentHashMap<String, TreeSet<DomainEventHandler>>(); this.disruptorPoolFactory = new DisruptorPoolFactory(); this.disruptorPoolFactory.setDisruptorFactory(this); } public Disruptor createDw(String topic) { int size = Integer.parseInt(RingBufferSize); return new Disruptor(new EventDisruptorFactory(), size, Executors.newCachedThreadPool()); } public Disruptor createSingleDw(String topic) { int size = Integer.parseInt(RingBufferSize); WaitStrategy waitStrategy = new BlockingWaitStrategy(); return new Disruptor(new EventDisruptorFactory(), size, Executors.newCachedThreadPool(), ProducerType.SINGLE, waitStrategy); } public Disruptor addEventMessageHandler(Disruptor dw, String topic, TreeSet<DomainEventHandler> handlers) { if (handlers.size() == 0) return null; EventHandlerGroup eh = null; for (DomainEventHandler handler : handlers) { DomainEventHandlerAdapter dea = new DomainEventHandlerAdapter(handler); if (eh == null) { eh = dw.handleEventsWith(dea); } else { eh = eh.handleEventsWith(dea); } } return dw; } public Disruptor getDisruptor(String topic) { return this.disruptorPoolFactory.getDisruptor(topic); } public void releaseDisruptor(Object owner) { } /** * one topic one EventDisruptor * * @param topic * @return */ public Disruptor createDisruptor(String topic) { TreeSet handlers = getHandles(topic); if (handlers == null) return null; Disruptor dw = createDw(topic); Disruptor disruptor = addEventMessageHandler(dw, topic, handlers); if (disruptor == null) return null; disruptor.start(); return disruptor; } /** * single producer :single consumer * * no lock * * @param topic * @return */ public Disruptor createSingleDisruptor(String topic) { TreeSet handlers = getHandles(topic); if (handlers == null) return null; Disruptor dw = createSingleDw(topic); Disruptor disruptor = addEventMessageHandler(dw, topic, handlers); if (disruptor == null) return null; disruptor.start(); return disruptor; } private TreeSet getHandles(String topic) { TreeSet handlers = handlesMap.get(topic); if (handlers == null)// not inited { handlers = getTreeSet(); handlers.addAll(loadEvenHandler(topic)); handlers.addAll(loadOnEventConsumers(topic)); if (handlers.size() == 0) { // maybe by mistake in @Component(topicName) Object o = containerWrapper.lookup(topic); if (o == null) { Debug.logError("[Jdonframework]no found the class annotated with @Consumer(" + topic + ") ", module); } return null; } handlesMap.put(topic, handlers); } return handlers; } public boolean isContain(String topic) { if (containerWrapper.lookup(ConsumerLoader.TOPICNAME + topic) == null && containerWrapper.lookup(ConsumerLoader.TOPICNAME2 + topic) == null) { return false; } else return true; } /** * if there are many consumers, execution order will be alphabetical list by * Name of @Consumer class. * * @param topic * @return */ protected Collection loadEvenHandler(String topic) { Collection ehs = new ArrayList(); Collection<String> consumers = (Collection<String>) containerWrapper.lookup(ConsumerLoader.TOPICNAME + topic); if (consumers == null || consumers.size() == 0) { Debug.logWarning("[Jdonframework]there is no any consumer class annotated with @Consumer(" + topic + ") ", module); return ehs; } for (String consumerName : consumers) { DomainEventHandler eh = (DomainEventHandler) containerWrapper.lookup(consumerName); ehs.add(eh); } return ehs; } protected Collection loadOnEventConsumers(String topic) { Collection ehs = new ArrayList(); Collection consumerMethods = (Collection) containerWrapper.lookup(ConsumerLoader.TOPICNAME2 + topic); if (consumerMethods == null) return ehs; for (Object o : consumerMethods) { ConsumerMethodHolder consumerMethodHolder = (ConsumerMethodHolder) o; DomainEventDispatchHandler domainEventDispatchHandler = new DomainEventDispatchHandler(consumerMethodHolder, containerWrapper); ehs.add(domainEventDispatchHandler); } return ehs; } public TreeSet<DomainEventHandler> getTreeSet() { return new TreeSet(new Comparator() { public int compare(Object num1, Object num2) { String inum1, inum2; if (num1 instanceof DomainEventDispatchHandler) { inum1 = ((DomainEventDispatchHandler) num1).getSortName(); } else { inum1 = num1.getClass().getName(); } if (num2 instanceof DomainEventDispatchHandler) { inum2 = ((DomainEventDispatchHandler) num2).getSortName(); } else { inum2 = num2.getClass().getName(); } if (inum1.compareTo(inum2) < 1) { return -1; // returning the first object } else { return 1; } } }); } @Override public void start() { // TODO Auto-generated method stub } @Override public void stop() { this.containerWrapper = null; this.handlesMap.clear(); this.RingBufferSize = null; } }