周末简单看了下netty5的源码,只看懂了个大概,记录下成果,方便下次再看的时候回忆.
上服务端代码:
public void run() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(
new ObjectEncoder(),
new ObjectDecoder(ClassResolvers.cacheDisabled(null)),
new ObjectEchoServerHandler(),
new MyServerHandler());
}
});
// Bind and start to accept incoming connections.
b.bind(port).sync().channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
EventLoopGroup类有4个相关类容易搞混:
EventExecutor EventExecutorGroup, EventLoop, EventLoopGroup,
EventExecutor 继承自EventExecutorGroup , EventExecutorGroup 继承自ScheduledExecutorService(java自带的线程池服务类), EventExecutor 是一个特殊的EventExecutorGroup,提供了检测一个线程是否在eventLoop中被执行之类的方法.
EventLoopGroup 也继承自EventExecutorGroup, 并提供EventLoop的生成方法next(),
EventLoop继承自EventLoopGroup, EventLoop的英文解释是:Will handle all the I/O-Operations for a Channel once it was registered,即处理channel的io操作,
代码中的第一个bossGroup用于接受channel产生的消息(类似NIo中的通过selector得到感兴趣 的事情),只有一个线程在执行该操作,
第二个workerGroup主要用于处理IO操作,多个线程在执行操作.
ServerBootstrap 类主要用于创建NioServerSocketChannel类,并初始化. 主要看它的 bind()方法,如下:
private ChannelFuture doBind(final SocketAddress localAddress) {
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
final ChannelPromise promise;
if (regFuture.isDone()) {
promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise);
} else {
// Registration future is almost always fulfilled already, but just in case it's not.
promise = new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE);
regFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
doBind0(regFuture, channel, localAddress, promise);
}
});
}
return promise;
}
上面代码主要是两个过程,一个注册生产 channel,第二个是将channel绑定到指定端口,
创建并注册方法如下:
final ChannelFuture initAndRegister() {
Channel channel;
try {
channel = createChannel();
} catch (Throwable t) {
return VoidChannel.INSTANCE.newFailedFuture(t);
}
try {
init(channel);
} catch (Throwable t) {
channel.unsafe().closeForcibly();
return channel.newFailedFuture(t);
}
ChannelPromise regFuture = channel.newPromise();
channel.<span style="color:#ff0000;">unsafe().register(regFuture);</span>
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
// If we are here and the promise is not failed, it's one of the following cases:
// 1) If we attempted registration from the event loop, the registration has been completed at this point.
// i.e. It's safe to attempt bind() or connect() now beause the channel has been registered.
// 2) If we attempted registration from the other thread, the registration request has been successfully
// added to the event loop's task queue for later execution.
// i.e. It's safe to attempt bind() or connect() now:
// because bind() or connect() will be executed *after* the scheduled registration task is executed
// because register(), bind(), and connect() are all bound to the same thread.
return regFuture;
}
上面标红的代码,注册操作实际由unsafe这个工具类来完成, 主要也是向ServerSocketChannel 这个类进行注册,和java NIO注册类似.源码如下:
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
selectionKey = javaChannel().register(eventLoop().selector, 0, this);
return;
} catch (CancelledKeyException e) {
if (!selected) {
// Force the Selector to select now as the "canceled" SelectionKey may still be
// cached and not removed because no Select.select(..) operation was called yet.
eventLoop().selectNow();
selected = true;
} else {
// We forced a select operation on the selector before but the SelectionKey is still cached
// for whatever reason. JDK bug ?
throw e;
}
}
}
}
channel 类与 unsafe类区别: channel 类主要给基于netty开发的程序员进行读写操作,但是netty内部跟 NIo的IO操作主要通过unsafe这个类进行.
通过分析channel的类继承关系会发现, channel部分抽象类内部同时也包含相应unsafe类的实现, 下图是NioServerSocketChannel类的继承结构.
AbstractNioMessageChannel 到 AbstractChannel 类内部都有 unsafe内部类作为实际操作IO类.