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

客户端代码:

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

方法解释:

  • 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 收到客户端消息时产生可读事件
  • 可写事件

猜你喜欢

转载自blog.csdn.net/qq_45171957/article/details/126079147