Martin Fowler在自己网站上写了一篇LMAX架构的文章,在文章中他介绍了LMAX是一种新型零售金融交易平台,它能够以很低的延迟产生大量交易。这个系统是建立在JVM平台上,其核心是一个业务逻辑处理器,它能够在一个线程里每秒处理6百万订单。业务逻辑处理器完全是运行在内存中,使用事件源驱动方式。业务逻辑处理器的核心是Disruptor。Disruptor它是一个开源的并发框架,并获得2011 Duke’s 程序框架创新奖,能够在无锁的情况下实现网络的Queue并发操作。Disruptor是一个高性能的异步处理框架,或者可以认为是最快的消息框架(轻量的JMS),也可以认为是一个观察者模式的实现,或者事件监听模式的实现。
开源:https://github.com/LMAX-Exchange/disruptor
com.lmax
disruptor
3.4.2
import java.text.SimpleDateFormat;
public class OrderEvent {
private long value; //订单的价格
public long getValue() {
return value;
}
public void setValue(long value) {
this.value = value;
}
}
import java.text.SimpleDateFormat;
public class OrderEvent {
private long value; //订单的价格
public long getValue() {
return value;
}
public void setValue(long value) {
this.value = value;
}
}
import java.nio.ByteBuffer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.lmax.disruptor.BlockingWaitStrategy;
import com.lmax.disruptor.EventFactory;
import com.lmax.disruptor.EventHandler;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
public class Main {
public static void main(String[] args) {
// 参数准备工作
OrderEventFactory orderEventFactory = new OrderEventFactory();
int ringBufferSize = 1024*1024;
ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
/**
* 建立OrderEvent类
* 1 eventFactory: 消息(event)工厂对象 实现 implements EventFactory接口
* OrderEventHandler implements EventHandler 用来接受数据
* 2 ringBufferSize: 容器的长度
* 3 executor: 线程池(建议使用自定义线程池) RejectedExecutionHandler
* 4 ProducerType: 单生产者 还是 多生产者
* 5 waitStrategy: 等待策略
*/
//1. 实例化disruptor对象
Disruptor disruptor = new Disruptor(orderEventFactory,
ringBufferSize, //容器大小
executor, //线程池
ProducerType.SINGLE, //SINGLE\MULTI 单生产者模式和 多生产者模式
new BlockingWaitStrategy()); //等待策略 阻塞策略
//2. 添加消费者的监听 (构建disruptor 与 消费者的一个关联关系)
disruptor.handleEventsWith(new OrderEventHandler());
//3. 启动disruptor
disruptor.start();
//4. 获取实际存储数据的容器: RingBuffer
RingBuffer ringBuffer = disruptor.getRingBuffer();
//订单生产者
OrderEventProducer producer = new OrderEventProducer(ringBuffer);
ByteBuffer bb = ByteBuffer.allocate(8);
for(long i = 0 ; i < 100; i ++){
bb.putLong(0, i);
producer.sendData(bb);
}
disruptor.shutdown();
executor.shutdown();
}
}
import java.nio.ByteBuffer;
import com.lmax.disruptor.RingBuffer;
public class OrderEventProducer {
private RingBuffer ringBuffer;
public OrderEventProducer(RingBuffer ringBuffer) {
this.ringBuffer = ringBuffer;
}
public void sendData(ByteBuffer data) {
//1 在生产者发送消息的时候, 首先 需要从我们的ringBuffer里面 获取一个可用的序号
long sequence = ringBuffer.next(); //0
try {
//2 根据这个序号, 找到具体的 "OrderEvent" 元素 注意:此时获取的OrderEvent对象是一个没有被赋值的"空对象"
OrderEvent event = ringBuffer.get(sequence);
//3 进行实际的赋值处理
event.setValue(data.getLong(0));
} finally {
//4 提交发布操作
ringBuffer.publish(sequence);
}
}
}
唯一使用锁的就是消费者的等待策略实现类中,下图。补充一句,生产者的等到策略就是LockSupport.parkNanos(1),再自旋判断。
名称 | 措施 | 适用场景 |
---|---|---|
BlockingWaitStrategy | 加锁 | CPU资源紧缺,吞吐量和延迟并不重要的场景 |
BusySpinWaitStrategy | 自旋 | 通过不断重试,减少切换线程导致的系统调用,而降低延迟。推荐在线程绑定到固定的CPU的场景下使用 |
PhasedBackoffWaitStrategy | 自旋 + yield + 自定义策略 | 策略 CPU资源紧缺,吞吐量和延迟并不重要的场景 |
SleepingWaitStrategy | 自旋 + yield + sleep | 性能和CPU资源之间有很好的折中。延迟不均匀 |
TimeoutBlockingWaitStrategy | 加锁,有超时限制 | CPU资源紧缺,吞吐量和延迟并不重要的场景 |
YieldingWaitStrategy | 自旋 + yield + 自旋 | 性能和CPU资源之间有很好的折中。延迟比较均匀 |
abstract class SingleProducerSequencerPad extends AbstractSequencer
{
protected long p1, p2, p3, p4, p5, p6, p7;
SingleProducerSequencerPad(int bufferSize, WaitStrategy waitStrategy)
{
super(bufferSize, waitStrategy);
}
}
public final class SingleProducerSequencer extends SingleProducerSequencerFields
{
protected long p1, p2, p3, p4, p5, p6, p7;
//..省略
}
Java中通过填充缓存行,来解决伪共享问题的思路,现在可能已经是老生常谈,连Java8中都新增了sun.misc.Contended注解来避免伪共享问题。但在Disruptor刚出道那会儿,用缓存行来优化Java数据结构,这恐怕还很新潮。
还有一些细节性的
1)通过sequence & (bufferSize - 1)定位元素的index比普通的求余取模(%)要快得多。sequence >>> indexShift 快速计算出sequence/bufferSize的商flag(其实相当于当前sequence在环形跑道上跑了几圈,在数据生产时要设置好flag。
2)合理使用Unsafe,CPU级别指令。实现更加高效地内存管理和原子访问。
至于一些更细节的,下面源码搞起来,还是很简单的。
@Override
public long next(int n) //1
{
if (n < 1) //初始值 sequence= -1
{
throw new IllegalArgumentException("n must be > 0");
}
long nextValue = this.nextValue; //语义级别的: nextvalue 为singleProducerSequencer的变量
long nextSequence = nextValue + n; //0
long wrapPoint = nextSequence - bufferSize; // -10 wrapPoint用于判断当前的序号有没有绕过整个ringbuffer容器
//这个cachedGatingSequence 他的目的就是不用每次都去进行获取消费者的最小序号,用一个缓存区进行接收
long cachedGatingSequence = this.cachedValue; //-1 cachedValue我不太清楚 但是语义上说是进行缓存优化的
if (wrapPoint > cachedGatingSequence || cachedGatingSequence > nextValue)
{
long minSequence; //最小序号
// 如果你的生产者序号大于消费者中最小的序号 那么你就挂起 进行自旋操作
//生产者号数值不能大于消费者中最小的序号数值
while (wrapPoint > (minSequence = Util.getMinimumSequence(gatingSequences, nextValue))) //自旋操作 自旋锁 避免加锁 与cas操作
// Util.getMinimumSequence(gatingSequences, nextValue) 找到消费者中最小的序号值
{
LockSupport.parkNanos(1L); // TODO: Use waitStrategy to spin?
}
this.cachedValue = minSequence;
}
this.nextValue = nextSequence;
return nextSequence;
}
参考:
https://www.cnblogs.com/lewis09/p/9974995.html