Netty之Reactor线程模型

Reactor 线程模型

一、传统线程模型与NIO模型

1. BIO 模型

传统 I/O 模型为同步阻塞式 I/O 模型,对于网络I/O而言,需要赋予每个连接一个线程,同时该线程会阻塞于I/O事件,无法操作执行其他任务

在这里插入图片描述

1.1 处理模型

每当有新连接到来时,创建一个新的线程以对新连接进行处理,可用***线程池***进行优化,但仍然无法应对高并发服务

1.2 服务端代码
public class BIOServer {

    private ServerSocket serverSocket;
    private ExecutorService executorService;

    // 初始化 server 端
    public BIOServer() {
        try {
            System.out.println("服务器开始启动");
            init();
        } catch (IOException e) {
            System.out.println("服务器启动失败");
            e.printStackTrace();
            // 启动失败,退出服务器
            System.exit(0);
        }
    }

    private void init() throws IOException {
        // 创建 serverSocket
        serverSocket = new ServerSocket(9999);
        // 实例化线程池,优化BIO的线程使用效率
        executorService = Executors.newFixedThreadPool(4);
        System.out.println("服务器初始化成功");
    }

    public void start() {
        while (true) {
            try  {
                final Socket socket = serverSocket.accept();
                System.out.println("收到来自客户端的连接");
                // 使用 lambda 表达式
                executorService.execute(() -> {
                    handler(socket);
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    // 对连接的处理
    private void handler(Socket socket) {
        System.out.println("线程:"+Thread.currentThread().getName());
        System.out.println("读取数据 ==> start");
        byte[] bytes = new byte[1024];
        int read;
        try {
            InputStream inputStream = socket.getInputStream();
            while ((read = inputStream.read(bytes)) != -1) {
                System.out.print(new String(bytes, 0, read));
            }
            System.out.println("读取数据 ==> finish");
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
2. NIO 模型

NIO 为同步非阻塞式 IO,属于事件驱动模式,类似于 java的swing 或者时html页面的事件。一个线程管理多个 I/O 通道(简单的单线程reactor模型),每当检测出某些通道有事件发生时,才对其触发的事件进行处理。

在这里插入图片描述

1. 处理模型
  1. 新连接到来时,即serverSocketChannel的连接事件发生时,接收连接,并将客户端管道注册到selector中进行监听
  2. 当客户端通道有读事件发生时,对读事件进行处理
2. 服务端代码
public class NIOServer {

    ServerSocketChannel serverSocketChannel;
    Selector selector;

    // 默认端口
    private static final int DEFAULT_PORT = 12345;

    public NIOServer() {
        this(DEFAULT_PORT);
    }

    public NIOServer(int port) {
        try {
            init(port);
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("初始化异常");
            System.exit(0);
        }
    }


    private void init(int port) throws IOException {
		// 创建服务端管道
        serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(new InetSocketAddress(port));
        // 设置为非阻塞
        serverSocketChannel.configureBlocking(false);
        selector = Selector.open();
        // 将serverSocketChannel注册到selector,监听accept操作
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    }

    public void start() {
        try {
            inStart();
        } catch (Exception e) {
            System.out.println("服务器无法正常启动");
            e.printStackTrace();
        }
    }

    private void inStart() throws IOException, InterruptedException {

        while (true) {
            // 非阻塞式获取发生事件的管道
            if (selector.select(1000) == 0) {
                // System.out.println("server 无事件");
                // 每一秒检查一次
                Thread.sleep(1000);
            } else {
                // 获取有事件发生的SelectionKey
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    // 移除当前key, 防止重复操作
                    iterator.remove();
                    if (key.isAcceptable()) {
                        acceptHandler();
                    }
                    if (key.isReadable()) {
                        readHandler(key);
                    }
                }
            }
        }
    }

    /**
     * 对读事件进行处理
     */
    private void readHandler(SelectionKey key) throws IOException {
        SocketChannel socketChannel = (SocketChannel) key.channel();
        ByteBuffer attachment = (ByteBuffer) key.attachment();
        // 进行读操作
        System.out.println("接收到客户端[ " +
                socketChannel.getRemoteAddress() + " ]的信息:");
        while (true) {
            int l = socketChannel.read(attachment);
            if (l == 0) {
                break;
            }
            if (l == -1) {
                System.out.println("客户端[ " + socketChannel.getRemoteAddress() + " ]断开连接");
                key.cancel();
                break;
            }
            attachment.flip();
            System.out.print(new String(attachment.array(), 0, attachment.limit()));
            attachment.clear();
        }
        System.out.println();
    }

    /**
     * 对读事件进行处理
     */
    private void acceptHandler() throws IOException {

        SocketChannel socketChannel;
        while ((socketChannel = serverSocketChannel.accept()) == null) ;
        // 将socketChannel设置为非阻塞
        socketChannel.configureBlocking(false);
        // 将SocketChannel注册到selector,并绑定buffer
        socketChannel.register(selector,
                SelectionKey.OP_READ, ByteBuffer.allocate(1024));
        System.out.println("成功连接客户端:" + socketChannel.getRemoteAddress());
    }
}
3. 客户端代码
public class NIOClientApplication {

    public static void main(String[] args) throws IOException, InterruptedException {
        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.configureBlocking(false);
        // 连接到服务器,十秒内连接不上则退出客户端
        if (!socketChannel.connect(new InetSocketAddress(12345))) {
            for (int i = 0; i < 10; i++) {
                Thread.sleep(1000);
                if (socketChannel.finishConnect()) {
                    break;
                }
            }
            if (!socketChannel.finishConnect()) {
                System.out.println("连接出错,请稍后重连");
                System.exit(0);
            }
        }
        String msg = "hello, I am ccy";
        System.out.println(msg.getBytes().length);
        ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
        socketChannel.write(buffer);
        Thread.sleep(4000);
        String msg1 = "hello, I am ccy2";
        ByteBuffer buffer1 = ByteBuffer.wrap(msg1.getBytes());
        socketChannel.write(buffer1);
        Thread.sleep(2000);
        socketChannel.close();
    }
}

二、Reactor线程模型

Reactor线程模型基于NIO事件驱动进行实现,每当有事件发生时,将其进行分发。有三种实现,分别是***单线程 Reactor模型 、多线程 Reactor模型 、主从多线程 Reactor模型***

1. 单线程 Reactor 模型

与NIO模型相同

在这里插入图片描述

2. 多线程 Reactor 模型

当客户端连接到服务器时,reactor负责读取数据并将数据交由工作线程处理,待处理完成之后由reactor将数据会送给客户端

在这里插入图片描述

3. 主从 Reactor 模型

主Reactor负责监听serverSocketChannal的连接事件,当收到客户端连接时,将连接注册到从reactor中,由从reactor负责处理客户端的读写事件

在这里插入图片描述


后续更新Reactor模型代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值