系列
Netty源碼分析 - Bootstrap服務(wù)端
Netty源碼分析 - Bootstrap客戶端
netty源碼分析 - ChannelHandler
netty源碼分析 - EventLoop類關(guān)系
netty源碼分析 - register分析
Netty源碼分析 - NioEventLoop事件處理
netty源碼分析 - accept過程分析
Netty源碼分析 - ByteBuf
Netty源碼分析 - 粘包和拆包問題
開篇
- 這篇文章主要用于分析NioSocketChannel注冊到NioEventLoop對象的過程,順帶和Nio的例子進行下對比。
- 通過這篇文章能夠了解到整個注冊過程的脈絡(luò)為NioEventLoopGroup => NioEventLoop => NioSocketChannel的劉哦吃。
Nio的例子
public class NioEchoServer {
private static final int BUF_SIZE = 256;
private static final int TIMEOUT = 3000;
public static void main(String args[]) throws Exception {
// 打開服務(wù)端 Socket,ServerSocketChannel.open()
// 內(nèi)部執(zhí)行的是 SelectorProvider.provider().openServerSocketChannel();
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 打開 Selector
Selector selector = Selector.open();
// 服務(wù)端 Socket 監(jiān)聽8080端口, 并配置為非阻塞模式
serverSocketChannel.socket().bind(new InetSocketAddress(8080));
serverSocketChannel.configureBlocking(false);
// 將 channel 注冊到 selector 中.
// 通常我們都是先注冊一個 OP_ACCEPT 事件, 然后在 OP_ACCEPT 到來時, 再將這個 Channel 的 OP_READ
// 注冊到 Selector 中.
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
// 通過調(diào)用 select 方法, 阻塞地等待 channel I/O 可操作
if (selector.select(TIMEOUT) == 0) {
System.out.print(".");
continue;
}
// 獲取 I/O 操作就緒的 SelectionKey, 通過 SelectionKey 可以知道哪些 Channel 的哪類 I/O 操作已經(jīng)就緒.
Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
// 當獲取一個 SelectionKey 后, 就要將它刪除, 表示我們已經(jīng)對這個 IO 事件進行了處理.
keyIterator.remove();
if (key.isAcceptable()) {
// 當 OP_ACCEPT 事件到來時, 我們就有從 ServerSocketChannel 中獲取一個 SocketChannel,
// 代表客戶端的連接
// 注意, 在 OP_ACCEPT 事件中, 從 key.channel() 返回的 Channel 是 ServerSocketChannel.
// 而在 OP_WRITE 和 OP_READ 中, 從 key.channel() 返回的是 SocketChannel.
SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept();
clientChannel.configureBlocking(false);
//在 OP_ACCEPT 到來時, 再將這個 Channel 的 OP_READ 注冊到 Selector 中.
// 注意, 這里我們?nèi)绻麤]有設(shè)置 OP_READ 的話, 即 interest set 仍然是 OP_CONNECT 的話, 那么 select 方法會一直直接返回.
clientChannel.register(key.selector(), OP_READ, ByteBuffer.allocate(BUF_SIZE));
}
if (key.isReadable()) {
SocketChannel clientChannel = (SocketChannel) key.channel();
ByteBuffer buf = (ByteBuffer) key.attachment();
long bytesRead = clientChannel.read(buf);
if (bytesRead == -1) {
clientChannel.close();
} else if (bytesRead > 0) {
key.interestOps(OP_READ | SelectionKey.OP_WRITE);
System.out.println("Get data length: " + bytesRead);
}
}
if (key.isValid() && key.isWritable()) {
ByteBuffer buf = (ByteBuffer) key.attachment();
buf.flip();
SocketChannel clientChannel = (SocketChannel) key.channel();
clientChannel.write(buf);
if (!buf.hasRemaining()) {
key.interestOps(OP_READ);
}
buf.compact();
}
}
}
}
}
Netty NioServer
NioServerSocketChannel

NioServerSocketChannel
- NioServerSocketChannel的類繼承關(guān)系如上圖,核心關(guān)注NioServerSocketChannel和AbstraceNioChannel。
- NioServerSocketChannel和AbstraceNioChannel類關(guān)注初始化順序。
public class NioServerSocketChannel extends AbstractNioMessageChannel
implements io.netty.channel.socket.ServerSocketChannel {
private static final ChannelMetadata METADATA = new ChannelMetadata(false, 16);
// SelectorProvider.provider()返回的是單例對象
private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
private final ServerSocketChannelConfig config;
private static ServerSocketChannel newSocket(SelectorProvider provider) {
try {
return provider.openServerSocketChannel();
} catch (IOException e) {
}
}
public NioServerSocketChannel() {
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
public NioServerSocketChannel(ServerSocketChannel channel) {
super(null, channel, SelectionKey.OP_ACCEPT);
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
}
public abstract class AbstractNioChannel extends AbstractChannel {
private final SelectableChannel ch;
protected final int readInterestOp;
volatile SelectionKey selectionKey;
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent);
this.ch = ch;
this.readInterestOp = readInterestOp;
try {
// 如果一個Channel要注冊到Selector中, 那么這個Channel必須是非阻塞的
// 即channel.configureBlocking(false);
ch.configureBlocking(false);
} catch (IOException e) {
}
}
protected SelectableChannel javaChannel() {
return ch;
}
}
- NioServerSocketChannel核心關(guān)注SelectableChannel和readInterestOp。
- DEFAULT_SELECTOR_PROVIDER是通過SelectorProvider.provider()返回單例對象。
- SelectableChannel對象由provider.openServerSocketChannel()。
- readInterestOp在NioServerSocketChannel中監(jiān)聽事件為SelectionKey.OP_ACCEPT。
- 核心關(guān)注ServerSocketChannel,繼續(xù)關(guān)注分析。
- 核心關(guān)注SelectorProvider,用于創(chuàng)建ServerSocketChannel對象,繼續(xù)關(guān)注分析。
ServerSocketChannel

ServerSocketChannel
- ServerSocketChannel的類關(guān)系圖如上圖所示,突出父類SelectableChannel,解答AbstractNioChannel的參數(shù)對象SelectableChannel的緣由。
SelectorProvider

SelectorProvider
- SelectorProvider的類圖如上所示,生成ServerSocketChannel對象,分析下原理。
public abstract class SelectorProvider {
private static final Object lock = new Object();
private static SelectorProvider provider = null;
public static SelectorProvider provider() {
synchronized (lock) {
if (provider != null)
return provider;
return AccessController.doPrivileged(
new PrivilegedAction<SelectorProvider>() {
public SelectorProvider run() {
if (loadProviderFromProperty())
return provider;
if (loadProviderAsService())
return provider;
provider = sun.nio.ch.DefaultSelectorProvider.create();
return provider;
}
});
}
}
}
public abstract class SelectorProviderImpl extends SelectorProvider {
public abstract AbstractSelector openSelector() throws IOException;
public ServerSocketChannel openServerSocketChannel() throws IOException {
return new ServerSocketChannelImpl(this);
}
public SocketChannel openSocketChannel() throws IOException {
return new SocketChannelImpl(this);
}
}
- SelectorProvider#provider用于生成單例的SelectorProvider對象。
- SelectorProviderImpl#openServerSocketChannel負責創(chuàng)建ServerSocketChannel對象。
- SelectorProviderImpl#openSocketChannel負責創(chuàng)建SocketChannel對象。
- 在Netty的體系里面channel對象都是由SelectorProvider來完成的,相當于靈魂。
register分析

注冊調(diào)用鏈
- channel注冊調(diào)用鏈如上圖,按照NioEventLoopGroup => NioEventLoop => NioSocketChannel進行注冊。
- 最終的注冊是將NioSocketChannel注冊到NioEventLoop的selector對象當中。
NioEventLoopGroup注冊
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
channel = channelFactory.newChannel();
init(channel);
} catch (Throwable t) {
}
// 進入NioEventLoopGroup的register過程,進入NioEventLoop的過程
ChannelFuture regFuture = config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
}
public abstract class MultithreadEventLoopGroup extends MultithreadEventExecutorGroup implements EventLoopGroup {
public ChannelFuture register(Channel channel) {
return next().register(channel);
}
}
- config().group().register(channel)的方法中從NioEventLoopGroup執(zhí)行register方法。
- next().register(channel)的方法實現(xiàn)從NioEventLoopGroup到NioEventLoop的調(diào)用傳遞。
NioEventLoop注冊
public abstract class SingleThreadEventLoop extends SingleThreadEventExecutor implements EventLoop {
public ChannelFuture register(Channel channel) {
// DefaultChannelPromise(channel, this)的this為NioEventLoop對象
return register(new DefaultChannelPromise(channel, this));
}
public ChannelFuture register(final ChannelPromise promise) {
ObjectUtil.checkNotNull(promise, "promise");
// 由NioEventLoop進入NioSocketChannel的register過程
promise.channel().unsafe().register(this, promise);
return promise;
}
}
- promise.channel().unsafe().register(this, promise)實現(xiàn)由NioEventLoop到NioSocketChannel的調(diào)用傳遞。
NioSocketChannel注冊
public abstract class AbstractNioChannel extends AbstractChannel {
protected abstract class AbstractUnsafe implements Unsafe {
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
AbstractChannel.this.eventLoop = eventLoop;
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
} catch (Throwable t) {
}
}
}
private void register0(ChannelPromise promise) {
try {
// 省略代碼
doRegister();
} catch (Throwable t) {
}
}
}
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
// 綁定NioSocketChannel到NioEventLoop對象當中,并且附加NioSocketChannel對象。
selectionKey = javaChannel().register(eventLoop().selector, 0, this);
return;
} catch (CancelledKeyException e) {
}
}
}
}
- AbstractUnsafe#register執(zhí)行的NioSocketChannel的注冊動作,最終會執(zhí)行AbstractNioChannel#doRegister。
- AbstractNioChannel#doRegister內(nèi)部通過javaChannel().register(eventLoop().selector, 0, this)實現(xiàn)綁定Channel到NioEventLoop對象當中。