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 收到客户端消息时产生可读事件
  • 可写事件
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

i进击的攻城狮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值