7. 彤哥说netty系列之Java NIO核心组件之Selector

——日拱一卒,不期而至!

nio

你好,我是彤哥,本篇是netty系列的第七篇。

简介

上一章我们一起学习了Java NIO的核心组件Buffer,它通常跟Channel一起使用,但是它们在网络IO中又该如何使用呢,今天我们将一起学习另一个NIO核心组件——Selector,没有它可以说就干不起来网络IO。

概念

我们先来看两段Selector的注释,见类java.nio.channels.Selector。

注释I

A multiplexor of {@link SelectableChannel} objects.

它是SelectableChannel对象的多路复用器,从这里我们也可以知道Java NIO实际上是多路复用IO。

SelectableChannel有几个子类,你会非常熟悉:

DatagramChannel,UDP协议连接

SocketChannel,TCP协议连接

ServerSocketChannel,专门处理TCP协议Accept事件

我们有必要复习一下多路复用IO的流程

multiplexing-io

第一阶段通过select去轮询检查有没有连接准备好数据,第二阶段把数据从内核空间拷贝到用户空间。

在Java中,就是通过Selector这个多路复用器来实现第一阶段的。

注释II

A selector may be created by invoking the {@link #open open} method of this class, which will use the system's default {@link java.nio.channels.spi.SelectorProvider selector provider} to create a new selector. A selector may also be created by invoking the {@link java.nio.channels.spi.SelectorProvider#openSelector openSelector} method of a custom selector provider. A selector remains open until it is closed via its {@link #close close} method.

Selector可以通过它自己的open()方法创建,它将通过默认的java.nio.channels.spi.SelectorProvider类创建一个新的Selector。也可以通过实现java.nio.channels.spi.SelectorProvider类的抽象方法openSelector()来自定义实现一个Selector。Selector一旦创建将会一直处于open状态直到调用了close()方法为止。

那么,默认使用的Selector究竟是哪个呢?

通过跟踪源码:

> java.nio.channels.Selector#open() 1> java.nio.channels.spi.SelectorProvider#provider() 1.1> sun.nio.ch.DefaultSelectorProvider#create() // 返回WindowsSelectorProvider 2> sun.nio.ch.WindowsSelectorProvider#openSelector() // 返回WindowsSelectorImpl

可以看到,在Windows平台下,默认实现的Provider是WindowsSelectorProvider,它的openSelector()方法返回的是WindowsSelectorImpl,它就是Windows平台默认的Selector实现。

为什么要提到在Windows平台呢,难道在Linux下面实现不一样?

是滴,因为网络IO是跟操作系统息息相关的,不同的操作系统的实现可能都不一样,Linux下面JDK的实现完全不一样,那么我们为什么没有感知到呢?我的代码在Windows下面写的,拿到Linux下面不是一样运行?那是Java虚拟机(或者说Java运行时环境)帮我们把这个事干了,它屏蔽了跟操作系统相关的细节,这也是Java代码可以“Write Once, Run Anywhere”的精髓所在。

Selector与Channel的关系

上面我们说了selector是多路复用器,它是在网络IO的第一阶段用来轮询检查有没有连接准备好数据的,那么它和Channel是什么关系呢?

nio

Selector通过不断轮询的方式同时监听多个Channel的事件,注意,这里是同时监听,一旦有Channel准备好了,它就会返回这些准备好了的Channel,交给处理线程去处理。

所以,在NIO编程中,通过Selector我们就实现了一个线程同时处理多个连接请求的目标,也可以一定程序降低服务器资源的消耗。

基本用法 创建Selector

通过调用Selector.open()方法是我们常用的方式:

Selector selector = Selector.open();

当然,也可以通过实现java.nio.channels.spi.SelectorProvider.openSelector()抽象方法自定义一个Selector。

将Channel注册到Selector上

为了将Channel跟Selector绑定在一起,需要将Channel注册到Selector上,调用Channel的register()方法即可:

channel.configureBlocking(false); SelectionKey key = channel.register(selector, SelectionKey.OP_READ);

Channel必须是非阻塞模式才能注册到Selector上,所以,无法将一个FileChannel注册到Selector,因为FileChannel没有所谓的阻塞还是非阻塞模式,本文来源于工从号彤哥读源码。

注册的时候第二个参数传入的是监听的事件,一共有四种事件:

Connect

Accept

Read

Write

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpssgz.html