• NIO之Selector执行流程


    一、Seletor是什么?

    selector 单从字面意思不好理解,Seletor是一个监听器,它可以监听Channel中发生的事件。Channel可以注册在Seletor中,当这些注册的Channel在事件发生时,Seletor的select 方法就会返回这些事件交给 thread 来处理。

    selector 版
    selector
    thread
    channel
    channel
    channel

    二、Seletor和多线程处理的区别

    光从上面的解释不好理解,需要结合服务器的设计演化来理解它的用途

    多线程版设计

    多线程版
    socket1
    thread
    socket2
    thread
    socket3
    thread

    对网络中的客户端socket请求,如果每个请求都开一个线程去处理,那么系统内存占用高,线程上下文切换成本也高,对系统的压力会非常大。

    线程池版设计

    线程池版
    socket1
    thread
    socket2
    thread
    socket3
    socket4

    使用线程池,优化了线程的创建,但是在阻塞模式下,线程仅能处理一个 socket 连接, 仅适合短连接场景

    selector版本

    selector 的作用就是配合一个线程来管理多个 channel,获取这些 channel 上发生的事件,这些 channel 工作在非阻塞模式下,不会让线程吊死在一个 channel 上。适合连接数特别多,但流量低的场景(low traffic)

    image.png

    三、selector单线程处理多socket案例

    服务端代码:

    @Slf4j
    public class ChannelDemo6 {
        public static void main(String[] args) {
            try (ServerSocketChannel channel = ServerSocketChannel.open()) {
                channel.bind(new InetSocketAddress(8080));
                System.out.println(channel);
                Selector selector = Selector.open();
                channel.configureBlocking(false);
                channel.register(selector, SelectionKey.OP_ACCEPT);
    
                while (true) {
                    int count = selector.select();
                     log.debug("select count: {}", count);
    
    
                    // 获取所有事件
                    Set<SelectionKey> keys = selector.selectedKeys();
    
                    // 遍历所有事件,逐一处理
                    Iterator<SelectionKey> iter = keys.iterator();
                    while (iter.hasNext()) {
                        SelectionKey key = iter.next();
                        // 判断事件类型
                        if (key.isAcceptable()) {
                            ServerSocketChannel c = (ServerSocketChannel) key.channel();
                            // 必须处理
                            SocketChannel sc = c.accept();
                            sc.configureBlocking(false);
                            sc.register(selector, SelectionKey.OP_READ);
                            log.debug("连接已建立: {}", sc);
                        }
                        else if (key.isReadable()) {
                            SocketChannel sc = (SocketChannel) key.channel();
                            ByteBuffer buffer = ByteBuffer.allocate(128);
                            int read = sc.read(buffer);
                            if(read == -1) {
                                key.cancel();
                                sc.close();
                            } else {
                                buffer.flip();
                                debugAll(buffer);
                            }
                        }
                        // 处理完毕,必须将事件移除
                        iter.remove();
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    客户端代码:

    public class Client {
        public static void main(String[] args) {
            try (Socket socket = new Socket("localhost", 8080)) {
                System.out.println(socket);
                socket.getOutputStream().write("world".getBytes());
                System.in.read();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    方法解释:

    • ServerSocketChannel用来创建服务器的Channel,它有点像ServerSocket.
    • channel.configureBlocking(false);设置channel为非阻塞,这样如果accpet的时候,如果没有连接,就会返回null.
    • channel.register(selector, SelectionKey.OP_ACCEPT)将channel注册到selector中,监听accept连接事件
    • selector.select(),会阻塞,等待客户端连接
    • selector.selectedKeys(),事件产生式,会把事件添加到这个集合中

    四、selector事件

    • accept 产生连接时触发
    • connect 客户端建立连接时出发
    • read 收到客户端消息时产生可读事件
    • 可写事件
  • 相关阅读:
    springboot react 代码生成器
    RabbitMQ 学习(一)-- 概念和安装
    (14)点云数据处理学习——RGBD 里程计
    最优闭回路问题
    数据结构之散列表
    MySQL安装教程(Windows版)
    状态管理Vuex
    瑞吉外卖09-菜品模块的CRUD与启售、停售
    Primavera P6 Professional 21.12 登录异常案例分享
    搭建深度学习网络时节约GPU显存的技巧
  • 原文地址:https://blog.csdn.net/qq_45171957/article/details/126079147