无锁编程笔记

一 简单压测

1 ArrayBlockingQueue

// 单生产者单消费者
public static void main(String[] args) {
    ArrayBlockingQueue<Data> queue = new ArrayBlockingQueue<Data>(10000000);
    long startTime = System.currentTimeMillis();

    new Thread(() -> {
        long i = 0;
        while (i < Constrants.EVENT_NUM_OM) {
            Data data = new Data(i, "c" + i);
            try {
                queue.put(data);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            i++;
        }
    }).start();

    new Thread(() -> {
        int k = 0;
        while (k < Constrants.EVENT_NUM_OM) {
            try {
                queue.take();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            k++;
        }
        long endTime = System.currentTimeMillis();
        System.out.println("runTime:" + (endTime - startTime));
    }).start();
}

// Data
public class Data implements Serializable {
    private Long id;
    private String name;
}

2 Disruptor

public class DataConsumer implements EventHandler<Data> {
    private long startTime;
    private int i;

    public DataConsumer() {
        this.startTime = System.currentTimeMillis();
    }

    @Override
    public void onEvent(Data event, long sequence, boolean endOfBatch) throws Exception {
        i++;
        if (i == Constrants.EVENT_NUM_OM) {
            long endTime = System.currentTimeMillis();
            System.out.println("runTime:" + (endTime - startTime));
        }
    }
}

public static void main(String[] args) {
    int ringBufferSize = 65536;// 必需为2的倍数

    Disruptor<Data> disruptor = new Disruptor<>(() -> new Data(), ringBufferSize, Executors.newSingleThreadExecutor(), ProducerType.SINGLE, new YieldingWaitStrategy());
    DataConsumer dataConsumer = new DataConsumer();
    disruptor.handleEventsWith(dataConsumer);
    disruptor.start();

    new Thread(() -> {
        RingBuffer<Data> ringBuffer = disruptor.getRingBuffer();
        for (long i = 0; i < Constrants.EVENT_NUM_OM; i++) {
            long seq = ringBuffer.next();
            Data data = ringBuffer.get(seq);
            data.setId(i);
            data.setName("c" + i);
            ringBuffer.publish(seq);
        }
    }).start();
}

二 并发编程框架核心讲解

1 简介

  • 一个线程执行6百万订单
  • 业务逻辑处理器运行在内存中
  • 基于事件驱动方式

2 Quick Start

(1)核心类

  • Event工厂类:创建Event实例
  • 监听事件类:处理Event
  • Disruptor实例:配置参数,编写核心组件
  • 生产者组件:向Disruptor中投递数据

(2)demo

@Data
public class OrderEvent {
    private long value;
}

public class OrderEventFactory implements EventFactory<OrderEvent> {
    @Override
    public OrderEvent newInstance() {
        return new OrderEvent();
    }
}

public class OrderEventHandler implements EventHandler<OrderEvent> {
    @Override
    public void onEvent(OrderEvent event, long sequence, boolean endOfBatch) throws Exception {
        System.out.println("消费者:" + event.getValue());
    }
}

public class OrderEventProducer {
    private RingBuffer<OrderEvent> ringBuffer;

    public OrderEventProducer(RingBuffer<OrderEvent> ringBuffer) {
        this.ringBuffer = ringBuffer;
    }

    public void sendData(ByteBuffer byteBuffer) {
        // 1. 在生产者发送消息时,从RingBuffer获取一个序号
        long seq = ringBuffer.next();
        try {
            // 2. 根据需要找到具体的OrderEvent元素,此时为未填充的对象
            OrderEvent orderEvent = ringBuffer.get(seq);
            // 3. 进行实际的赋值
            orderEvent.setValue(byteBuffer.getLong(0));
        } finally {
            // 4. 发布提交
            ringBuffer.publish(seq);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        int ringBufferSize = 1024 * 8;

        ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

        /**
         * 1 eventFactory: 消息工厂类
         * 2 ringBufferSize: 容器长度
         * 3 executor: 线程池(自定义线程池) RejectedExecutionHandler
         * 4 ProducerType: 生产者数量
         * 5 WaitStrategy: 等待策略
         */

        // 1. 实例化disruptor
        Disruptor<OrderEvent> disruptor = new Disruptor<>(new OrderEventFactory(), ringBufferSize, executorService, ProducerType.SINGLE, new BlockingWaitStrategy());

        // 2.添加消息消费者监听
        disruptor.handleEventsWith(new OrderEventHandler());

        // 3.启动disruptor
        disruptor.start();

        // 4.获取实际存储数据的容器:RingBuffer
        RingBuffer<OrderEvent> ringBuffer = disruptor.getRingBuffer();
        OrderEventProducer producer = new OrderEventProducer(ringBuffer);

        ByteBuffer byteBuffer = ByteBuffer.allocate(8);

        for (long i = 0; i < 100; i++) {
            byteBuffer.putLong(0,i);
            producer.sendData(byteBuffer);
        }

        disruptor.shutdown();
        executorService.shutdown();
    }
}

3 RingBuffer

  • 它是一个环。
  • 它在做不同上下文(线程)间传递数据的Buffer。
  • 它拥有一个序号,序号指向数组中下一个可用的元素(取模操作)。
  • 它的容量一般为2的N次方,因为2的N次方有利于计算机计算。
  • 它是基于数组的缓存实现,也是创建Sequencer与定义WaitStrategy的入口

4 Disruptor

  • 持有RingBuffer、消费者线程池Executor、消费者集合ConsumerRepository等引用

5 Sequence

  • 通过顺序递增的序号来编号,管理进行交换的数据
  • 对数据进行处理过程总是沿着序号逐个递增处理
  • 一个Sequence用于追踪标识某个特定的事件处理者(RingBuffer/Producer/Consumer)的处理进度
  • Sequence可以看成一个AtomicLong用于标识进度,防止Sequence之间CPU缓存伪共享(Flase Sharing)的问题

6 Sequencer

  • 它是Disruptor的真正核心
  • 两个实现:
    • SingleProducerSequencer
    • MultiProducerSequencer
  • 主要实现生产者和消费者之间快速、正确地传递数据的并发算法

7 Sequence Barrier

  • 保持对RingBuffer的Main Pushlished Sequence(Producer)和Consumer之间的平衡关系
  • Sequence Barrier还定义了决定Consumer是否还有可处理的事件的逻辑

8 EventProcessor

  • 主要事件循环,处理Disruptor的Event,拥有消费者的Sequence。
  • 它有一个实现类BatchEventProcessor,包含了event loop有效的实现,并将回调到一个EventHandler接口的实现对象。

三 Disruptor的高级应用

1 核心链路场景应用讲解

  • 特点:至关重要但业务复杂

  • 实现1:传统完全解耦模式(copy)

  • 实现2:模板模式

  • 解决手段:

    • 领域模型的高度抽象。
    • 寻找更好的框架帮助我们进行编码。
  • 框架:

    • 有限状态机框架:如Spring-StateMachine
    • 使用Disruptor

2 并行计算—串、并行操作、多边形高端操作

(1)概述

EventHandlerGroup<T> handleEvnetsWith(final EventHandler<? super T>... handlers);

// 串行操作:链式调用

// 并行操作:单独调用

(2)操作

@Data
public class Trade {
    private String id;
    private String name;
    private double price;
    private AtomicInteger count = new AtomicInteger(0);
}

public class TradePublisher implements Runnable {

    private CountDownLatch latch;
    private Disruptor disruptor;

    private static final int PUBLISH_COUNT = 2;

    public TradePublisher(CountDownLatch latch, Disruptor<Trade> disruptor) {
        this.latch = latch;
        this.disruptor = disruptor;
    }

    @Override
    public void run() {
        EventTranslator<Trade> eventTranslator = new EventTranslator<Trade>() {
            private Random random = new Random();

            private void generateTrade(Trade trade) {
                trade.setPrice(random.nextDouble() * 9999);
            }

            @Override
            public void translateTo(Trade trade, long sequence) {
                this.generateTrade(trade);
            }
        };

        for (int i = 0; i < PUBLISH_COUNT; i++)
            disruptor.publishEvent(eventTranslator);

        latch.countDown();
    }
}

public static void main(String[] args) throws InterruptedException {
    ExecutorService es1 = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    ExecutorService es2 = Executors.newFixedThreadPool(8);

    Disruptor<Trade> disruptor = new Disruptor<Trade>(
        (EventFactory<Trade>) () -> new Trade(),
        1024 * 1024,
        es2,
        ProducerType.SINGLE,
        new BusySpinWaitStrategy());
    
    Handler1 handler1 = new Handler1();
    Handler2 handler2 = new Handler2();
    Handler3 handler3 = new Handler3();		
    Handler3 handler4 = new Handler4();		
    Handler3 handler5 = new Handler5();		
    
    // 串行操作1
    // disruptor.handleEventsWith(handler1)
    //            .then(handler2)
    //            .then(handler3);
    //
    
    // 串行操作2
    // disruptor.handleEventsWith(handler1)
    //            .handleEventsWith(handler2)
    //            .handleEventsWith(handler3);
    //

    // 并行操作1
    // disruptor.handleEventsWith(handler1);
    // disruptor.handleEventsWith(handler3);
    // disruptor.handleEventsWith(handler2);

    // 并行操作2
    // disruptor.handleEventsWith(handler1,handler3,handler2);

    // 唯一操作: 多消费者
    // disruptor.handleEventsWithWorkerPool(handler1,handler2,handler3);
    
    // 多边形
    // disruptor.handleEventsWith(handler1,handler2).then(handler3);
    	
    // 复杂多边形操作
    // disruptor.handleEventsWith(handler1, handler4);
    // disruptor.after(handler1).then(handler2);
    // disruptor.after(handler4).then(handler5);
    // disruptor.after(handler2, handler5).then(handler3);
    
    RingBuffer<Trade> ringBuffer = disruptor.start();

    CountDownLatch latch = new CountDownLatch(1);

    es1.submit(new TradePublisher(latch, disruptor));

    latch.await();

    disruptor.shutdown();
    es1.shutdown();
    es2.shutdown();
}

3 多生产者、多消费者模型

@Data
public class Order {
    private String id;
    private String name;
    private double price;
    private AtomicInteger count = new AtomicInteger(0);
}

public class Consumer implements WorkHandler<Order> {
    private String consumerId;
    private AtomicInteger atomicInteger = new AtomicInteger(0);
    private Random random = new Random();

    public Consumer(String consumerId) {
        this.consumerId = consumerId;
    }

    @Override
    public void onEvent(Order event) throws Exception {
        Thread.sleep(1 * random.nextInt(5));
        System.out.println("当前消费者:" + this.consumerId + ",消费:" + event);
        atomicInteger.incrementAndGet();
    }

    public int getCount() {
        return atomicInteger.get();
    }
}

public class Producer {
	private RingBuffer<Order> ringBuffer;
	
	public Producer(RingBuffer<Order> ringBuffer) {
		this.ringBuffer = ringBuffer;
	}

	public void sendData(String uuid) {
		long sequence = ringBuffer.next();
		try {
			Order order = ringBuffer.get(sequence);
			order.setId(uuid);
		} finally {
			ringBuffer.publish(sequence);
		}
	}
}

// Main1
public static void main(String[] args) throws InterruptedException {
    // 1.创建RingBuffer
    RingBuffer<Order> ringBuffer = RingBuffer.create(ProducerType.MULTI, () -> new Order(), 1024 * 1024, new YieldingWaitStrategy());

    // 2.RingBuffer创建屏障
    SequenceBarrier barrier = ringBuffer.newBarrier();

    // 3.构建多消费者
    int count = 10;
    Consumer[] consumers = new Consumer[count];
    for (int i = 0; i < count; i++) {
        consumers[i] = new Consumer("c-" + i);
    }

    // 4.多消费者工作池
    WorkerPool<Order> workerPool = new WorkerPool<>(
        ringBuffer,
        barrier,
        new ExceptionHandler<Order>() {
            @Override
            public void handleEventException(Throwable ex, long sequence, Order event) { }
            @Override
            public void handleOnStartException(Throwable ex) { }
            @Override
            public void handleOnShutdownException(Throwable ex) { }
        },
        consumers
    );
    //5 设置多个消费者的sequence序号 用于单独统计消费进度, 并且设置到ringbuffer中
    ringBuffer.addGatingSequences(workerPool.getWorkerSequences());

    //6 启动workerPool
    ExecutorService executorService = Executors.newFixedThreadPool(5);
    workerPool.start(executorService);

    final CountDownLatch latch = new CountDownLatch(1);

    for (int i = 0; i < 100; i++) {
        final Producer producer = new Producer(ringBuffer);
        new Thread(() -> {
            try {
                latch.await();
            } catch (Exception e) {
                e.printStackTrace();
            }
            for (int j = 0; j < 100; j++) {
                producer.sendData(UUID.randomUUID().toString());
            }
        }).start();
    }

    Thread.sleep(2000);
    System.err.println("----------线程创建完毕,开始生产数据----------");
    latch.countDown();

    Thread.sleep(10000);

    for (int i = 0; i < consumers.length; i++) {
        System.err.println("任务总数:" + consumers[i].getCount());
    }
    executorService.shutdown();
}

// Main2
public static void main(String[] args) throws InterruptedException {
    Consumer[] consumers = new Consumer[10];
    for (int i = 0; i < consumers.length; i++) {
        consumers[i] = new Consumer("c-" + i);
    }

    Disruptor<Order> disruptor = new Disruptor<Order>(
        (EventFactory<Order>) () -> new Order(),
        1024 * 1024,
        Executors.defaultThreadFactory(),
        ProducerType.SINGLE,
        new BlockingWaitStrategy());
    disruptor.handleEventsWithWorkerPool(consumers);
    disruptor.start();

    final CountDownLatch latch = new CountDownLatch(1);

    for (int i = 0; i < 100; i++) {
        final Producer producer = new Producer(disruptor.getRingBuffer());
        new Thread(() -> {
            try {
                latch.await();
            } catch (Exception e) {
                e.printStackTrace();
            }
            for (int j = 0; j < 100; j++) {
                producer.sendData(UUID.randomUUID().toString());
            }
        }).start();
    }

    Thread.sleep(2000);
    System.err.println("----------线程创建完毕,开始生产数据----------");
    latch.countDown();
    Thread.sleep(10000);

    for (int i = 0; i < consumers.length; i++) {
        System.err.println("任务总数:" + consumers[i].getCount());
    }
    disruptor.shutdown();
}

四 Disruptor源码分析

1 UML图

2 Disruptro性能为什么这么优秀?

  • 数据结构层面:环形结构、数组、内存预加载
  • 单线程写方式、内存屏障
  • 消除伪共享(填充缓存行)
  • 序号栅栏和序号配合使用来消除锁和CAS

3 数据结构层面

  • RingBuffer使用Object[] entries作为存储容器,环形较少内存消费,防止内存溢出,不会无限增长;
  • 内存预加载:一开始就将对象创建(new)出来,元素在内存中一直存在,减少GC频率,空间换时间。
abstract class RingBufferFields<E> extends RingBufferPad{
    private final Object[] entries;
    
    RingBufferFields(EventFactory<E> eventFactory,Sequencer sequencer){
        this.sequencer = sequencer;
        this.bufferSize = sequencer.getBufferSize();

        if (bufferSize < 1){
            throw new IllegalArgumentException("bufferSize must not be less than 1");
        }
        if (Integer.bitCount(bufferSize) != 1){
            throw new IllegalArgumentException("bufferSize must be a power of 2");
        }

        this.indexMask = bufferSize - 1;
        this.entries = new Object[sequencer.getBufferSize() + 2 * BUFFER_PAD];
        fill(eventFactory);
    }
    
    private void fill(EventFactory<E> eventFactory){
        for (int i = 0; i < bufferSize; i++){
            entries[BUFFER_PAD + i] = eventFactory.newInstance();
        }
    }
}

public final class RingBuffer<E> extends RingBufferFields<E> implements Cursored, EventSequencer<E>, EventSink<E>{
    
    RingBuffer(EventFactory<E> eventFactory,Sequencer sequencer){
        super(eventFactory, sequencer);
    }
}

4 内核——单线程写

  • RingBuffer完全无锁,因为它是单线程。
  • Redis、Netty、Nodejs都使用这个核心思想(事件驱动)。

5 系统内存优化——内存屏障

  • 正确实现无锁:内存屏障
  • 实际就是volatile和happens before语义
  • 内存屏障—Linux的smp_wmb()/smp_rmb()
  • 系统内核:Linux的kfifo举例:使用了smp_wmb()

6 系统缓存优化——消除伪共享

  • 缓存系统中以缓存行(cache line)为单位存储的
  • 缓存行为2的整数幂个连续字节,一般为32~256个字节
  • 最常见的缓存行大小为64字节
  • 当多线程修改互相独立的变量时,如果这些变量共享在一个缓存行中,就无意中影响了彼此的性能,这就是伪共享。
// (左边填充7个) value (右边填充7个),一个缓存行一般为64字节,即访问value时,可以保证value独占一个缓存行,不存在伪共享问题,空间换时间。
class LhsPadding{
    protected long p1, p2, p3, p4, p5, p6, p7;
}

class Value extends LhsPadding{
    protected volatile long value;
}

class RhsPadding extends Value{
    protected long p9, p10, p11, p12, p13, p14, p15;
}

public class Sequence extends RhsPadding{
    
   	// ...
}

7 算法优化—序号栅栏

  • ringBuffer.next()
  • SequenceBarrier和Sequence结合使用来协调和管理消费者与生产者的工作节奏,避免了锁和CAS的使用。
  • 各个消费者和生产者都持有自己的序号
    • 消费者序号必须小于生产者序号
    • 消费者序号必须小于其前置消费者的序号
    • 生产者序号不能大于消费者中最小的序号(防止覆盖)
// SingleProducerSequencer.next()
public long next(){
    next(1);
}

// SingleProducerSequencer.next(int n)
public long next(int n){
    if (n < 1){
        throw new IllegalArgumentException("n must be > 0");
    }

    long nextValue = this.nextValue;
	
    // nextSequence默认为-1.
    long nextSequence = nextValue + n;
    // 判断是否绕过整个RingBuffer环
    long wrapPoint = nextSequence - bufferSize;
    // 缓存值:缓存上次的最小消费者序号,无需每次都重新获取
    long cachedGatingSequence = this.cachedValue;

    if (wrapPoint > cachedGatingSequence || cachedGatingSequence > nextValue){
        cursor.setVolatile(nextValue);  // StoreLoad fence
	
        long minSequence;
        // Util.getMinimumSequence(gatingSequences, nextValue)找到消费者中最小的序号(初始值为-1)
        // 自旋锁: 生产者序号不能大于消费者中最小的序号(防止覆盖)
        while (wrapPoint > (minSequence = Util.getMinimumSequence(gatingSequences, nextValue))){
            LockSupport.parkNanos(1L); // TODO: Use waitStrategy to spin?
        }
        this.cachedValue = minSequence;
    }

    // 赋值nextValue
    this.nextValue = nextSequence;
    return nextSequence;
}

8 WaitStrategy等待策略

(1)各个实现

  • WaitStrategy
    • BlockingWaitStrategy:当吞吐量和低延迟不如CPU资源那么重要时,可以使用此策略。
    • BusySpinWaitStrategy:此策略将使用CPU资源来避免可能引入延迟抖动的系统调用。 最好在线程绑定到特定CPU内核时使用。
    • LiteBlockingWaitStrategy:当吞吐量和低延迟不如CPU资源那么重要时,可以使用此策略。相当于BlockingWaitStrategy的优化,但处于实验阶段。
    • PhasedBackoffWaitStrategy:当吞吐量和低延迟不如CPU资源那么重要时,可以使用此策略。自旋,一定时间自旋失败则采用回退策略。
    • SleepingWaitStrategy:此策略是性能和CPU资源之间的良好折衷。
      • 自旋策略:默认尝试200次,前100次为疯狂尝试,后100次为降低该线程的CPU资源。如果200次都没结束,则进行休眠,默认休眠100ms后。
    • TimeoutBlockingWaitStrategy:类似BlockWaitStrategy,只是加个超时。
    • YieldWaitStrategy:此策略将使用100%CPU,但是当其他线程需要CPU资源,它比忙碌旋转策略更容易放弃CPU。(性能最高)

(2)使用

// RingBuffer publish
public void publish(long sequence){
    sequencer.publish(sequence);
}


// Sequencer#publish(long)
// 
// AbstractSequencer#Sequence cursor 
// AbstractSequencer#WaitStrategy waitStrategy
public void publish(long sequence){
    cursor.set(sequence);
    waitStrategy.signalAllWhenBlocking();
}

// BlockingWaitStrategy
public final class BlockingWaitStrategy implements WaitStrategy{
    private final Lock lock = new ReentrantLock();
    private final Condition processorNotifyCondition = lock.newCondition();

    @Override
    /**
    * sequence: 消费者下一个想要得到的序号
    * cursorSequence: 当前容器中最大序号
    * 
    **/
    public long waitFor(long sequence, Sequence cursorSequence, Sequence dependentSequence, SequenceBarrier barrier) throws AlertException, InterruptedException{
        long availableSequence;
        if (cursorSequence.get() < sequence){
            lock.lock();
            try{
                while (cursorSequence.get() < sequence){
                    barrier.checkAlert();
                    processorNotifyCondition.await();
                }
            }finally{
                lock.unlock();
            }
        }

        while ((availableSequence = dependentSequence.get()) < sequence){
            barrier.checkAlert();
            ThreadHints.onSpinWait();
        }

        return availableSequence;
    }

    @Override
    public void signalAllWhenBlocking() {
        lock.lock();
        try{
            processorNotifyCondition.signalAll();
        }finally {
            lock.unlock();
        }
    }
}

9 EventProcessor核心机制深度分析

public interface EventProcessor extends Runnable{
    /**
     * Get a reference to the {@link Sequence} being used by this {@link EventProcessor}.
     */
    Sequence getSequence();

    /**
     * Signal that this EventProcessor should stop when it has finished consuming at the next clean break.
     */
    void halt();

    boolean isRunning();
}

public final class BatchEventProcessor<T>implements EventProcessor{
    private static final int IDLE = 0;
    private static final int HALTED = IDLE + 1;
    private static final int RUNNING = HALTED + 1;

    // 是否正在运行
    private final AtomicInteger running = new AtomicInteger(IDLE);
    // 运行异常处理
    private ExceptionHandler<? super T> exceptionHandler = new FatalExceptionHandler();
    // 获取数据
    private final DataProvider<T> dataProvider;
    // 生产者与消费者的进度管理
    private final SequenceBarrier sequenceBarrier;
    // 消费者接口
    private final EventHandler<? super T> eventHandler;
    private final Sequence sequence = new Sequence(Sequencer.INITIAL_CURSOR_VALUE);
	// 超时处理
    private final TimeoutHandler timeoutHandler;
    private final BatchStartAware batchStartAware;
	
    @Override
    public void run(){
        if (running.compareAndSet(IDLE, RUNNING)){
            sequenceBarrier.clearAlert();// 清空栅栏
            notifyStart(); // 唤醒线程工作
            try{
                // 实际工作
                if (running.get() == RUNNING) processEvents();   
            }finally{
                notifyShutdown();
                running.set(IDLE);
            }
        }else{
            // This is a little bit of guess work.  The running state could of changed to HALTED by this point.  However, Java does not have compareAndExchange which is the only way to get it exactly correct.
            if (running.get() == RUNNING){
                throw new IllegalStateException("Thread is already running");
            }else{
                earlyExit();
            }
        }
    }
    
    private void processEvents(){
        T event = null;
        long nextSequence = sequence.get() + 1L;

        while (true){
            try{
                // 真实可用序号
                final long availableSequence = sequenceBarrier.waitFor(nextSequence);
                	
                // batchStart钩子
                if (batchStartAware != null){
                    batchStartAware.onBatchStart(availableSequence - nextSequence + 1);
                }

                // 当下一个希望消费的Sequence小于当前可消费的Sequence,则把所有可消费的Sequence都消费掉。
                while (nextSequence <= availableSequence){
                    event = dataProvider.get(nextSequence);
                    eventHandler.onEvent(event, nextSequence, nextSequence == availableSequence);
                    nextSequence++;
                }

                sequence.set(availableSequence);
            }catch (final TimeoutException e){
                notifyTimeout(sequence.get());
            }catch (final AlertException ex){
                if (running.get() != RUNNING) break;
            }catch (final Throwable ex){
                exceptionHandler.handleEventException(ex, nextSequence, event);
                sequence.set(nextSequence);
                nextSequence++;
            }
        }
    }
}

五 Netty整合Disruptor

1 Netty Common(Netty 公共项目)

@Data
// Netty传输类
public class TranslatorData implements Serializable {
	private String id;
	private String name;
	private String message;	//传输消息体内容
}

@Data
// Disruptor传输类
public class TranslatorDataWapper {
	private TranslatorData data;
	private ChannelHandlerContext ctx;
}

@Data
// 公共消费者
public abstract class MessageConsumer implements WorkHandler<TranslatorDataWapper> {
	protected String consumerId;
}

@Data
// 公共生产者
public class MessageProducer {
	private String producerId;
	private RingBuffer<TranslatorDataWapper> ringBuffer;
    
    public void onData(TranslatorData data, ChannelHandlerContext ctx) {
		long sequence = ringBuffer.next();
		try {
			TranslatorDataWapper wapper = ringBuffer.get(sequence);
			wapper.setData(data);
			wapper.setCtx(ctx);
		} finally {
			ringBuffer.publish(sequence);
		}
	}
}

// 单例实现
public class RingBufferWorkerPoolFactory {
    
	private static class SingletonHolder {
		static final RingBufferWorkerPoolFactory instance = new RingBufferWorkerPoolFactory();
	}

	private RingBufferWorkerPoolFactory() {
	}

	public static RingBufferWorkerPoolFactory getInstance() {
		return SingletonHolder.instance;
	}

    // 生产者池化
	private static Map<String, MessageProducer> producers = new ConcurrentHashMap<String, MessageProducer>();

    // 消费者池化
	private static Map<String, MessageConsumer> consumers = new ConcurrentHashMap<String, MessageConsumer>();

	private RingBuffer<TranslatorDataWapper> ringBuffer;

	private SequenceBarrier sequenceBarrier;

	private WorkerPool<TranslatorDataWapper> workerPool;

	public void initAndStart(ProducerType type, int bufferSize, WaitStrategy waitStrategy,
			MessageConsumer[] messageConsumers) {
		// 1. 构建ringBuffer对象
		this.ringBuffer = RingBuffer.create(type, new EventFactory<TranslatorDataWapper>() {
			public TranslatorDataWapper newInstance() {
				return new TranslatorDataWapper();
			}
		}, bufferSize, waitStrategy);
		// 2.设置序号栅栏
		this.sequenceBarrier = this.ringBuffer.newBarrier();
		// 3.设置工作池
		this.workerPool = new WorkerPool<TranslatorDataWapper>(this.ringBuffer, this.sequenceBarrier,new EventExceptionHandler(), messageConsumers);
		// 4 把所构建的消费者置入池中
		for (MessageConsumer mc : messageConsumers) {
			this.consumers.put(mc.getConsumerId(), mc);
		}
		// 5 添加我们的sequences
		this.ringBuffer.addGatingSequences(this.workerPool.getWorkerSequences());
		// 6 启动我们的工作池
		this.workerPool.start(Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() / 2));
	}

	public MessageProducer getMessageProducer(String producerId) {
		MessageProducer messageProducer = this.producers.get(producerId);
		if (null == messageProducer) {
			messageProducer = new MessageProducer(producerId, this.ringBuffer);
			this.producers.put(producerId, messageProducer);
		}
		return messageProducer;
	}

	/**
	 * 异常静态类
	 * 
	 * @author Alienware
	 *
	 */
	static class EventExceptionHandler implements ExceptionHandler<TranslatorDataWapper> {
		public void handleEventException(Throwable ex, long sequence, TranslatorDataWapper event) {
		}

		public void handleOnStartException(Throwable ex) {
		}

		public void handleOnShutdownException(Throwable ex) {
		}
	}
}

/**
 * Marshalling工厂
 * Marshalling是JBoss的序列化框架,Netty提供了Marshalling的编码和解码方便用户使用。
 */
public final class MarshallingCodeCFactory {

    /**
     * 创建Jboss Marshalling解码器MarshallingDecoder
     * @return MarshallingDecoder
     */
    public static MarshallingDecoder buildMarshallingDecoder() {
    	//首先通过Marshalling工具类的精通方法获取Marshalling实例对象 参数serial标识创建的是java序列化工厂对象。
		final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
		//创建了MarshallingConfiguration对象,配置了版本号为5 
		final MarshallingConfiguration configuration = new MarshallingConfiguration();
		configuration.setVersion(5);
		//根据marshallerFactory和configuration创建provider
		UnmarshallerProvider provider = new DefaultUnmarshallerProvider(marshallerFactory, configuration);
		//构建Netty的MarshallingDecoder对象,俩个参数分别为provider和单个消息序列化后的最大长度
		MarshallingDecoder decoder = new MarshallingDecoder(provider, 1024 * 1024 * 1);
		return decoder;
    }

    /**
     * 创建Jboss Marshalling编码器MarshallingEncoder
     * @return MarshallingEncoder
     */
    public static MarshallingEncoder buildMarshallingEncoder() {
		final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
		final MarshallingConfiguration configuration = new MarshallingConfiguration();
		configuration.setVersion(5);
		MarshallerProvider provider = new DefaultMarshallerProvider(marshallerFactory, configuration);
		//构建Netty的MarshallingEncoder对象,MarshallingEncoder用于实现序列化接口的POJO对象序列化为二进制数组
		MarshallingEncoder encoder = new MarshallingEncoder(provider);
		return encoder;
    }
}

2 Netty Server

// Netty服务器
public class NettyServer {
	public NettyServer() {
		// 1. 创建两个工作线程组: 一个用于接受网络请求的线程组. 另一个用于实际处理业务的线程组
		EventLoopGroup bossGroup = new NioEventLoopGroup();
		EventLoopGroup workGroup = new NioEventLoopGroup();
		// 2 辅助类
		ServerBootstrap serverBootstrap = new ServerBootstrap();
		try {
			serverBootstrap.group(bossGroup, workGroup)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 1024)
                // 表示缓存区动态调配(自适应)
                .option(ChannelOption.RCVBUF_ALLOCATOR, AdaptiveRecvByteBufAllocator.DEFAULT)
                // 缓存区 池化操作
                .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                .handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel sc) throws Exception {
                    Pipeline pipeline = sc.pipeline();
                    pipeline.addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
                    pipeline.addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
                    pipeline.addLast(new ServerHandler());
                }
            });
            // 绑定端口,同步等等请求连接
            ChannelFuture cf = serverBootstrap.bind(8765).sync();
            System.err.println("Server Startup...");
            cf.channel().closeFuture().sync();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			// 优雅停机
			bossGroup.shutdownGracefully();
			workGroup.shutdownGracefully();
			System.err.println("Sever ShutDown...");
		}
	}
}

// 实际处理器
public class ServerHandler extends ChannelInboundHandlerAdapter {

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		TranslatorData request = (TranslatorData) msg;
		// 自已的应用服务应该有一个ID生成规则
		String producerId = "code:sessionId:001";
		MessageProducer messageProducer = RingBufferWorkerPoolFactory.getInstance()
            									.getMessageProducer(producerId);	
		messageProducer.onData(request, ctx);
	}
}

// 具体消费者
public class MessageConsumerImpl4Server extends MessageConsumer {

	public MessageConsumerImpl4Server(String consumerId) {
		super(consumerId);
	}

	public void onEvent(TranslatorDataWapper event) throws Exception {
		TranslatorData request = event.getData();
		ChannelHandlerContext ctx = event.getCtx();
		//1.业务处理逻辑:
    	System.err.println("Sever端: id= " + request.getId() 
                           + ", name= " + request.getName() 
                           + ", message= " + request.getMessage());
    	
    	//2.回送响应信息:
    	TranslatorData response = new TranslatorData();
    	response.setId("resp: " + request.getId());
    	response.setName("resp: " + request.getName());
    	response.setMessage("resp: " + request.getMessage());
    	//写出response响应信息:
    	ctx.writeAndFlush(response);
	}

}

// 启动类
public class NettyServerApplication {

	public static void main(String[] args) {
		SpringApplication.run(NettyServerApplication.class, args);
		
		MessageConsumer[] conusmers = new MessageConsumer[4];
		for(int i =0; i < conusmers.length; i++) {
			MessageConsumer messageConsumer = new MessageConsumerImpl4Server("code:serverId:" + i);
			conusmers[i] = messageConsumer;
		}
		RingBufferWorkerPoolFactory.getInstance().initAndStart(ProducerType.MULTI,
				1024*1024,
				//new YieldingWaitStrategy(),
				new BlockingWaitStrategy(),
				conusmers);
		new NettyServer();
	}
}

3 Netty Client

// Netty客户端
public class NettyClient {
	public static final String HOST = "127.0.0.1";
	public static final int PORT = 8765;
	
	//扩展 完善 池化: ConcurrentHashMap<KEY -> String, Value -> Channel> 
	private Channel channel;	
	
	//1. 创建工作线程组: 用于实际处理业务的线程组
	private EventLoopGroup workGroup = new NioEventLoopGroup();
	
	private ChannelFuture cf;
	
	public NettyClient() {
		this.connect(HOST, PORT);
	}

	private void connect(String host, int port) {
		//2 辅助类(注意Client 和 Server 不一样)
		Bootstrap bootstrap = new Bootstrap();
		try {
			bootstrap.group(workGroup)
			.channel(NioSocketChannel.class)
			//表示缓存区动态调配(自适应)
			.option(ChannelOption.RCVBUF_ALLOCATOR, AdaptiveRecvByteBufAllocator.DEFAULT)
			//缓存区 池化操作
			.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
			.handler(new LoggingHandler(LogLevel.INFO))
			.handler(new ChannelInitializer<SocketChannel>() {
				@Override
				protected void initChannel(SocketChannel sc) throws Exception {
					sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
					sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
					sc.pipeline().addLast(new ClientHandler());
				}
			});
			//绑定端口,同步等等请求连接
			this.cf = bootstrap.connect(host, port).sync();
			System.err.println("Client connected...");
			//接下来就进行数据的发送, 但是首先我们要获取channel:
			this.channel = cf.channel();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	public void sendData(){
		
		for(int i =0; i <10; i++){
			TranslatorData request = new TranslatorData();
			request.setId("" + i);
			request.setName("请求消息名称 " + i);
			request.setMessage("请求消息内容 " + i);
			this.channel.writeAndFlush(request);
		}
	}
	
	public void close() throws Exception {
		cf.channel().closeFuture().sync();
		//优雅停机
		workGroup.shutdownGracefully();
		System.err.println("Sever ShutDown...");		
	}
}

// 客户端处理器
public class ClientHandler extends ChannelInboundHandlerAdapter {
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		TranslatorData response = (TranslatorData) msg;
		String producerId = "code:seesionId:002";
		MessageProducer messageProducer = RingBufferWorkerPoolFactory.getInstance().getMessageProducer(producerId);
		messageProducer.onData(response, ctx);
	}
}

// 具体的消费者
public class MessageConsumerImpl4Client extends MessageConsumer {
	public MessageConsumerImpl4Client(String consumerId) {
		super(consumerId);
	}

	public void onEvent(TranslatorDataWapper event) throws Exception {
		TranslatorData response = event.getData();
		ChannelHandlerContext ctx = event.getCtx();
		//业务逻辑处理:
		try {
    		System.err.println("Client端: id= " + response.getId() 
			+ ", name= " + response.getName()
			+ ", message= " + response.getMessage());
		} finally {
			ReferenceCountUtil.release(response);
		}
	}
}

// Netty客户端启动类
public class NettyClientApplication {
	public static void main(String[] args) {
		SpringApplication.run(NettyClientApplication.class, args);
		MessageConsumer[] conusmers = new MessageConsumer[4];
		for(int i =0; i < conusmers.length; i++) {
			MessageConsumer messageConsumer = new MessageConsumerImpl4Client("code:clientId:" + i);
			conusmers[i] = messageConsumer;
		}
		RingBufferWorkerPoolFactory.getInstance().initAndStart(ProducerType.MULTI,
                                                               1024*1024,
                                                               //new YieldingWaitStrategy(),
                                                               new BlockingWaitStrategy(),
                                                               conusmers);
		//建立连接并发送消息
		new NettyClient().sendData();
	}
}

六 分布式统一ID生成抗压策略

1 ID生成策略

  • 最基本
    • java.util.UUID:保证唯一性,但没有排序,需要额外加时间字段
    • 雪花算法、数据库sequence序列、自增ID
  • 顺序ID生成工具
    • KeyUtil生成ID,减少索引,减少硬盘占用。
  • 业务ID生成方式
    • 维度1(区域)+维度2(类型)+UUID:10010+001+UUID

2 顺序ID生成工具

// 1. 引入jar包,com.fasterxml.uuid java-uuid-generator

// 2. 编写keytool类
public class KeyUtil {
	public static String generatorUUID(){
		TimeBasedGenerator timeBasedGenerator = Generators.timeBasedGenerator(EthernetAddress.fromInterface());
		return timeBasedGenerator.generate().toString();
	}
}

3 业务ID生成方式

  • 节省索引,节省字段,节省占用
select * from goods_shelf gs where id > '1001000000000000000000000000000000000' 
						   and id < '2000000000000000000000000000000000000' 

4 高并发统一ID生成策略抗压

(1)统一ID生成策略服务

  • 如何解决ID生成在并发下的重复生成问题
  • 如何承载高并发ID生成的性能瓶颈问题

(2)如何落地

  • 使用Zookeeper的分布式锁实现
  • 使用Redis缓存,利用Redis分布锁实现,存在超时重试,但是QPS下降

(3)业界主流的分布式ID生成器策略:

  • 实现1:提前加载
    • 提前加载,放到内存中
    • 并发获取,采用Disruptor框架去提升性能
  • 实现2:单点生成方式(保底策略)
    • 固定一个机器节点来生成一个唯一ID,全局唯一
    • 需要根据业务规则拼接:机器码+时间戳+自增序列

5 经典NTP问题

  • 为什么需要拼接自增序列?因为高并发场景下,生成的ID十分巨大,会暴露出NTP问题
  • NTP问题:NTP是网络时间协议,用来同步网络中各个计算机的时间的协议。NTP是服务器系统的时间会定时去获取,然后进行更新校准,导致时间戳重复。

6 实现架构

posted @ 2019-02-22 15:15  月下小魔王  阅读(602)  评论(0编辑  收藏  举报