上个学期自己也读过Netty的源码,但那个时候每想到通过博客的方式把它给记录下来,以至于,几个月过去后自己也忘得差不多了,所以打算重新读一遍Netty的源码,并把读的过程给记录下来,下面就开始吧:服务端启动流程:
public static void main(String[] args) throws Exception {
//一组boss线程,用来监听客户端的连接请求
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
//worker线程,服务端用来处理客户端的逻辑
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//新建一个启动器
ServerBootstrap b = new ServerBootstrap();
//添加boss线程和worker线程
b.group(bossGroup, workerGroup)
//设定channel的类型
.channel(NioServerSocketChannel.class)
//设定新连接的属性
.childOption(ChannelOption.TCP_NODELAY, true)
.childAttr(AttributeKey.newInstance("childAttr"), "childAttrValue")
//添加一个处理器
.handler(new ServerHandler())
//添加一组channel的处理器
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new AuthHandler());
//..
}
});
//绑定端口并启动
ChannelFuture f = b.bind(8888).sync();
f.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
上面的主程序就是启动一个简单的Netty服务端的流程,我们把Netty的服务端启动分为一下四步:
1、创建服务端Channel
2、初始化服务端Channel
3、注册selector
4、端口绑定
接下来,从主程序的bind方法开始,我们一步一步的跟踪:
public ChannelFuture bind(SocketAddress localAddress) {
validate();
if (localAddress == null) {
throw new NullPointerException("localAddress");
}
return doBind(localAddress);
}
这个方法最终调用了doBind方法:
private ChannelFuture doBind(final SocketAddress localAddress) {
final ChannelFuture regFuture = initAndRegister();
...
}
我们首先看第一步,创建服务端Channel,就是上面initAndRegister方法,我们进入这个方法深入看一下实现的过程:
final ChannelFuture initAndRegister() {
Channel channel = null;
//创建一个服务端channel
channel = channelFactory.newChannel();
//初始化channel
init(channel);
...
//注册channel
ChannelFuture regFuture = config().group().register(channel);
return regFuture;
}
这个方法一共做了三件事:
1、创建一个服务端channel
2、初始化channel
3、注册channel
先看一下创建服务端channel:
channel = channelFactory.newChannel();
我们发现这行代码是用一个工厂来实例化了一个channel,那这个channelFactory是在哪里初始化的呢?既然我们程序没跟踪到,那可能就是在构造方法中创建的,于是我们回到main方法找到了这一行:
channel(NioServerSocketChannel.class)
可能就和这行代码有关,我们走进去看一下
public B channel(Class<? extends C> channelClass) {
if (channelClass == null) {
throw new NullPointerException("channelClass");
}
return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}
果然,根据我们传进去的使用的channel的类型创建了一个工厂,作为参数传到了另一个方法中:
public B channelFactory(ChannelFactory<? extends C> channelFactory) {
...
this.channelFactory = channelFactory;
return (B) this;
}
在这个方法中就将我们创建的channelFactory存到了启动类的属性中。我们继续回到这行代码,开始创建一个channel:
channel = channelFactory.newChannel();
进入newChannel方法:
public T newChannel() {
try {
//通过反射实例化channel
return clazz.newInstance();
} catch (Throwable t) {
throw new ChannelException("Unable to create Channel from class " + clazz, t);
}
}
这个方法使用我们传入的channel类使用默认的构造方法实例化了channel,难道channel就这么简单的被创建了么?我们进入channel类的默认构造方法中看一下:
public NioServerSocketChannel() {
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
默认的构造方法又调用了其他的构造方法,并且传入了参数,我们看一下newSocket方法执行后返回了什么:
SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
private static ServerSocketChannel newSocket(SelectorProvider provider) {
try {
return provider.openServerSocketChannel();
} catch (IOException e) {
throw new ChannelException(
"Failed to open a server socket.", e);
}
}
这个方法就是通过provider打开了一个channel并返回,那么这个provider是怎么被创建的呢?我们带着好奇心继续进入SelectorProvider.provider()方法:
public static SelectorProvider provider() {
//加锁
synchronized (lock) {
//如果selector provider不为空直接返回
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;
}
});
}
}
这个方法就是返回返回此调用的系统范围默认选择器。
现在selectorProvider创建了一个java底层的channel并且传入了另外一个构造方法:
public NioServerSocketChannel(ServerSocketChannel channel) {
super(null, channel, SelectionKey.OP_ACCEPT);
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
我们继续看它父类的构造方法,并且记住传入的参数:null, channel, SelectionKey.OP_ACCEPT:
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
//继续调用父类的构造方法
super(parent);
//保存已创建的java底层channel
this.ch = ch;
//保存感兴趣的事件
this.readInterestOp = readInterestOp;
try {
//设置channel为非阻塞
ch.configureBlocking(false);
} catch (IOException e) {
try {
ch.close();
} catch (IOException e2) {
if (logger.isWarnEnabled()) {
logger.warn(
"Failed to close a partially initialized socket.", e2);
}
}
throw new ChannelException("Failed to enter non-blocking mode.", e);
}
}
这个方法保存了我们传入的一些参数,并且继续调用父类的构造方法:
protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
//创建unsafe类
unsafe = newUnsafe();
//创建channelPipeline
pipeline = newChannelPipeline();
}
这个构造方法做了三件事:
1、创建了一个id
2、创建了unsafe对象
3、创建了channelPipeline类
我们再继续看一下channelPipeline是如何被创建的:
protected DefaultChannelPipeline(Channel channel) {
//保存我们常见的服务端channel
this.channel = ObjectUtil.checkNotNull(channel, "channel");
succeededFuture = new SucceededChannelFuture(channel, null);
voidPromise = new VoidChannelPromise(channel, true);
//创建一个头节点和尾节点
tail = new TailContext(this);
head = new HeadContext(this);
//双向链表
head.next = tail;
tail.prev = head;
}
pipeline里面保存了一个双向链表,那些处理器在之后的初始化channel的过程中就会被封装成一个节点被放到这个双向链表中,这些在之后讲这个组件的时候会讲到。我们回到最初的那个构造方法:
public NioServerSocketChannel(ServerSocketChannel channel) {
super(null, channel, SelectionKey.OP_ACCEPT);
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
分析完父类的构造方法了,接着看第二行代码,应该是创建了当前channel的配置对象,我们就不深究了,继续回到我们分析的进度上,现在分析完了实例化当前channel的构造方法,我们发现服务端的channel实际上是创建了一个Java底层的channel然后封装成了服务端的channel。
继续看下面的流程:
init(channel);
接下来就要对我们创建的channel进行初始化了:
void init(Channel channel) throws Exception {
//获得channel的配置
final Map<ChannelOption<?>, Object> options = options0();
synchronized (options) {
channel.config().setOptions(options);
}
//获得channel的属性
final Map<AttributeKey<?>, Object> attrs = attrs0();
synchronized (attrs) {
for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
@SuppressWarnings("unchecked")
AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
channel.attr(key).set(e.getValue());
}
}
//获得channel的pipeline
ChannelPipeline p = channel.pipeline();
//获得worker线程
final EventLoopGroup currentChildGroup = childGroup;
//获得我们添加的handler
final ChannelHandler currentChildHandler = childHandler;
//获得我们设置的配置和属性
final Entry<ChannelOption<?>, Object>[] currentChildOptions;
final Entry<AttributeKey<?>, Object>[] currentChildAttrs;
synchronized (childOptions) {
currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size()));
}
synchronized (childAttrs) {
currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));
}
//为pipeline添加我们在主程序中添加的handler,并且在自动添加一个用来处理accept的handler
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(Channel ch) throws Exception {
//获得channel的pipeline
final ChannelPipeline pipeline = ch.pipeline();
//获得我们配置的handler
ChannelHandler handler = config.handler();
//添加到pipeline上
if (handler != null) {
pipeline.addLast(handler);
}
//这个handler是当又新的channel注册的时候为他们添加上我们的配置属性
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}
这个初始化方法为我们创建的服务端channel设置了配置和属性信息,并且添加了我们配置的handler和一个默认的handler。
这样初始化channel就结束了,接下来是注册selector:
ChannelFuture regFuture = config().group().register(channel);
我们一部分一部分的看,先看一下config()这个方法返回了什么:
public final ServerBootstrapConfig config() {
return config;
}
返回的config封装了一个bootStrap,里面的方法都是返回bootStrap各种属性的,比如接下来的:
public final EventLoopGroup group() {
return bootstrap.group();
}
返回的是bossGroup,调用他的register方法
public ChannelFuture register(Channel channel) {
return next().register(channel);
}
next方法是做什么的呢?我们看一下:
public EventExecutor next() {
return chooser.next();
}
他最终调用了chooser的next方法:
public EventExecutor next() {
return executors[idx.getAndIncrement() & executors.length - 1];
}
原来他是按照id轮询来选择一个eventLoop来工作,好,现在又工作的线程了,我们看看注册是怎么实现的:
public ChannelFuture register(Channel channel) {
return register(new DefaultChannelPromise(channel, this));
}
他又将我们要注册的channel和选中的工作线程封装成了promise对象传进去:
public ChannelFuture register(final ChannelPromise promise) {
ObjectUtil.checkNotNull(promise, "promise");
promise.channel().unsafe().register(this, promise);
return promise;
}
最终通过unsafe类的方法注册:
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
...
if (!isCompatible(eventLoop)) {
promise.setFailure(
new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
return;
}
AbstractChannel.this.eventLoop = eventLoop;
//如果当前的eventLoop绑定的线程就是当前线程,那么就直接调用注册方法
if (eventLoop.inEventLoop()) {
register0(promise);
}
//否则放到任务队列里
else {
try {
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
} catch (Throwable t) {
logger.warn(
"Force-closing a channel whose registration task was not accepted by an event loop: {}",
AbstractChannel.this, t);
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
}
}
这个方法最终调用了register0进行注册:
private void register0(ChannelPromise promise) {
...
//注册
doRegister();
...
//触发添加channel事件
pipeline.invokeHandlerAddedIfNeeded();
safeSetSuccess(promise);
//触发channel注册完成事件
pipeline.fireChannelRegistered();
...
}
这个方法做了三件事:
1、最终调用了注册方法
2、触发添加channel事件
3、触发channel注册完成事件
我们分别来看一下注册是怎么实现的:
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
//将我们创建的服务端channel注册到eventLoop的selector,注册的感兴趣的事件事无
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;
}
}
}
}
最后一步就是要绑定端口了
protected void doBind(SocketAddress localAddress) throws Exception {
if (PlatformDependent.javaVersion() >= 7) {
javaChannel().bind(localAddress, config.getBacklog());
} else {
javaChannel().socket().bind(localAddress, config.getBacklog());
}
}
最中调用java底层来绑定,绑定结束后会触发事件并且执行方法:
private void readIfIsAutoRead() {
if (channel.config().isAutoRead()) {
channel.read();
}
}
最终会调用:
public void read(ChannelHandlerContext ctx) {
unsafe.beginRead();
}
我们进去看一下:
protected void doBeginRead() throws Exception {
// Channel.read() or ChannelHandlerContext.read() was called
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
}
readPending = true;
final int interestOps = selectionKey.interestOps();
if ((interestOps & readInterestOp) == 0) {
selectionKey.interestOps(interestOps | readInterestOp);
}
}
为我们的服务端channel设置上对读感兴趣,也就是对accept感兴趣的事件。
这样服务端就启动起来了。