通过Selector多路复用器实现IO的多路复用, Selector可以监听多个连接的Channel事件, 同事可以不断的查询已注册Channel是否处于就绪状态, 实现一个线程高效管理多个Channel
Selector、SelectionKey、ServerSocketChannel和SocketChannel的关系
这里的Selector, SelectionKey, ServerSocketChannel, SocketChannel都是Nio中的。
NIO中的ServerSocketChannel功能类似java jdk中的ServerSocket,SocketChannel功能类似Socket
标识Selector和网络通道的注册关系
public abstract class SelectionKey {
public abstract Selector selector();//得到与之关联的Selector对象
public abstract SelectableChannel channel();//得到与之关联的通道
public final Object attachment();//得到与之关联的共享数据
public abstract SelectionKey interestOps(int ops);//设置或改变监听事件
public final boolean isAcceptable();//是否可以accept
public final boolean isReadable();//是否可以读
public final boolean isWritable();//是否可以写
}
ServerSocketChannel在服务器端监听薪的客户端Socket连接
public abstract class ServerSocketChannel
extends AbstractSelectableChannel
implements NetworkChannel
{
public static ServerSocketChannel open();//得到一个ServerSocketChannel通道
public final ServerSocketChannel bind(SocketAddress local);//设置服务器端端口号
public final SelectableChannel configureBlocking(boolean block);//设置阻塞或非阻塞模式,取值false标识采用非阻塞模式
public abstract SocketChannel accept();//接受一个连接,返回代表这个连接的通道对象
public final SelectionKey register(Selector sel, int ops);//注册一个选择器并设置监听事件
}
网络IO通道, 具体负责进行读写操作, Nio把缓冲区的数据写入到通道, 或者把通道的数据写入缓冲区
public abstract class SocketChannel
extends AbstractSelectableChannel
implements ByteChannel, ScatteringByteChannel, GatheringByteChannel, NetworkChannel
{
public static SocketChannel open();//得到一个SocketChannel通道
public final SelectableChannel configureBlocking(boolean block);//设置阻塞或非阻塞模式,取值false标识采用非阻塞模式
public abstract boolean connect(SocketAddress remote);//连接服务器
public abstract boolean finishConnect();//如果上面的方法连接失败,接下来就要通过该方法完成连接操作
public abstract int write(ByteBuffer src);//往通道里写数据
public abstract int read(ByteBuffer dst);//从通道里读数据
public abstract SelectionKey register(Selector sel, int ops, Object att);//注册一个选择器并设置监听事件,最后一个参数可以设置共享数据
public final void close();//关闭通道
}
Netty中的选择器是通过NioEventLoop.run()方法中的select方法去调用Nio包下的selector选择器实现的
NioEventLoop.run()
run 方法的主基调肯定是死循环等待 I/O 事件产生, 然后处理事件
@Override
protected void run() {
for (;;) {
try {
try {
switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
case SelectStrategy.CONTINUE:
continue;
case SelectStrategy.BUSY_WAIT:
// fall-through to SELECT since the busy-wait is not supported with NIO
case SelectStrategy.SELECT:
select(wakenUp.getAndSet(false));
if (wakenUp.get()) {
selector.wakeup();
}
// fall through
default:
}
} catch (IOException e) {
// If we receive an IOException here its because the Selector is messed up. Let's rebuild
// the selector and retry. https://github.com/netty/netty/issues/8566
rebuildSelector0();
handleLoopException(e);
continue;
}
cancelledKeys = 0;
needsToSelectAgain = false;
final int ioRatio = this.ioRatio;
if (ioRatio == 100) {
try {
processSelectedKeys();
} finally {
// Ensure we always run tasks.
runAllTasks();
}
} else {
final long ioStartTime = System.nanoTime();
try {
processSelectedKeys();
} finally {
// Ensure we always run tasks.
final long ioTime = System.nanoTime() - ioStartTime;
runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
}
}
} catch (Throwable t) {
handleLoopException(t);
}
// Always handle shutdown even if the loop processing threw an exception.
try {
if (isShuttingDown()) {
closeAll();
if (confirmShutdown()) {
return;
}
}
} catch (Throwable t) {
handleLoopException(t);
}
}
}
这个策略会根据最近将要发生的定时任务的执行时间来控制 select 最长阻塞的时间。
select方法中的是Nio包下的selector
Reactor单线程模式
EventLoopGroup eventGroup = new NioEventLoopGroup(1)
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(eventGroup);
非主从Reactor多线程模式
EventLoopGroup eventGroup = new NioEventLoopGroup()
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(eventGroup);
主从Reactor多线程模式
EventLoopGroup bossGroup = new NioEventLoopGroup()
EventLoopGroup workerGroup = new NioEventLoopGroup()
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup);