网络编程 - 6(Disruptor + Netty实现600W+连接网络应用)

336 阅读14分钟

1. Disruptor框架

1.1 什么是Disruptor?

诞生前提:LMAX是英国外汇交易公司,目标是成为世界上最快的交易平台。为了实现这一点,这家公司的技术团队使用 Java 平台实现非常低的延迟和高吞吐量的交易系统。经过一系列性能测试表明,使用队列在系统的各个阶段之间传递数据会导致延迟,当然吞吐量也就很难上的去,因此他们技术团队专注于优化这个领域,所以Disruptor诞生了。

Disruptor是一个通用的解决方案,用于解决并发编程中的难题(低延迟和高吞吐量)。本质还是一个队列(环形),与其他队列相似,也是基于生产者消费者设计,只不过这个队列很特别是一个环形队列。这个队列能够在无锁的条件下进行并行消费,也可以根据消费者之间的依赖关系进行先后次序消费。

它与并发编程中的阻塞队列有什么不同?

  • 低延迟高吞吐
  • 快!!!

image.png

1.2 使用Disruptor通用步骤

  1. 创建工厂类,用于生产Event对象
  2. 创建consumer监听类,用于监听并处理Event
  3. 创建Disruptor对象,并初始化一系列参数:工厂类、RingBuffer大小、线程池、单生产者或多生产者、Event等待策略等
  4. 编写Product组件,向Disruptor容器中投递Event

1.3 核心概念

1.3.1 Disruptor

它是一个辅助类,持有RingBuffer、消费者线程池Executor、消费者仓库ConsumerRepository等饮用。

1.3.2 RingBuffer环形缓存器

RingBuffer基于数组的实现,数据结构是一个首尾相接的环,用做在不同的上下文(线程)间传递数据的buffer。RingBuffer拥有一个Sequence序号器,这个序号器指向数组中下一个可用元素。

image.png

1.3.3 Sequencer序号器

Sequencer序号器是Disruptor核心。 此接口有2个实现类:

  • SingleProductSequencer 单生产者
  • MultiProdunctSequencer 多生产者

1.3.4 Sequencer序号

Sequencer序号器中有Sequence序号,通过顺序递增的序号来编号和管理进行交换的event。

event处理过程总是沿着序号逐个递增处理。

一个Sequence用于跟踪标识某个特定的事件处理者的处理进度。Producer和Consumer都有自己的Sequence,用于判断Consumer和Producer之间平衡,防止生产快,消费慢或者生产慢消费快等情况(上下游速度不一致问题)

  • 解决上下游消费速度不一致的问题
  • 异步提速
  • 削峰填谷

1.3.5 WaitStrategy等待策略

决定一个Consumer将如何等待Producer将Event置入RingBuffer

主要策略有:

  • BlockingWaitStrategy: 阻塞等待策略,最低效的策略,但是对CPU的消耗最小并且在各种不同的部署环境能够提供更加一致的性能表现。
  • SleepingWaitStrategy: 休眠等待策略,性能表现跟BlockingWaitStrategy差不多,对于CPU的消耗也类似,但是对生产者线程影响的最小,适合用户异步日志类似的场景。
  • YieldingWatiStratety: 产生等待策略,性能最好,适合用于低延迟的系统,在要求极高性能且事件处理线程数小于CPU核心逻辑数的场景中,推荐使用。是无锁并行

1.3.6 Event

从Producer到Consumer过程中所处理的数据单元

1.3.7 EventHandler

由用户实现,并且代表Disruptor中的一个消费者接口,我们的消费者逻辑都写在这里。

1.2 单生产者单消费者案例

目标: 演示Disruptor高性能队列的基本用法,创建循环100个订单消息并消费

步骤:

  1. 创建OrderEventFactory来创建OrderEvent实例对象
  2. 创建consumer处理者OrderEventHandler,当Producer投递一条数据时,此Handler进行处理
  3. 编写核心类Main创建disruptor对象,让其与consumer处理者OrderEventHandler绑定,启动disruptor。
  4. 通过disruptor对象获取ringBuffer容器。
  5. 创建生产者OrderEventProducer,将消息放到ringBuffer容器。
  6. 循环100次,通过sendData()投递消息。sendData()当法的最后将消息发布出去,只有发布出去,消费者才能收到

image.png

maven依赖

<dependency>
    <groupId>com.lmax</groupId>
    <artifactId>disruptor</artifactId>
    <version>3.3.4</version>
</dependency>

1.2.1 OrderEvent

定义需要处理的OrderEvent类

package hero.disruptor.demo;

/**
 * 订单对象,生产者生产订单对象,消费者消费订单对象
 */
public class OrderEvent {

    // 订单价格
    private long value;

    public long getValue() {
        return value;
    }

    public void setValue(long value) {
        this.value = value;
    }
}

1.2.2 OrderEventFactory

定义工厂类OrderEventFactory,用于创建OrderEvent对象。

package hero.disruptor.demo;

import com.lmax.disruptor.EventFactory;

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

1.2.3 OrderEventHandler

定义Event监听及处理类OrderEventHandler,用于处理OrderEvent

package hero.disruptor.demo;


import com.lmax.disruptor.EventHandler;

/**
 * 定义Event监听及处理类OrderEventHandler,用于处理OrderEvent
 */
public class OrderEventHandler implements EventHandler<OrderEvent> {

    @Override
    public void onEvent(OrderEvent orderEvent, long l, boolean b) throws Exception {
        // 展示订单价格
        System.err.println(("消费者:"+ orderEvent.getValue()));
    }
}

1.2.4 TestDisruptor

定义测试类,创建Disruptor对象,并初始化一系列参数:工厂类、RingBuffer大小、线程池、单生产者或多生产者、Event等待策略。

package hero.disruptor.demo;

import com.lmax.disruptor.BlockingWaitStrategy;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;

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

public class TestDisruptor {
    public static void main(String[] args) {

        OrderEventFactory orderEventFactory = new OrderEventFactory();
        int ringBufferSize = 8;
        ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

        // 1. 实例化对象
        Disruptor<OrderEvent> disruptor = new Disruptor<>(
                orderEventFactory, // eventFactory:消息(event)工厂对象
                ringBufferSize, // ringBufferSize: 容器的长度
                executor, // executor:线程池,建议使用自定义的线程池,线程上限。
                ProducerType.SINGLE, // ProducerType:单生产者或多生产者
                new BlockingWaitStrategy()); // waitStrategy:等待策略

        // 2. 添加消费者的监听(去构建disruptor与消费者的一个关联关系)
        disruptor.handleEventsWith(new OrderEventHandler());
        // 3. 启动disruptor
        disruptor.start();
        // 4. 取到容器后,通过生产者去生产消息
        RingBuffer<OrderEvent> ringBuffer = disruptor.getRingBuffer();
        OrderEventProducer producer = new OrderEventProducer(ringBuffer);

        ByteBuffer byteBuffer = ByteBuffer.allocate(8);
        for (int i = 0; i < 100; i++) {
            byteBuffer.putLong(0,i);
            producer.sendData(byteBuffer);
        }

        disruptor.shutdown();
        executor.shutdown();

    }
}

1.2.5 OrderEventProducer

定义Producer类,向Disruptor容器中去投递数据。

package hero.disruptor.demo;

import com.lmax.disruptor.RingBuffer;

import java.nio.ByteBuffer;

/**
 * 定义Producer类,向Disruptor容器中去投递数据。生产者
 */
public class OrderEventProducer {

    private RingBuffer<OrderEvent> ringBuffer;

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

    public void sendData(ByteBuffer data) {
        // 1. 在生产者发送消息之前,首先要从ringBuffer中找到一个可用的序号
        long sequence = ringBuffer.next();
        try {
            // 2. 根据这个序号找到具体的OrderEvent元素,此时获取到的OrderEvent对象是一个没有被赋值的空对象value
            OrderEvent orderEvent = ringBuffer.get(sequence);
            // 3. 设置订单价格
            orderEvent.setValue(data.getLong(0));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 4. 提交发布操作
            ringBuffer.publish(sequence);
        }
    }
}

1.3 多生产者和多消费者

image.png

image.png

1.3.1 Order

package hero.disruptor.multi;

import java.math.BigDecimal;

public class Order {

    private String id;
    private String name;
    private BigDecimal price;

    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 BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }
}

1.3.2 ConsumerHandler

package hero.disruptor.multi;

import com.lmax.disruptor.WorkHandler;

import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class ConsumerHandler implements WorkHandler<Order> {

    // 消费者ID
    private String consumerId;
    // 计数统计,多个消费者,所有的消费者总共消费了多个消息。
    private static AtomicInteger count = new AtomicInteger(0);
    private Random random = new Random();

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

    /**
     * 当生产者发布一个sequence,ringBuffer中一个序号,里面生产者生产出来的消息,生产者最后publish发布序号
     * 消费者会监听,如果监听到,就会从ringBuffer中取出这个序号,取到里面消息
     * @param order
     * @throws Exception
     */
    @Override
    public void onEvent(Order order) throws Exception {
        // 模拟消费者处理消息的耗时,设定1-4毫秒之间
        TimeUnit.MICROSECONDS.sleep(random.nextInt(5));

        System.err.println("当前消费者:"+this.consumerId+",消费信息 ID:"+ order.getId());

        // count计数器加1,表示消费了一个消息
        count.incrementAndGet();
    }

    /**
     * 返回所有消费者总共消费的消息的个数。
     */
    public int getCount() {
        return count.get();
    }
}

1.3.3 Producer

package hero.disruptor.multi;

import com.lmax.disruptor.RingBuffer;

public class Producer {

    private RingBuffer<Order> ringBuffer;

    // 为生产者绑定ringBuffer
    public Producer(RingBuffer<Order> ringBuffer) {
        this.ringBuffer = ringBuffer;
    }

    // 发送数据
    public void sendData(String uuid) {
        // 获取到可用的sequence
        long sequence = ringBuffer.next();
        try {
            Order order = ringBuffer.get(sequence);
            order.setId(uuid);
        } finally {
            //发布序号
            ringBuffer.publish(sequence);
        }
    }
}

1.3.4 TestMultiDisruptor

package hero.disruptor.multi;

import com.lmax.disruptor.*;
import com.lmax.disruptor.dsl.ProducerType;

import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class TestMultiDisruptor {
    public static void main(String[] args) throws InterruptedException {
        // 1.创建RingBuffer
        RingBuffer<Order> ringBuffer = RingBuffer.create(
                ProducerType.MULTI, //多生产者
                Order::new,
                1024 * 1024,
                new YieldingWaitStrategy());
        // 2.创建ringBuffer屏障
        SequenceBarrier sequenceBarrier = ringBuffer.newBarrier();
        // 3.创建多个消费者组
        ConsumerHandler[] consumerHandlers = new ConsumerHandler[10];

        for (int i = 0; i < consumerHandlers.length; i++) {
            consumerHandlers[i] = new ConsumerHandler("C" + i);
        }
        // 4. 构建多消费者工作池
        WorkerPool<Order> workerPool = new WorkerPool<>(
                ringBuffer,
                sequenceBarrier,
                new EventExceptionHandler(),
                consumerHandlers);
        // 5. 设置多个消费者的sequence序号,用于单独统计消费者的消费进度。消费进度让ringBuffer知道
        ringBuffer.addGatingSequences(workerPool.getWorkerSequences());
        // 6. 启动workerPool - 在实际开发中,自定义线程池
        workerPool.start(Executors.newFixedThreadPool(5));

        // 创建100个生产者,每个生产者发送100条数据,总共投递10000
        CountDownLatch latch = new CountDownLatch(1);
        for (int i = 0; i < 100; i++) {
            Producer producer = new Producer(ringBuffer);

            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        //每次一个生产者创建后就处理等待。先创建100个生产者,创建完100个生产者后再去发送数据。
                        latch.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    for (int j = 0; j < 100; j++) {
                        producer.sendData(UUID.randomUUID().toString());
                    }
                }
            }).start();
        }
        // 把所有线程都创建完
        TimeUnit.SECONDS.sleep(2);
        // 唤醒,开始执行
        latch.countDown();
        //休眠10s,让生产者将100次循环走完
        TimeUnit.SECONDS.sleep(10);
        System.err.println("任务总数:" + consumerHandlers[0].getCount());
    }

    static class EventExceptionHandler implements ExceptionHandler<Order> {
        //消费时出现异常
        @Override
        public void handleEventException(Throwable throwable, long l, Order order) {
        }
        //启动时出现异常
        @Override
        public void handleOnStartException(Throwable throwable) {
        }
        //停止时出现异常
        @Override
        public void handleOnShutdownException(Throwable throwable) {
        }
    }
}

1.4 使用Disruptor提升Netty应用性能

1.4.1 构建Netty网络模型

image.png

1.4.1.1 构建基础网络应用环境

  • disruptor-netty-com是通用包
  • disruptor-netty-client是客户端
  • disruptor-netty-server是服务端

1.4.1.2 TranslatorData

传输的数据对象

package hero.disruptor.com;

import java.io.Serializable;

/**
 * 传输的数据对象
 */
public class TranslatorData implements Serializable {
    private static final long serialVersionUID = 8763561286199081881L;

    private String id;
    private String name;
    private String message;

    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 String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

1.4.1.3 NettyServer

package hero.disruptor.server;

import hero.disruptor.com.MarshallingCodeCFactory;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.AdaptiveRecvByteBufAllocator;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

public class NettyServer {

    public NettyServer() {
        //1. 创建两个工作线程组: 一个用于接受网络请求的线程组. 另一个用于实际处理业务的线程组
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup 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 {
                            sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
                            sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
                            sc.pipeline().addLast(new ServerHandler());
                        }
                    });
            ChannelFuture channelFuture = serverBootstrap.bind(9999).sync();
            System.err.println("Sever StartUp...");
            channelFuture.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
            System.err.println("Sever ShutDown...");
        }
    }
}

1.4.1.4 ServerHandler

package hero.disruptor.server;

import hero.disruptor.com.TranslatorData;
import hero.disruptor.disruptor.MessageProducer;
import hero.disruptor.disruptor.RingBufferWorkerPoolFactory;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class ServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        TranslatorData request = (TranslatorData) msg;
        System.err.println("Sever端: id= " + request.getId() + ", name= " + request.getName() + ", message= " + request.getMessage());
        //数据库持久化操作 IO读写 ---> 交给一个线程池 去异步的调用执行
        TranslatorData response = new TranslatorData();
        response.setId("resp: " + request.getId());
        response.setName("resp: " + request.getName());
        response.setMessage("resp: " + request.getMessage());
        //写出response响应信息:
        ctx.writeAndFlush(response);
    }
}

1.4.1.5 NettyClient

package hero.disruptor.client;

import hero.disruptor.com.MarshallingCodeCFactory;
import hero.disruptor.com.TranslatorData;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;


public class NettyClient {
    public static final String HOST = "127.0.0.1";
    public static final int PORT = 9999;

    //扩展 完善 池化: 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(NettyClient.HOST, NettyClient.PORT).sync();
            System.err.println("Client connected...");

            //接下来就进行数据的发送, 但是首先我们要获取channel:
            this.channel = cf.channel();

        } catch (Exception 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);
            System.out.println("发送信息:" +i);
        }
    }

    public void close() throws Exception {
        cf.channel().closeFuture().sync();
        workGroup.shutdownGracefully();
        System.err.println("Sever ShutDown...");
    }
}

1.4.1.6 ClientHandler

package hero.disruptor.client;

import hero.disruptor.com.TranslatorData;
import hero.disruptor.disruptor.MessageProducer;
import hero.disruptor.disruptor.RingBufferWorkerPoolFactory;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;

public class ClientHandler extends ChannelInboundHandlerAdapter {

    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("收到服务端返回信息:" + msg);
        try {
            TranslatorData response = (TranslatorData) msg;
            System.err.println("Client端: id= " + response.getId()
                    + ", name= " + response.getName()
                    + ", message= " + response.getMessage());
        } finally {
            //一定要注意 用完了缓存 要进行释放
            ReferenceCountUtil.release(msg);
        }
    }
}

1.4.1.7 解析器:MarshallingCodeCFactory

package hero.disruptor.com;

import io.netty.handler.codec.marshalling.*;
import org.jboss.marshalling.MarshallerFactory;
import org.jboss.marshalling.Marshalling;
import org.jboss.marshalling.MarshallingConfiguration;

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;
    }
}

1.4.1.8 启动类

服务端启动类

package hero.disruptor;

import com.lmax.disruptor.YieldingWaitStrategy;
import com.lmax.disruptor.dsl.ProducerType;
import hero.disruptor.disruptor.MessageConsumer;
import hero.disruptor.disruptor.RingBufferWorkerPoolFactory;
import hero.disruptor.server.MessageConsumerImpl4Server;
import hero.disruptor.server.NettyServer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class NettyServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(NettyServerApplication.class, args);
        new NettyServer();
    }
}

客户端启动类

package hero.disruptor;

import com.lmax.disruptor.YieldingWaitStrategy;
import com.lmax.disruptor.dsl.ProducerType;
import hero.disruptor.client.MessageConsumerImpl4Server;
import hero.disruptor.client.NettyClient;
import hero.disruptor.disruptor.MessageConsumer;
import hero.disruptor.disruptor.RingBufferWorkerPoolFactory;
import hero.disruptor.server.NettyServer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class NettyClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(NettyClientApplication.class, args);
        new NettyClient().sendData();
    }
}

1.4.2 整合Disruptor

在使用Netty进行接收处理数据时,尽量不要在工作线程上编写自己的代理逻辑,会降低netty性能。可以利用异步机制,如使用线程池异步处理,如果使用线程池就意味使用阻塞对列,可以替换为Disruptor提高性能。

加入disruptor提升性能:

image.png

Event是客户端发送到服务端的数据,serverHandler获取到event之后,不再serverHandler处理数据,将Event通过生产者交给Disruptor组件。消费者c1,c2,c3通过负载均衡去消费投递过来的数据。服务端最终返回一个响应数据给客户端。客户端这边也不是在ClientHandler中处理数据,也要构建一个生产者消费者模型,有多个线程去处理。

1.4.2.1 TranslatorDataWapper

传输的对象包装类

package hero.disruptor.entity;

import hero.disruptor.com.TranslatorData;
import io.netty.channel.ChannelHandlerContext;

public class TranslatorDataWapper {

    private TranslatorData data;

    private ChannelHandlerContext ctx;

    public TranslatorData getData() {
        return data;
    }

    public void setData(TranslatorData data) {
        this.data = data;
    }

    public ChannelHandlerContext getCtx() {
        return ctx;
    }

    public void setCtx(ChannelHandlerContext ctx) {
        this.ctx = ctx;
    }
}

1.4.2.2 生产者MessageProducer

package hero.disruptor.disruptor;

import com.lmax.disruptor.RingBuffer;
import hero.disruptor.com.TranslatorData;
import hero.disruptor.entity.TranslatorDataWapper;
import io.netty.channel.ChannelHandlerContext;

public class MessageProducer {

    private String producerId;

    private RingBuffer<TranslatorDataWapper> ringBuffer;

    public MessageProducer(String producerId, RingBuffer<TranslatorDataWapper> ringBuffer) {
        this.producerId = producerId;
        this.ringBuffer = ringBuffer;
    }

    public void onData(TranslatorData data, ChannelHandlerContext ctx) {
        long sequence = ringBuffer.next();
        try {
            TranslatorDataWapper translatorDataWapper = ringBuffer.get(sequence);
            translatorDataWapper.setData(data);
            translatorDataWapper.setCtx(ctx);
        } finally {
            ringBuffer.publish(sequence);
        }
    }
}

1.4.2.3 消费者MessageConsumer

package hero.disruptor.disruptor;

import com.lmax.disruptor.WorkHandler;
import hero.disruptor.entity.TranslatorDataWapper;

public abstract class MessageConsumer implements WorkHandler<TranslatorDataWapper> {

    protected String consumerId;

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

    public String getConsumerId() {
        return consumerId;
    }

    public void setConsumerId(String consumerId) {
        this.consumerId = consumerId;
    }
}

1.4.2.4 创建连接池工厂类

package hero.disruptor.disruptor;

import com.lmax.disruptor.*;
import com.lmax.disruptor.dsl.ProducerType;
import hero.disruptor.entity.TranslatorDataWapper;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

public class RingBufferWorkerPoolFactory {

    // 单例
    private static class SingletonHandler {
        static final RingBufferWorkerPoolFactory instance = new RingBufferWorkerPoolFactory();
    }

    private RingBufferWorkerPoolFactory() {

    }

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

    // 需要生产者池和消费者池管理生产和消费
    private static Map<String, MessageConsumer> consumers = new ConcurrentHashMap<String, MessageConsumer>();
    private static Map<String, MessageProducer> producers = new ConcurrentHashMap<String, MessageProducer>();

    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>() {
                    @Override
                    public TranslatorDataWapper newInstance() {
                        return new TranslatorDataWapper();
                    }
                },
                bufferSize,
                waitStrategy);
        // 2. 设置序号栅栏
        this.sequenceBarrier = ringBuffer.newBarrier();
        // 3. 设置工作池
        this.workerPool = new WorkerPool<TranslatorDataWapper>(this.ringBuffer, this.sequenceBarrier, new EventExceptionHandler(), messageConsumers);
        // 4. 把构建的消费者放到池中
        for (MessageConsumer messageConsumer : messageConsumers) {
            this.consumers.put(messageConsumer.getConsumerId(), messageConsumer);
        }
        // 5. 添加我们的sequence
        this.ringBuffer.addGatingSequences(this.workerPool.getWorkerSequences());
        // 6. 启动工作池
        this.workerPool.start(Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()/2));
    }

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

    /**
     * 异常静态类
     */
    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) {
        }
    }
}

1.4.2.5 修改serverHandler

package hero.disruptor.server;

import hero.disruptor.com.TranslatorData;
import hero.disruptor.disruptor.MessageProducer;
import hero.disruptor.disruptor.RingBufferWorkerPoolFactory;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

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);
    }
}

1.4.2.6 服务器端消费者:用来处理客户端发送来数据的逻辑

MessageConsumerImpl4Server

package hero.disruptor.server;

import hero.disruptor.com.TranslatorData;
import hero.disruptor.disruptor.MessageConsumer;
import hero.disruptor.entity.TranslatorDataWapper;
import io.netty.channel.ChannelHandlerContext;

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);
    }
}

1.4.2.7 修改clientHandler

package hero.disruptor.client;

import hero.disruptor.com.TranslatorData;
import hero.disruptor.disruptor.MessageProducer;
import hero.disruptor.disruptor.RingBufferWorkerPoolFactory;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;

public class ClientHandler extends ChannelInboundHandlerAdapter {

    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
       
        TranslatorData response = (TranslatorData) msg;
        //i消费者和生产者共用一个池,所以id不可以冲突,以后可以随机生成id(机器码:sessionId:标识)
        String producerId = "code:session:002";
        MessageProducer messageProducer = RingBufferWorkerPoolFactory.getInstance().getMessageProducer(producerId);
        messageProducer.onData(response, ctx);
    }
}

1.4.2.8 客户端消费者:客户端处理服务端返回数据

MessageConsumerImpl4Server

package hero.disruptor.client;

import hero.disruptor.com.TranslatorData;
import hero.disruptor.disruptor.MessageConsumer;
import hero.disruptor.entity.TranslatorDataWapper;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.ReferenceCountUtil;

/**
 * 客户端消费者
 */
public class MessageConsumerImpl4Server extends MessageConsumer {

    public MessageConsumerImpl4Server(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);
        }
    }
}

1.4.2.9 启动类

服务端

package hero.disruptor;

import com.lmax.disruptor.YieldingWaitStrategy;
import com.lmax.disruptor.dsl.ProducerType;
import hero.disruptor.disruptor.MessageConsumer;
import hero.disruptor.disruptor.RingBufferWorkerPoolFactory;
import hero.disruptor.server.MessageConsumerImpl4Server;
import hero.disruptor.server.NettyServer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

客户端

package hero.disruptor;

import com.lmax.disruptor.YieldingWaitStrategy;
import com.lmax.disruptor.dsl.ProducerType;
import hero.disruptor.client.MessageConsumerImpl4Server;
import hero.disruptor.client.NettyClient;
import hero.disruptor.disruptor.MessageConsumer;
import hero.disruptor.disruptor.RingBufferWorkerPoolFactory;
import hero.disruptor.server.NettyServer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class NettyClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(NettyClientApplication.class, args);

        MessageConsumer[] consumers = new MessageConsumer[4];
        for (int i = 0; i < consumers.length; i++) {
            MessageConsumer consumer = new MessageConsumerImpl4Server("code:clientId:" + i);
            consumers[i] = consumer;
        }
        RingBufferWorkerPoolFactory.getInstance().initAndStart(ProducerType.MULTI, 1024*1024, new YieldingWaitStrategy(), consumers);
        new NettyClient().sendData();
    }
}

测试修改下sendData()

public void sendData() {
    for (int i = 0; i < 6000000; i++) {
        TranslatorData request = new TranslatorData();
        request.setId("" + i);
        request.setName("请求消息名称" + i);
        request.setMessage("请求消息内容" +i);
        this.channel.writeAndFlush(request);
    }
}