源码分析
Netty是一种异步非阻塞的通信框架,但是Netty底层是通过NIO来实现了, 不是AIO来实现的,Netty的线程模型在之前的笔记中已经分析了,其实Netty的线程模型是通过doug lea的一本书演化而来,Netty通过线程组的方式来启动多个多路复用器,每个多路复用器所做的事情比较明确,比如boss的线程组主要是处理链接事件,而work的线程组主要处理读写事件,Netty这里我也只是熟悉而已,如果要达到精通的程度就需要在项目中大量的实战,但是真正的netty开发来说是非常简单的,只需要开发handler就可以了,因为最复杂的逻辑Netty都给我们封装了,我们只需要写一些我们关心的部分即可,但是我们还是很有必要将它的底层实现原理和线程模型架构分析清楚,这样以后在扩展或者说使用方面就更明了,这样在排错或者进行中间件的开发的过程中将会给我们带来非常大的帮助;研究服务端的启动过程,就需要有一个客户端的实例代码来开始,比如:
public static void main(String[] args) {
System.out.println(Math.max(16,
SystemPropertyUtil.getInt("io.netty.eventLoop.maxPendingTasks", Integer.MAX_VALUE)));
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workGroup)
.channel(NioServerSocketChannel.class)
// 初始化服务器连接队列大小,服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接。
// 多个客户端同时来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理
.option(ChannelOption.SO_BACKLOG, 1024)
//创建通道初始化对象,设置初始化参数
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new NettyServerHandler());
}
});
System.out.println("netty server start success ");
//绑定一个端口并且同步, 生成了一个ChannelFuture异步对象,通过isDone()等方法可以判断异步事件的执行情况
// 启动服务器(并绑定端口),bind是异步操作,sync方法是等待异步操作执行完毕
ChannelFuture channelFuture = bootstrap.bind(8000).sync();
//对通道关闭进行监听,closeFuture是异步操作,监听通道关闭
// 通过sync方法同步等待通道关闭处理完毕,这里会阻塞等待通道关闭完成
channelFuture.channel().closeFuture().sync();
} catch (Exception ce) {
ce.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
EventLoopGroup 源码分析
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workGroup = new NioEventLoopGroup();
这两行代码就是创建两个线程池组,第一个是boss线程组,第二个是工作线程组,netty底层的boos就是用来处理连接事件,然后将连接注册到工作线程上,其实在NioEventLoopoGroup中如果初始化的线程数是1,那么底层就是一个多路复用器,也就是说传入了几个线程,就会产生多少给多路复用器Selector
public NioEventLoopGroup(int nThreads, Executor executor) {
/**
* 这个构造中第三个参数就是Nio的操作,nio中创建一个ServerSocket就是通过ServerSocket.open,
* 其实底层执行的代码就是调用的SelectorProvider.provider().openServerSocketChannel(),所以这里
* 传入的是NIO的相关入口api,后续要使用
*/
this(nThreads, executor, SelectorProvider.provider());
}
/**
* 这里是初始化Nio线程组,在Netty中是线程组,线程组使用的是MultithreadEventLoopGroup,而每个线程组
* 中都是一个NioEventLoop,对应的是SingleThreadEventLoop
* @param nThreads
* @param executor
* @param selectorProvider
* @param selectStrategyFactory
*/
public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,
final SelectStrategyFactory selectStrategyFactory) {
super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());
}
//MultithreadEventLoopGroup是NioEventLoopGroup的父类
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
/**
* 在构造线程组的时候如果没有传入线程的具体数字,也就是没有指定线程数的情况下,那么netty会默认
* 分配一个线程 数量:Math.max(1, SystemPropertyUtil.getInt(
* "io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2))
*其实简单来说就是获取服务器的cpu核数 * 2,比如我的电脑是8核 ,那么线程数就是16
*/
super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
EventExecutorChooserFactory chooserFactory, Object... args) {
if (nThreads <= 0) {
throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
}
//初始化线程池
if (executor == null) {
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
/**
* 这里就是初始化线程组的,比如说 NioEventLoopGroup初始化的是16,那么这里就会初始化一个线程组
* EventExecutor数组为16,这里面的每一个都是一个线程对象,这个线程对象是NioEventLoop,也是一个
* SingleThreadEventLoop,所以NioEventLoopGroup中包含的是MultitheadEventLoopGroup,而每个
* MultitheadEventLoopGroup中的线程是NioEventLoop(SingleThreadEventLoop),在里面维护了一个
* 线程池,所以NioEventLoop中有一个多路复用器Selector,简单来说就是初始化的NioEventLoopGroup有多个个线程
* 就有多少个多路多路复用器Selector和TaskQueue,而Selector和TaskQueue是存在于NioEventLoop中的
*
*/
children = new EventExecutor[nThreads];
for (int i = 0; i < nThreads; i ++) {
boolean success = false;
try {
//创建一个NioEventLoop,放入线程组,创建NioEventLoop的时候会创建Selector和taskQueue,并且维护了一个线程池
//线程池是共用的,就是NioEventLoopGroup初始化的线程池Executor是共用的
children[i] = newChild(executor, args);
success = true;
} catch (Exception e) {
// TODO: Think about if this is a good exception type
throw new IllegalStateException("failed to create a child event loop", e);
} finally {
....
io.netty.channel.nio.NioEventLoopGroup#newChild
@Override
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
EventLoopTaskQueueFactory queueFactory = args.length == 4 ? (EventLoopTaskQueueFactory) args[3] : null;
//创建一个NioEventLoop,创建Selector和taskQueue
return new NioEventLoop(this, executor, (SelectorProvider) args[0],
((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2], queueFactory);
}
NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler,
EventLoopTaskQueueFactory queueFactory) {
//这里创建了一个TaskQueue,然后调用父类的构造
super(parent, executor, false, newTaskQueue(queueFactory), newTaskQueue(queueFactory),
rejectedExecutionHandler);
this.provider = ObjectUtil.checkNotNull(selectorProvider, "selectorProvider");
this.selectStrategy = ObjectUtil.checkNotNull(strategy, "selectStrategy");
//调用NIO的api创建一个多路复用器Selector,是通过provider创建的
final SelectorTuple selectorTuple = openSelector();
//将创建的多路复用器Selector赋值给全局的selector
this.selector = selectorTuple.selector;
//unwrappedSelector和selector是相同的
this.unwrappedSelector = selectorTuple.unwrappedSelector;
}
//SingleThreadEventExecutor是NioEventLoop父类
protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
boolean addTaskWakesUp, Queue<Runnable> taskQueue,
RejectedExecutionHandler rejectedHandler) {
super(parent);
this.addTaskWakesUp = addTaskWakesUp;
//设置最大的task任务数量 Integer.MAX_VALUE
this.maxPendingTasks = DEFAULT_MAX_PENDING_EXECUTOR_TASKS;
this.executor = ThreadExecutorMap.apply(executor, this);
this.taskQueue = ObjectUtil.checkNotNull(taskQueue, "taskQueue");
this.rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
}
上面的源码分析:
1.通过NioEventLoopGroup创建一个线程组,这个线程组是一个MultithreadEventExecutorGroup,然后在MultithreadEventExecutorGroup中根据传入的线程数会创建很多个NioEventLoop,每个NioEventLoop中都会维护一个线程池Executor、一个Selector多路复用器、一个taskQueue,其中selector、taskQueue是NioEventLoop是每个NioEventLoop私有的,就是比如创建了16个NioEventLoop对象,那么每个事件循环线程组对象NioEventLoop中的selector、taskQueue都是私有的,而Executor是共有的,就是所有的对象都会共用Executor这个线程池。
2.然后创建的所有的事件线程组对象组成一个EventExecutor数组 ,EventExecutor的子类是NioEventLoop;
3.NioEventLoopGroup是一个线程组,这个线程组是通过MultithreadEventExecutorGroup创建的,所以NioEventLoopGroup对应的是MultithreadEventExecutorGroup,而NioEventLoop是通过MultithreadEventExecutorGroup创建的,创建的是一个SingleThreadEventLoop线程组,就是单线程组;
4.每个SingleThreadEventLoop中会创建一个多路复用器selector和一个taskQueue,taskQueue主要存放一个任务,然后通过executor去执行这个任务,其实对于boss来说,这个task其实就是注册事件。
ServerBootstrap分析
ServerBootstrap就是服务端启动的一个启动类,包含了netty启动的所有过程,它采用的是链式编程,比如绑定线程组、设置tcp的一些参数和处理器都是通过启动类来绑定的,前面声明了两个线程组,但是声明的线程组如果没有绑定到启动类,也就是主运行的类上是没有任何效果的,所以这里看下这些代码:
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workGroup)
.channel(NioServerSocketChannel.class)
// 初始化服务器连接队列大小,服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接。
// 多个客户端同时来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理
.option(ChannelOption.SO_BACKLOG, 1024)
//创建通道初始化对象,设置初始化参数
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new NettyServerHandler());
}
});
绑定线程组
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
super.group(parentGroup);
if (this.childGroup != null) {
throw new IllegalStateException("childGroup set already");
}
//将工作线程组的对象赋值给childGroup,没有其他过程
this.childGroup = ObjectUtil.checkNotNull(childGroup, "childGroup");
return this;
}
public B group(EventLoopGroup group) {
ObjectUtil.checkNotNull(group, "group");
if (this.group != null) {
throw new IllegalStateException("group set already");
}
//将boss的线程组绑定到group中,就是一个赋值的过程,没有其他的操作
this.group = group;
return self();
}
上面的过程很简单,就是简单的将boss线程组和work线程组赋值给ServerBoostrap中的group属性和childGroup 属性。
channel(NioServerSocketChannel.class)
public B channel(Class<? extends C> channelClass) {
/**
* 这里将传入的比如NioServerSocketChannel通过反射创建一个ReflectiveChannelFactory对象,然后
* 调用channelFactory将创建的ReflectiveChannelFactory对象赋值给ServerBootstrap 中channelFactory属性
* 所以这里记录下,在ServerBootstrap中有个属性为channelFactory
* 这里的new ReflectiveChannelFactory其实就是得到了传入进来的channelClass一个对象,通过反射调用
* 构造NioServerSocketChannel的构造空构造方法
*/
return channelFactory(new ReflectiveChannelFactory<C>(
ObjectUtil.checkNotNull(channelClass, "channelClass")
));
}
所以下来就要重点分析下NioServerSocketChannel的构造方法(在调用newInstance的时候调用)
io.netty.channel.socket.nio.NioServerSocketChannel#NioServerSocketChannel()
/**
* Create a new instance
* 这里是一个空构造方法,在这个构造方法中做的事情有:
* 1.创建一个ServerSocketChannel(NIO);
* 2.将创建的ServerSocketChannel设置一些参数,比如设置这个ServerSocketChannel的事件为OP_ACCEPT,就是接受连接事件
* 3.设置ServerSocketChannel为非阻塞的模式。
*/
public NioServerSocketChannel() {
//newSocket就是创建一个ServerSocketChannel,调用的就是NIO的创建逻辑
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
private static ServerSocketChannel newSocket(SelectorProvider provider) {
try {
/**
* Use the {@link SelectorProvider} to open {@link SocketChannel} and so remove condition in
* {@link SelectorProvider#provider()} which is called by each ServerSocketChannel.open() otherwise.
*
* See <a href="https://github.com/netty/netty/issues/2308">#2308</a>.
* 调用NIO创建一个ServerSocketChannel
*/
return provider.openServerSocketChannel();
} catch (IOException e) {
throw new ChannelException(
"Failed to open a server socket.", e);
}
}
public NioServerSocketChannel(ServerSocketChannel channel) {
//调用父类将这个ServerSocketChannel设置为非阻塞模式和将创建的ServerSocketChannel赋值到父类的属性ch中
super(null, channel, SelectionKey.OP_ACCEPT);
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
//AbstractNioChannel是NioServerSocketChannel的父类
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
//调用父类初始化的管道ChannelPipeline
super(parent);
//赋值channel
this.ch = ch;
//设置这个ServerSocketChannel的事件类型为readInterestOp(OP_ACCEPT)
this.readInterestOp = readInterestOp;
try {
//设置这个ServerSocketChannel为非阻塞的模式
ch.configureBlocking(false);
} catch (IOException e) {
try {
ch.close();
} catch (IOException e2) {
logger.warn(
"Failed to close a partially initialized socket.", e2);
}
throw new ChannelException("Failed to enter non-blocking mode.", e);
}
}
//AbstractChannel是AbstractNioChannel的父类
protected AbstractChannel(Channel parent) {
this.parent = parent;
//创建一个ChannelId
id = newId();
//创建AbstractUnsafe对象,后续要使用
unsafe = newUnsafe();
//初始化管道pipeline对象,这个对象非常重要,就是我们的管道的处理器就是放在里面的,所有的处理器都会加入到这个管道中
//这个管道就是一个双向链表,有head和tail,默认的管道实现类是DefaultChannelPipeline
pipeline = newChannelPipeline();
}
io.netty.channel.DefaultChannelPipeline#DefaultChannelPipeline
//channel就是刚刚NioServerSocketChannel,也是一个ServerScoketChannel
protected DefaultChannelPipeline(Channel channel) {
this.channel = ObjectUtil.checkNotNull(channel, "channel");
succeededFuture = new SucceededChannelFuture(channel, null);
voidPromise = new VoidChannelPromise(channel, true);
//这个管道pipeline中的尾部
tail = new TailContext(this);
//这个管道pipeline中的头部
head = new HeadContext(this);
//建立双向链表关系
head.next = tail;
tail.prev = head;
}
上面的过程就是在绑定一个NioServerSocketChannel,其实简单来说就是
1.将NioServerSocketChannel通过反射调用构造方法封装成一个ReflectiveChannelFactory对象,然后设置这个对象到ServerBootstrap中的属性channelFactory中,ReflectiveChannelFactory包含了NioServerSocketChannel中的构造方法 和通过反射得到实例对象的方法,其实就是对NioServerSocketChannel的一个包装类;
2.第一步是通过反射得到NioServerSocketChannel对象,所以NioServerSocketChannel中构造所做的事情就是调用Nio创建一个ServerSocketChannel,然后设置这个ServerSocketChannel的事件类型为OP_ACCETP,并且设置为阻塞;
3.在这个构造方法中还做一件很重要的事情就是初始化管道DefaultChannelPipeline,这个管道对象中还创建了一个双向链表的管道对象head和tail,分别对应
AbstractChannelHandlerContext head;final AbstractChannelHandlerContext tail;
其他参数设置
/**
* Allow to specify a {@link ChannelOption} which is used for the {@link Channel} instances once they got
* created. Use a value of {@code null} to remove a previous set {@link ChannelOption}.
* 这里是赋值一些tcp参数
*/
public <T> B option(ChannelOption<T> option, T value) {
ObjectUtil.checkNotNull(option, "option");
synchronized (options) {
if (value == null) {
options.remove(option);
} else {
options.put(option, value);
}
}
return self();
}
/**
* Set the {@link ChannelHandler} which is used to serve the request for the {@link Channel}'s.
*/
public ServerBootstrap childHandler(ChannelHandler childHandler) {
//将创建的childHandler对象赋值给childHandler,childHandler中的内部类方法是在后面调用的,这里只是简单的赋值
this.childHandler = ObjectUtil.checkNotNull(childHandler, "childHandler");
return this;
}
上面的代码的意思就是简单的一个赋值操作,类似于set方法,将自定义设置的 tcp参数设置到ServerBootStrap中,还有就是将ChannelHandler 赋值给ServerBootstrap ,至于childHandler中的内部内方法是后面调用的。
启动流程
上面的一些操作都是Netty启动前需要做的一些准备工作,真正的启动流程就在最后一行代码 bootstrap.bind(8000),这行代码承载了Netty启动的最复杂的逻辑,但是如果你只是向大概懂下整体流程的话,那么也是不复杂的,但是如果你要把里面的每一行代码都 搞清楚,我觉得就没太必要,当然你如果你做的中间件的一些开发,并且大量使用到了Netty的话,还是有必要把里面的大部分代码都知道,但是我这里也就分析的大概,很细的地方就没有去分析。
Bind
public ChannelFuture bind(int inetPort) {
//绑定启动,将端口封装成了一个InetSocketAddress对象
return bind(new InetSocketAddress(inetPort));
}
首先头脑里面要有一个思路就是我们在看NIO的原生代码的时候都知道,NIO中肯定是创建一个ServerSocketChannel,然后绑定一个端口,最后调用epoll的select进行阻塞,当有事件过来的时候select就会解除阻塞,最后通过selectKeys来接受事件,所以Netty也是这么做的,只是它的实现有点复杂,但是你要有这个思路,有了这个思路,带着猜一猜的想法去分析看这个源码。
/**
* 这个方法就是Netty的启动过程的最核心的方法,前面的都只是为 下面这个方法做准备的
* 这个方法里面有两个最核心的方法,
* initAndRegister:初始化和注册,注册的是将创建ServerSocketChannel注册到多路复用器selector上
* doBind0:端口绑定,启动监听
* 总结来说就是initAndRegister配置Netty和注册多路复用器
* doBind0:绑定端口,调用epoll阻塞,监听事件的发生
* @param localAddress
* @return
*/
private ChannelFuture doBind(final SocketAddress localAddress) {
//初始化和注册(太多 )
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
/**
* 下面的代码逻辑就是绑定端口,开启select阻塞
* 首先条件isDone第一次启动是不会进入这里,走的else的逻辑
* 在else逻辑中添加了一个监听器,最后由监听器出发了doBind0方法
* 所以不管怎么说,核心代码逻辑都是doBind0
*/
if (regFuture.isDone()) {
// At this point we know that the registration was complete and successful.
ChannelPromise promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise);
return promise;
} else {
// Registration future is almost always fulfilled already, but just in case it's not.
final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
//添加一个监听器,最后由监听器调用了doBind0方法
regFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
Throwable cause = future.cause();
if (cause != null) {
// Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an
// IllegalStateException once we try to access the EventLoop of the Channel.
promise.setFailure(cause);
} else {
// Registration was successful, so set the correct executor to use.
// See https://github.com/netty/netty/issues/2586
promise.registered();
/**
* 在DefaultChannelPromise中的notifyListeners进行调用的
*/
doBind0(regFuture, channel, localAddress, promise);
}
}
});
return promise;
}
}
这个方法承载了Netty启动了所有过程,在上面的代码中有两个最核心的方法 initAndRegister();和 doBind0(regFuture, channel, localAddress, promise);,这两个方法上面也说了,所以这里要对这两个方法做重点分析。
initAndRegister
这个方法从名字也知道了,就是初始化和注册的意思,得到Netty的服务端通道类NioServerSocketChannel(继承了ServerSocketChannel 【JDK】),然后初始化这个Channel,最后将这个channel注册到多路复用器Selector上,Selector是在上面通过NioEventLoopGroup的时候已经创建了多路复用器了,所以这个initAndRegister就是要从NioEventLoopGroup中选择一个Selector进行注册,最后初始化管道的处理器;再调用注册,将channel注册到多路复用器上。
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
/**
* 这里的channelFactory就是在.channel(NioServerSocketChannel.class)注册进去的,所以
* channelFactory就是ReflectiveChannelFactory,而ReflectiveChannelFactory封装的是
* NioServerSocketChannel的信息,但是需要通过反射获取,所以下面的
* channelFactory.newChannel();其实就是调用ReflectiveChannelFactory的newChannel()
* 也就是调用NioServerSocketChannel默认空构造方法进行实例化对象得到channel
*/
channel = channelFactory.newChannel();
//初始化这个channel
init(channel);
} catch (Throwable t) {
if (channel != null) {
// channel can be null if newChannel crashed (eg SocketException("too many open files"))
channel.unsafe().closeForcibly();
// as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
}
// as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
}
/**
* 下面这个逻辑就特别复杂了,config()方法得到ServerBootstrapConfig一个对象
* 通过ServerBootstrapConfig对象可以调用group得到boss线程组,然后调用register进行注册
* 虽然复杂,简单来说就是一个祖册,注册什么?当然是注册之前创建的 NioServerSocketChannel
* 注册到多路复用器Selector上了,而且这里注册还要选择一个多路复用器,比如说我们创建boss线程组的时候传入的线程数大于1或者
* 说没有传入,那么可能就会有很多的NioEventLoop,那么这里的register其实就是将创建的NioServerSocketChannel
* 注册到Selector上,而selector在每个NioEventLoop中都有,所以这里需要选择一个来进行注册
*
* config()得到ServerBootstrapConfig
* group()通过ServerBootstrapConfig.bootstrap.group得到boss线程组对象,所以register就是调用
* NioEventLoopGroup,因为这里是group,所以调用的是MultithreadEventLoopGroup
*
*/
ChannelFuture regFuture = config().group().register(channel);
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 because 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;
}
io.netty.bootstrap.ServerBootstrap#init
// 这个channel就是NioServerSocketChannel
void init(Channel channel) {
//将bootstrap.option时设置的参数设置到这个channel中
setChannelOptions(channel, newOptionsArray(), logger);
setAttributes(channel, attrs0().entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY));
//获取这个channel中的管道,这个管道是DefaultChannelPipeline,在创建NioServerSocketChannel的时候
//初始化的,这个管道中有两个属性head和tail构成了一个双向链表
ChannelPipeline p = channel.pipeline();
//childGroup是工作线程组对象NioEventLoopGroup,赋值给当前的局部变量currentChildGroup
final EventLoopGroup currentChildGroup = childGroup;
//childHandler是调用bootstrap.childHandler(new ChannelInitializer())得到的ChannelInitializer对象
final ChannelHandler currentChildHandler = childHandler;
final Entry<ChannelOption<?>, Object>[] currentChildOptions;
synchronized (childOptions) {
currentChildOptions = childOptions.entrySet().toArray(EMPTY_OPTION_ARRAY);
}
final Entry<AttributeKey<?>, Object>[] currentChildAttrs = childAttrs.entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY);
/**
* 这里向管道中添加了一个ServerBootstrapAcceptor,这个处理器有什么用,后面在说
* 向管道添加了一个ServerBootstrapAcceptor过后,那么管道是这样的
* head:HeadContext<--->ServerBootstrapAcceptor<--->tail:TailContext
*
*/
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) {
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
//通过NioEventLoop中绑定的线程池向管道中添加一个ServerBootstrapAcceptor
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}
initAndRegister要分两步,第一个步是创建NioServerSocketChannel的过程所以这里先把这个过程先画下来
register(注册多路复用器)
/**
* 下面这个逻辑就特别复杂了,config()方法得到ServerBootstrapConfig一个对象
* 通过ServerBootstrapConfig对象可以调用group得到boss线程组,然后调用register进行注册
* 虽然复杂,简单来说就是一个祖册,注册什么?当然是注册之前创建的 NioServerSocketChannel
* 注册到多路复用器Selector上了,而且这里注册还要选择一个多路复用器,比如说我们创建boss线程组的时候传入的线程数大于1或者
* 说没有传入,那么可能就会有很多的NioEventLoop,那么这里的register其实就是将创建的NioServerSocketChannel
* 注册到Selector上,而selector在每个NioEventLoop中都有,所以这里需要选择一个来进行注册
*
* config()得到ServerBootstrapConfig
* group()通过ServerBootstrapConfig.bootstrap.group得到boss线程组对象,所以register就是调用
* NioEventLoopGroup,因为这里是group,所以调用的是MultithreadEventLoopGroup
*
*/
ChannelFuture regFuture = config().group().register(channel);
io.netty.channel.MultithreadEventLoopGroup#register(io.netty.channel.Channel)
@Override
public ChannelFuture register(Channel channel) {
/**
* 这里很好理解,就是这里的register就是注册,将channel注册到多路复用器selector上
* 但是selector是存在于NioEventLoopGroup中每一个NioEventLoop上的,如果 线程数大于1,那么就
* 需要获取一个NioEventLoop上的selector来注册,所以这里的next()就是获取一个NioEventLoop
* next()最终调用的应该是一个随机算法
* 而register调用的就是NioEventLoop了,而NioEventLoop是SingleThreadEventLoop的子类,所以这里
* 调用的是SingleThreadEventLoop中的register方法
*/
return next().register(channel);
}
io.netty.channel.SingleThreadEventLoop#register(io.netty.channel.Channel)
@Override
public ChannelFuture register(Channel channel) {
//DefaultChannelPromise对channel和当前NioEventLoop的一个封装类
return register(new DefaultChannelPromise(channel, this));
}
@Override
public ChannelFuture register(final ChannelPromise promise) {
ObjectUtil.checkNotNull(promise, "promise");
//这里调用的是AbstractChannel中的register方法
promise.channel().unsafe().register(this, promise);
return promise;
}
io.netty.channel.AbstractChannel.AbstractUnsafe#register
@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
ObjectUtil.checkNotNull(eventLoop, "eventLoop");
//这里表示已经注册过了,不需要在注册
if (isRegistered()) {
promise.setFailure(new IllegalStateException("registered to an event loop already"));
return;
}
if (!isCompatible(eventLoop)) {
promise.setFailure(
new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
return;
}
//eventLoop 是从boss 线程组选择出来一个NioEventLoop
AbstractChannel.this.eventLoop = eventLoop;
//下面这个if表示当前的线程是否和NioEventLoop是否在一个线程中的
//反正就是判断需要同步处理还是异步处理,Netty是一个异步非阻塞的通信框架,所以很多地方都是使用这种线程池的方案去处理的
//所以毫无疑问,register0是核心方法
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
/**
* 这里不要被这个execute给迷糊了,看到这个以为是一个线程的启动方法,其实不是,其实就是简单的
* 调用了对象eventLoop中的execute方法,这个方法传入了一个线程对象,但是肯定不是在这里执行的这个线程的run方法
* 的,所以这里记住这个execute方法中传入了一个内部类线程,肯定要先看这个execute方法,而这里的
* eventLoop是NioEventLoop,也是SingleThreadEventExecutor
*/
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);
}
}
}
io.netty.util.concurrent.SingleThreadEventExecutor#execute(java.lang.Runnable)
/**
* 多路复用器注册的核心方法
* @param task
*/
@Override
public void execute(Runnable task) {
ObjectUtil.checkNotNull(task, "task");
execute(task, !(task instanceof LazyRunnable) && wakesUpForTask(task));
}
/**
* 多路复用器注册的核心方法,task就是最外层的那个register0所在的线程
* @param task
* @param immediate
*/
private void execute(Runnable task, boolean immediate) {
//这里就是和外层的那个判断一样,就是看当前线程是否是NioEventLoop的那个线程,代码到这里还在主线程中,所以这里返回的false
//因为task线程还没开始执行
boolean inEventLoop = inEventLoop();
//将注册的那个register0所在线程加入到task任务中,也就是加入到taskQueue.offer(task)队列中,等待调度
addTask(task);
if (!inEventLoop) {
//开始线程
startThread();
if (isShutdown()) {
boolean reject = false;
try {
if (removeTask(task)) {
reject = true;
}
} catch (UnsupportedOperationException e) {
// The task queue does not support removal so the best thing we can do is to just move on and
// hope we will be able to pick-up the task before its completely terminated.
// In worst case we will log on termination.
}
if (reject) {
reject();
}
}
}
if (!addTaskWakesUp && immediate) {
wakeup(inEventLoop);
}
}
private void startThread() {
if (state == ST_NOT_STARTED) {
if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {
boolean success = false;
try {
doStartThread();
success = true;
} finally {
if (!success) {
STATE_UPDATER.compareAndSet(this, ST_STARTED, ST_NOT_STARTED);
}
}
}
}
}
io.netty.channel.nio.NioEventLoop#run
这个run方法就是NioEventLoop的,channel注册多路复用器、select阻塞、事件的处理等都在这个run里面执行的,为什么说netty是异步非阻塞,就是因为它都是通过线程池来完成的;简单来说就是这个run方法将之前添加的taskQueue中的线程任务都拿出来要执行,但是需要将run的逻辑执行完成了 才会执行,所以当你看到eventLoop.execute(Runnable task)的时候你就要知道task的执行是execute去执行的,但是需要execute中的逻辑执行完成了才会去执行这个task,而Netty的这里的很多任务都是这样做的,就是当有eventLoop.execute(Runnable task)的时候,Netty首先将这个task添加到taskQueue中,然后执行了execute中的逻辑过后,然后将taskQueue中的task拿出来,然后调用task的run执行任务的核心逻辑,netty中大量的处理都是在taskQueue中去完成的,所以 对于eventLoop.execute(task)这个核心逻辑要明白才行。
/**
* 这里的run方法才是真正的将NIOServerSocketChannel注册到多路复用器上
*/
@Override
protected void run() {
int selectCnt = 0;
for (;;) {
try {
int strategy;
try {
/**
*这里就是判断现在是那种事件,如果Netty在启动的时候,这里的case都不满足,但是如果启动完成了
*那么再来调用就会调用SELECT,所以根据不同的情况来的
*/
strategy = selectStrategy.calculateStrategy(selectNowSupplier, hasTasks());
switch (strategy) {
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方法,就是在nio中的selector.select()方法
/**
* curDeadlineNanos这个是计算调用select的时候阻塞的时间的,因为netty这里 还有很多事情没有做
* 注册多路复用器都还没有做,所以这里要计算出一个时间,如果这个时间过了,还没有连接过来,那么也会唤醒,
* 不会一直阻塞
*/
long curDeadlineNanos = nextScheduledTaskDeadlineNanos();
if (curDeadlineNanos == -1L) {
curDeadlineNanos = NONE; // nothing on the calendar
}
nextWakeupNanos.set(curDeadlineNanos);
try {
/**
* 如果是第一次启动的注册多路复用器是不会进入这个 if的,因为
* hasTasks就是判断当前的多路复用器注册队列中是是否有任务,如果有任务,
* 那么返回true,没有就返回 false的,netty的意思就是服务端刚启动要注册的是NioServerSocketChannel
* 所以不阻塞,否则阻塞了没意义,因为多路复用器都还没有注册的有channel,根本没有办法监听
*/
if (!hasTasks()) {
strategy = select(curDeadlineNanos);
}
} finally {
// This update is just to help block unnecessary selector wakeups
// so use of lazySet is ok (no race condition)
nextWakeupNanos.lazySet(AWAKE);
}
// 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();
selectCnt = 0;
handleLoopException(e);
continue;
}
/**
* strategy在Netty启动的时候是0
*/
selectCnt++;
cancelledKeys = 0;
needsToSelectAgain = false;
final int ioRatio = this.ioRatio;
boolean ranTasks;
if (ioRatio == 100) {
try {
if (strategy > 0) {
//调用select有事件发生的时候会来调用,这里面就是NIO的那个获取selectKeys然后处理的逻辑,netty封装了
processSelectedKeys();
}
} finally {
// Ensure we always run tasks.
ranTasks = runAllTasks();
}
} else if (strategy > 0) {
final long ioStartTime = System.nanoTime();
try {
//调用select有事件发生的时候会来调用
processSelectedKeys();
} finally {
// Ensure we always run tasks.
final long ioTime = System.nanoTime() - ioStartTime;
ranTasks = runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
}
} else {
//Netty启动的时候调用,这里才是真正的多路复用器的注册,就是调用最开始的那个execute(Runable task)中的那个task
//就是从taskQueue中取出这个线程任务然后执行run方法
ranTasks = runAllTasks(0); // This will run the minimum number of tasks
}
if (ranTasks || strategy > 0) {
if (selectCnt > MIN_PREMATURE_SELECTOR_RETURNS && logger.isDebugEnabled()) {
logger.debug("Selector.select() returned prematurely {} times in a row for Selector {}.",
selectCnt - 1, selector);
}
selectCnt = 0;
} else if (unexpectedSelectorWakeup(selectCnt)) { // Unexpected wakeup (unusual case)
selectCnt = 0;
}
} catch (CancelledKeyException e) {
// Harmless exception - log anyway
if (logger.isDebugEnabled()) {
logger.debug(CancelledKeyException.class.getSimpleName() + " raised by a Selector {} - JDK bug?",
selector, e);
}
} catch (Error e) {
throw (Error) e;
} catch (Throwable t) {
handleLoopException(t);
} finally {
// Always handle shutdown even if the loop processing threw an exception.
try {
if (isShuttingDown()) {
closeAll();
if (confirmShutdown()) {
return;
}
}
} catch (Error e) {
throw (Error) e;
} catch (Throwable t) {
handleLoopException(t);
}
}
}
}
我这里分析的是netty的启动,所以其他的逻辑先不分析,当netty启动的时候执行到 ranTasks = runAllTasks(0); 的时候,那么还记得吗,在AbstractUnsafe中register方法中有一段代码是这样写的:
而在execute方法中,是将runnable的task放入了taskQueue中的,所以这里的 runAllTasks(0); 就是把task取出来进行执行了,所以看到 runAllTasks(0); 的时候,你如果看多了话就可以直接跳过这个方法,就可以直接去看register0的逻辑了,因为 runAllTasks(0); 中肯定是执行了taskQueue.poll()然后执行task的run
protected boolean runAllTasks(long timeoutNanos) {
fetchFromScheduledTaskQueue();
//从taskQueue中取出第一个tqsk
Runnable task = pollTask();
if (task == null) {
afterRunningAllTasks();
return false;
}
final long deadline = timeoutNanos > 0 ? ScheduledFutureTask.nanoTime() + timeoutNanos : 0;
long runTasks = 0;
long lastExecutionTime;
for (;;) {
//执行task的run方法
safeExecute(task);
runTasks ++;
// Check timeout every 64 tasks because nanoTime() is relatively expensive.
// XXX: Hard-coded value - will make it configurable if it is really a problem.
if ((runTasks & 0x3F) == 0) {
lastExecutionTime = ScheduledFutureTask.nanoTime();
if (lastExecutionTime >= deadline) {
break;
}
}
//循环 取出第二个、第三个。。。。然后循环调用task的run方法
task = pollTask();
if (task == null) {
lastExecutionTime = ScheduledFutureTask.nanoTime();
break;
}
}
afterRunningAllTasks();
this.lastExecutionTime = lastExecutionTime;
return true;
}
protected static void safeExecute(Runnable task) {
try {
//调用task的run方法
task.run();
} catch (Throwable t) {
logger.warn("A task raised an exception. Task: {}", task, t);
}
}
io.netty.channel.AbstractChannel.AbstractUnsafe#register0
private void register0(ChannelPromise promise) {
try {
// check if the channel is still open as it could be closed in the mean time when the register
// call was outside of the eventLoop
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
boolean firstRegistration = neverRegistered;
//这里就是将NioServerSocketChannel注册到多路复用上Selector
doRegister();
neverRegistered = false;
registered = true;
// Ensure we call handlerAdded(...) before we actually notify the promise. This is needed as the
// user may already fire events through the pipeline in the ChannelFutureListener.
/**
* 这个方法的调用就是处理管道的handler的,就是处理器,
* 它的过程会将之前创建的ChannelInitializer那个管道中的处理器添加到管道中,然后删除
* 简单来说就是先将ChannelInitializer中的那个initChannel方法调用完成过后,然后删除这个ChannelInitializer那
* 内部类
*/
pipeline.invokeHandlerAddedIfNeeded();
safeSetSuccess(promise);
//这里的调用的注册,那么注册也要调用管道中的所有的有实现了注册的处理器,调用是从链表的head开始的
//当看到有调用fireChannelRegistered方法的, 那么证明是调用了下一个处理器的ChannelRegistered方法
pipeline.fireChannelRegistered();
// Only fire a channelActive if the channel has never been registered. This prevents firing
// multiple channel actives if the channel is deregistered and re-registered.
if (isActive()) {
//表示netty现在是激活的状态来调用的
if (firstRegistration) {
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
// This channel was registered before and autoRead() is set. This means we need to begin read
// again so that we process inbound data.
//
// See https://github.com/netty/netty/issues/4805
beginRead();
}
}
} catch (Throwable t) {
// Close the channel directly to avoid FD leak.
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
}
@Override
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
//将NioServerSocketChannel 注册到selector多路复用器 上
selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 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;
}
}
}
}
io.netty.channel.ChannelPipeline
@Override
public final ChannelPipeline fireChannelRegistered() {
//从管道的head开始调用
AbstractChannelHandlerContext.invokeChannelRegistered(head);
return this;
}
static void invokeChannelRegistered(final AbstractChannelHandlerContext next) {
//对channelRegistered进行调用
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeChannelRegistered();
} else {
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelRegistered();
}
});
}
}
private void invokeChannelRegistered() {
if (invokeHandler()) {
try {
//这里调用的是HeadContext的channelRegistered方法
/**
* 这个方法channelRegistered(HeadContext)中调用完成了handler中的注册方法过后,还会调用fireChannelRegistered
* 方法,这个方法就是传递到下一个handler中的channelRegistered,依次传递,直到把管道中的
* 所有处理器的channelRegistered方法都调用完毕
* 调用完成找到下一个管道的处理器是通过fireChannelRegistered中的方法findContextInbound
*/
((ChannelInboundHandler) handler()).channelRegistered(this);
} catch (Throwable t) {
invokeExceptionCaught(t);
}
} else {
fireChannelRegistered();
}
}
doBind0
这个doBind0中所做的事情就是将NioServerSocketChannel中绑定一个端口,然后调用selector.select监听事件
final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
//添加一个监听器,最后由监听器调用了doBind0方法
regFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
Throwable cause = future.cause();
if (cause != null) {
// Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an
// IllegalStateException once we try to access the EventLoop of the Channel.
promise.setFailure(cause);
} else {
// Registration was successful, so set the correct executor to use.
// See https://github.com/netty/netty/issues/2586
promise.registered();
/**
* 在DefaultChannelPromise中的notifyListeners进行调用的
*/
doBind0(regFuture, channel, localAddress, promise);
}
}
});
io.netty.channel.DefaultChannelPromise#addListener
@Override
public ChannelPromise addListener(GenericFutureListener<? extends Future<? super Void>> listener) {
super.addListener(listener);
return this;
}
io.netty.util.concurrent.DefaultPromise#addListener
@Override
public Promise<V> addListener(GenericFutureListener<? extends Future<? super V>> listener) {
checkNotNull(listener, "listener");
synchronized (this) {
addListener0(listener);
}
if (isDone()) {
//调用监听器的方法
notifyListeners();
}
return this;
}
/**
* 这个方法不要看太多,抓住核心重点的方法notifyListenersNow,下面只是在判断是
* 同步执行notifyListenersNow还是异步执行,很显然这里是异步执行的
*/
private void notifyListeners() {
EventExecutor executor = executor();
if (executor.inEventLoop()) {
final InternalThreadLocalMap threadLocals = InternalThreadLocalMap.get();
final int stackDepth = threadLocals.futureListenerStackDepth();
if (stackDepth < MAX_LISTENER_STACK_DEPTH) {
threadLocals.setFutureListenerStackDepth(stackDepth + 1);
try {
notifyListenersNow();
} finally {
threadLocals.setFutureListenerStackDepth(stackDepth);
}
return;
}
}
//这里是异步执行,这里的异步执行就不是简单异步执行
/**
* 而是走 之前的那个EventLoop.execute(Runable task)一样的逻辑
* 简单来说就是将task添加到taskQueue过后,再执行玩execute中的逻辑过后
* 才会调用runTask,才会执行到这里的task,我们这里的task就是notifyListenersNow
* 太绕了,太多调用
*/
safeExecute(executor, new Runnable() {
@Override
public void run() {
notifyListenersNow();
}
});
}
private void notifyListenersNow() {
Object listeners;
synchronized (this) {
// Only proceed if there are listeners to notify and we are not already notifying listeners.
if (notifyingListeners || this.listeners == null) {
return;
}
notifyingListeners = true;
listeners = this.listeners;
this.listeners = null;
}
/**
* 这里的调用会调用到notifyListener0方法,这个notifyListener0方法调用就会调用到
* 外层的添加监听器中的那个内部类的方法operationComplete,从而调用到我们的doBind0方法
*/
for (;;) {
if (listeners instanceof DefaultFutureListeners) {
notifyListeners0((DefaultFutureListeners) listeners);
} else {
notifyListener0(this, (GenericFutureListener<?>) listeners);
}
synchronized (this) {
if (this.listeners == null) {
// Nothing can throw from within this method, so setting notifyingListeners back to false does not
// need to be in a finally block.
notifyingListeners = false;
return;
}
listeners = this.listeners;
this.listeners = null;
}
}
}
io.netty.bootstrap.AbstractBootstrap#doBind0
private static void doBind0(
final ChannelFuture regFuture, final Channel channel,
final SocketAddress localAddress, final ChannelPromise promise) {
// This method is invoked before channelRegistered() is triggered. Give user handlers a chance to set up
// the pipeline in its channelRegistered() implementation.
//这里异步去绑定端口并且开启阻塞select
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
if (regFuture.isSuccess()) {
channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
} else {
promise.setFailure(regFuture.cause());
}
}
});
}
io.netty.channel.AbstractChannelHandlerContext#bind(java.net.SocketAddress, io.netty.channel.ChannelPromise)
@Override
public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
ObjectUtil.checkNotNull(localAddress, "localAddress");
if (isNotValidPromise(promise, false)) {
// cancelled
return promise;
}
/**
* 调用invokeBind进行端口绑定
*/
final AbstractChannelHandlerContext next = findContextOutbound(MASK_BIND);
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeBind(localAddress, promise);
} else {
safeExecute(executor, new Runnable() {
@Override
public void run() {
next.invokeBind(localAddress, promise);
}
}, promise, null, false);
}
return promise;
}
io.netty.channel.DefaultChannelPipeline.HeadContext#bind
@Override
public void bind(
ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) {
//这里调用AbstractUnsafe中的bind进行端口绑定
unsafe.bind(localAddress, promise);
}
io.netty.channel.AbstractChannel.AbstractUnsafe#bind
@Override
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
assertEventLoop();
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
// See: https://github.com/netty/netty/issues/576
if (Boolean.TRUE.equals(config().getOption(ChannelOption.SO_BROADCAST)) &&
localAddress instanceof InetSocketAddress &&
!((InetSocketAddress) localAddress).getAddress().isAnyLocalAddress() &&
!PlatformDependent.isWindows() && !PlatformDependent.maybeSuperUser()) {
// Warn a user about the fact that a non-root user can't receive a
// broadcast packet on *nix if the socket is bound on non-wildcard address.
logger.warn(
"A non-root user can't receive a broadcast packet if the socket " +
"is not bound to a wildcard address; binding to a non-wildcard " +
"address (" + localAddress + ") anyway as requested.");
}
boolean wasActive = isActive();
try {
//调用绑定,在NIO中我们都知道绑定端口是通过ServerSocketChannel来绑定的
//所以这里的绑定的实现类肯定是NioServerScoketChannel
//最终会调用JDK的NIO代码去绑定
doBind(localAddress);
} catch (Throwable t) {
safeSetFailure(promise, t);
closeIfClosed();
return;
}
//按照正常的逻辑来说,上面绑定了端口,那么所有的工作都做完了,下面就开启监听,监听连接接受事件
//启动多路复用器selector的select,在BIO中就是accept来阻塞
/**
* 所以下面的这个if代码逻辑就很好理解了 :
* 1.调用invokeLater添加一个task到taskQueue中,这个task的调用逻辑和之前的启动注册多路复用器的逻辑一样,只是
* 那会儿的任务是注册NioServerSocketChannel到多路复用器上,而这里是将这个多路复用器进行select事件。
* 2.调用完成过后,按照惯例会调用invokeLater中的task事件,也就是下面的run方法的逻辑,下面的run方法的逻辑很好理解,就是调用
* 管道中的通道激活的事件,也就是说服务端启动了,执行了select事件处理,如果有客户端连接过来,那么服务端就会接受到
* 客户端的请求,表示通道激活,所以就会调用fireChannelActive,就类似于我们平时启动服务端了,客户端连接过来了,那么
* 会调用服务端的处理器的channelActive方法,表示通道被激活。
*/
if (!wasActive && isActive()) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireChannelActive();
}
});
}
safeSetSuccess(promise);
}
private void invokeLater(Runnable task) {
try {
// This method is used by outbound operation implementations to trigger an inbound event later.
// They do not trigger an inbound event immediately because an outbound operation might have been
// triggered by another inbound event handler method. If fired immediately, the call stack
// will look like this for example:
//
// handlerA.inboundBufferUpdated() - (1) an inbound handler method closes a connection.
// -> handlerA.ctx.close()
// -> channel.unsafe.close()
// -> handlerA.channelInactive() - (2) another inbound handler method called while in (1) yet
//
// which means the execution of two inbound handler methods of the same handler overlap undesirably.
eventLoop().execute(task);
} catch (RejectedExecutionException e) {
logger.warn("Can't invoke task later as EventLoop rejected it", e);
}
}
上面的代码其实就做了两件事儿,绑定端口到NioServerSocketChannel,然后调用selector.select(eventLoop().execute(task);)最后监听客户端连接事件
客户端的连接和读取事件,源码这里就不贴了,我这里画了一个完整的源码分析的流程图,可根据这个图就能完整的了解netty的启动流程,包括客户端的连接是如何注册到workGoup上的Selector