每天一例多线程[day23]-----Disruptor的两种简单实现

版权声明:原创文章,未经允许不得转载.Tips:传统电商火热的时代已经成为过去 , 下一个阶段属于大数据 人工智能 , 服务、便捷、安全、效率、创新成为下一个阶段互联网时代的新词汇,而IT技术也随着行业的变化发展而不断更迭。对于码农的出路总结一句话:追技术不如追领域。[基础][设计][能力] https://blog.csdn.net/shengqianfeng/article/details/80686889

在Disruptor中,Ringbuffer存放的数据可以被称作“trade”,即交易或者订单。Disruptor可以在一个线程里每秒处理600万的订单。

在之前HelloWorld的例子中,我们获取disruptor实例,然后调用getRingbuffer()来获取RingBuffer,其实在很多时候,我们可以直接使用RingBuffer,以及其他的API操作。

消费者的实现有两种方式,EventProcessor和WorkerPool,其中EventProcessor就是之前HelloWorld例子中消费者LongEventHandler的实现方式。

在本节中,我们只考虑“单生产者---单消费者”.

        int BUFFER_SIZE=1024;  
        final RingBuffer<Trade> ringBuffer = RingBuffer.createSingleProducer(new EventFactory<Trade>() {  
            @Override  
            public Trade newInstance() {  
                return new Trade();  
            }  
        }, BUFFER_SIZE, new YieldingWaitStrategy());
createSingleProducer就是创建一个单生产者的RingBuffer,由RingBuffer静态方法创建,构造参数是:
----com.lmax.disruptor.EventFactory

构造参数: 

   参数1EventFactory:是Disruptor的一个事件工厂,类型是我们自己定义的Trade,内部实现了newInstance方法返回Trade对象实例。

    参数2BUFFER_SIZE:RingBuffer的容量大小,建议2的n次方。

    参数3WaitStrategy:这里我们使用的是YieldingWaitStrategy,RingBuffer在没有可用区域的时候(可能是消费者太慢)的等待策略。


接下来我们来看下以上两种消费者处理器是如何实现的?

import java.util.concurrent.atomic.AtomicInteger;

public class Trade {  
	
	private String id;//ID  
	private String name;
	private double price;//金额  
	private AtomicInteger count = new AtomicInteger(0);
	
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public double getPrice() {
		return price;
	}
	public void setPrice(double price) {
		this.price = price;
	}
	public AtomicInteger getCount() {
		return count;
	}
	public void setCount(AtomicInteger count) {
		this.count = count;
	} 
	  
	  
}  
import java.util.UUID;

import com.lmax.disruptor.EventHandler;
import com.lmax.disruptor.WorkHandler;

public class TradeHandler implements EventHandler<Trade>, WorkHandler<Trade> {  
	  
    @Override  
    public void onEvent(Trade event, long sequence, boolean endOfBatch) throws Exception {  
        this.onEvent(event);  
    }  
  
    @Override  
    public void onEvent(Trade event) throws Exception {  
        //这里做具体的消费逻辑  
        event.setId(UUID.randomUUID().toString());//简单生成下ID  
        System.out.println(event.getId());  
    }  
}  
PS:EventHanler和WorkHandler只需要实现其中之一即可。


EventProcessor实现方式:


import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import com.lmax.disruptor.BatchEventProcessor;
import com.lmax.disruptor.EventFactory;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.SequenceBarrier;
import com.lmax.disruptor.YieldingWaitStrategy;

public class Main1 {  
   
	public static void main(String[] args) throws Exception {  
        int BUFFER_SIZE=1024;  
        int THREAD_NUMBERS=4;  
        /* 
         * createSingleProducer创建一个单生产者的RingBuffer, 
         * 第一个参数叫EventFactory,从名字上理解就是"事件工厂",其实它的职责就是产生数据填充RingBuffer的区块。 
         * 第二个参数是RingBuffer的大小,它必须是2的指数倍 目的是为了将求模运算转为&运算提高效率 
         * 第三个参数是RingBuffer的生产都在没有可用区块的时候(可能是消费者(或者说是事件处理器) 太慢了)的等待策略 
         */  
        final RingBuffer<Trade> ringBuffer = RingBuffer.createSingleProducer(new EventFactory<Trade>() {  
            @Override  
            public Trade newInstance() {  
                return new Trade();  
            }  
        }, BUFFER_SIZE, new YieldingWaitStrategy());  
        
        //创建线程池  
        ExecutorService executors = Executors.newFixedThreadPool(THREAD_NUMBERS);  
        
        //创建SequenceBarrier  
        SequenceBarrier sequenceBarrier = ringBuffer.newBarrier();  
          
        //创建消息处理器  
        BatchEventProcessor<Trade> transProcessor = new BatchEventProcessor<Trade>(  
                ringBuffer, sequenceBarrier, new TradeHandler());  
          
        //这一步的目的就是把消费者的位置信息引用注入到生产者    如果只有一个消费者的情况可以省略 
        ringBuffer.addGatingSequences(transProcessor.getSequence());  
          
        //把消息处理器提交到线程池  
        executors.submit(transProcessor);  
        
        //如果存在多个消费者 那重复执行上面3行代码 把TradeHandler换成其它消费者类  
          
        Future<?> future= executors.submit(new Callable<Void>() {  
            @Override  
            public Void call() throws Exception {  
                long seq;  
                for(int i=0;i<10;i++){  
                    seq = ringBuffer.next();//占个坑 --ringBuffer一个可用区块  
                    ringBuffer.get(seq).setPrice(Math.random()*9999);//给这个区块放入 数据 
                    ringBuffer.publish(seq);//发布这个区块的数据使handler(consumer)可见  
                }  
                return null;  
            }  
        }); 
        
        future.get();//等待生产者结束  
        Thread.sleep(1000);//等上1秒,等消费都处理完成  
        transProcessor.halt();//通知事件(或者说消息)处理器 可以结束了(并不是马上结束!!!)  
        executors.shutdown();//终止线程  
    }  
}  

以上代码创建消费者处理器是使用EventProcessor方式:

       //创建消息处理器  
        BatchEventProcessor<Trade> transProcessor = new BatchEventProcessor<Trade>(  
                ringBuffer, sequenceBarrier, new TradeHandler());  

构造参数:

    参数1:RingBuffer,不用解释了

    参数2:sequenceBarrier,直白翻译,序列障碍。用来协调和平衡生产者的生产能力与消费者的消费能力之间的差异,以使得两者之间达到最优的匹配。

        //创建SequenceBarrier  
        SequenceBarrier sequenceBarrier = ringBuffer.newBarrier();  

    参数3:TradeHandler实例,消费者具体业务处理类,实现了onEvent方法。

有了sequenceBarrier,transProcessor还不够,因为需要把消费者的位置信息引用注入到生产者  (如果只有一个消费者的情况可以省略),这样生产者才知道RingBuffer中是否可以由空间放置trade。

        ringBuffer.addGatingSequences(transProcessor.getSequence());  

然后把消费者处理器(实现了callable接口)提交给线程池。

        executors.submit(transProcessor);  

接下来就是生产者发布生产的数据了

 Future<?> future= executors.submit(new Callable<Void>() {  
            @Override  
            public Void call() throws Exception {  
                long seq;  
                for(int i=0;i<10;i++){  
                    seq = ringBuffer.next();//占个坑 --ringBuffer一个可用区块  
                    ringBuffer.get(seq).setPrice(Math.random()*9999);//给这个区块放入 数据 
                    ringBuffer.publish(seq);//发布这个区块的数据使handler(consumer)可见  
                }  
                return null;  
            }  
        }); 
        future.get();//等待生产者结束  
        Thread.sleep(1000);//等上1秒,等消费都处理完成  
        transProcessor.halt();//通知事件(或者说消息)处理器 可以结束了(并不是马上结束!!!)  
        executors.shutdown();//终止线程  

需要注意的是消费者处理器transProcessor需要调用halt方法通知消费者处理线程TradeHandler停止执行。

打印了10个UIUID随机数:

429d1457-8a1a-462b-99ab-2aab82852967
7a4d2579-7bad-4ddb-825b-b4b522b571c2
23897440-7cb4-42e9-99b3-7d4ac9c656d8
16a24560-1b37-4c97-9389-54fffb877c84
1865140e-4f6f-40ac-84b3-eb81132e81ec
ec75301e-6b04-4572-a2cf-dd7870580999
29edf596-453a-4f93-bc04-efa59c493d76
ba6057ef-ca4c-4ef1-bed3-b6e111715a5b
f09fb177-9d56-4ed0-98fa-1edbaf759e57
a046abca-f3cc-417f-ae2e-0dc1d6db96d1


WorkHandler实现方式:

直接看代码:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.lmax.disruptor.EventFactory;
import com.lmax.disruptor.IgnoreExceptionHandler;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.SequenceBarrier;
import com.lmax.disruptor.WorkHandler;
import com.lmax.disruptor.WorkerPool;

public class Main2 {  
    public static void main(String[] args) throws InterruptedException {  
        int BUFFER_SIZE=1024;  
        int THREAD_NUMBERS=4;  
        
        EventFactory<Trade> eventFactory = new EventFactory<Trade>() {  
            public Trade newInstance() {  
                return new Trade();  
            }  
        };  
        
        RingBuffer<Trade> ringBuffer = RingBuffer.createSingleProducer(eventFactory, BUFFER_SIZE);  
          
        SequenceBarrier sequenceBarrier = ringBuffer.newBarrier();  
          
        ExecutorService executor = Executors.newFixedThreadPool(THREAD_NUMBERS);  
          
        WorkHandler<Trade> handler = new TradeHandler();  

        WorkerPool<Trade> workerPool = new WorkerPool<Trade>(ringBuffer, sequenceBarrier, new IgnoreExceptionHandler(), handler);  
          
        workerPool.start(executor);  
          
        //下面这个生产8个数据
        for(int i=0;i<8;i++){  
            long seq=ringBuffer.next();  
            ringBuffer.get(seq).setPrice(Math.random()*9999);  
            ringBuffer.publish(seq);  
        }  
          
        Thread.sleep(1000);  
        workerPool.halt();  
        executor.shutdown();  
    }  
}  

跟前一种实现方式不同的是从:

 WorkHandler<Trade> handler = new TradeHandler();  
这里不一样。workerPool可以通过start方法传入线程池executor对象来启动消费者。
       WorkHandler<Trade> handler = new TradeHandler();  

        WorkerPool<Trade> workerPool = new WorkerPool<Trade>(ringBuffer, sequenceBarrier, new IgnoreExceptionHandler(), handler);  
          
        workerPool.start(executor);  
          
        //下面这个生产8个数据
        for(int i=0;i<8;i++){  
            long seq=ringBuffer.next();  
            ringBuffer.get(seq).setPrice(Math.random()*9999);  
            ringBuffer.publish(seq);  
        }  
          
        Thread.sleep(1000);  
        workerPool.halt();  
        executor.shutdown();  

workerPool也需要halt方法通知内部消费者处理业务线程停止。

第二种写法更简单一些,推荐使用!










阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页