1、服务端启动时序图
- 创建ServerBootStrap实例。
ServerBootStrap是Netty服务端的启动辅助类,它提供了一系列的方法用于设置服务器启动相关的参数。
ServerBootStrap只有一个无参的构造函数,是因为他的参数太多了。
- 设置并绑定Reactor线程池。
Netty的Reactor线程池就是EventLoopGroup,是EventLoop的数组。处理所有注册到本线程Selector上的Channel,Selector的轮询操作由绑定的EventLoop线程run方法驱动,在一个循环体内循环执行;除了处理网络IO事件,EventLoop也负责处理用户自定义的Task和定时Task。
从调度层面看,EventLoop也不会再启动其它类型的线程用于异步执行另外的任务,这样就避免了多线程并发操作和锁竞争,提升了IO线程的处理和调度性能;
- 设置并绑定服务端Channel。
Netty对原生的NIO类库进行了封装,对应实现是NioServerSocketChannel。对于用户而言,不需要关心服务器Channel的底层实现细节和工作原理,只需要指定具体使用哪种服务端Channel即可。
Netty的ServerBootStrap方法提供了channel方法用于指定服务端Channel的类型。
Netty通过工厂类,利用反射创建NioServerSocketChannel对象。
- 链路创建时并初始化ChannelPipeline。
本质是一个负责处理网络事件的职责链,负责管理执行ChannelHandler。网络事件以事件流的形式在ChannelPipeline中流转,由ChannelPipeline根据ChannelHandler的执行策略调度ChannelHandler执行。
- 初始化ChannelPipeline之后,添加并设置ChannelHandler。
用户可以完成大多数的功能定制,例如信息编解码、心跳、安全认证、TSL/SSL认证、流量控制和流量整形。
- 绑定并启动监听端口。
- Selector轮询。
由Reactor线程NioEventLoop负责调度执行Selector轮询操作,选择就绪的Channel集合。
- 当轮询到准备就绪的Channel后,由Reactor线程NioEventLoop执行ChannelPipeline的相应方法,最终调度并执行ChannelHandler。
- 执行Netty系统ChannelHandler和用户添加定制的ChannelHandler;
2、Netty服务端启动源码
2.1 创建EventLoopGroup过程:
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory, Object... args) {
this.terminatedChildren = new AtomicInteger();
this.terminationFuture = new DefaultPromise(GlobalEventExecutor.INSTANCE);
if (nThreads <= 0) {
throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
} else {
if (executor == null) {
executor = new ThreadPerTaskExecutor(this.newDefaultThreadFactory());
}
this.children = new EventExecutor[nThreads];
int j;
for(int i = 0; i < nThreads; ++i) {
boolean success = false;
boolean var18 = false;
try {
var18 = true;
this.children[i] = this.newChild((Executor)executor, args);
success = true;
var18 = false;
} catch (Exception var19) {
throw new IllegalStateException("failed to create a child event loop", var19);
} finally {
if (var18) {
if (!success) {
int j;
for(j = 0; j < i; ++j) {
this.children[j].shutdownGracefully();
}
for(j = 0; j < i; ++j) {
EventExecutor e = this.children[j];
try {
while(!e.isTerminated()) {
e.awaitTermination(2147483647L, TimeUnit.SECONDS);
}
} catch (InterruptedException var20) {
Thread.currentThread().interrupt();
break;
}
}
}
}
}
if (!success) {
for(j = 0; j < i; ++j) {
this.children[j].shutdownGracefully();
}
for(j = 0; j < i; ++j) {
EventExecutor e = this.children[j];
try {
while(!e.isTerminated()) {
e.awaitTermination(2147483647L, TimeUnit.SECONDS);
}
} catch (InterruptedException var22) {
Thread.currentThread().interrupt();
break;
}
}
}
}
this.chooser = chooserFactory.newChooser(this.children);
FutureListener<Object> terminationListener = new FutureListener<Object>() {
public void operationComplete(Future<Object> future) throws Exception {
if (MultithreadEventExecutorGroup.this.terminatedChildren.incrementAndGet() == MultithreadEventExecutorGroup.this.children.length) {
MultithreadEventExecutorGroup.this.terminationFuture.setSuccess((Object)null);
}
}
};
EventExecutor[] var24 = this.children;
j = var24.length;
for(int var26 = 0; var26 < j; ++var26) {
EventExecutor e = var24[var26];
e.terminationFuture().addListener(terminationListener);
}
Set<EventExecutor> childrenSet = new LinkedHashSet(this.children.length);
Collections.addAll(childrenSet, this.children);
this.readonlyChildren = Collections.unmodifiableSet(childrenSet);
}
}
最终会跟踪到上述函数:
- 如果executor是null,创建一个默认的ThreadPerTaskExecutor,使用Netty默认的线程工厂;
- 根据传入的线程数创建一个线程池数组;
- 循环填充数组中的元素。
- 创建一个线程选择器,默认是对2取余(位运算);
- 为每一个线程池添加一个关闭监听器;
- 将所有经过上述处理的线程池添加到一个HashSet;
2.2 ServerBootStrap创建和构造过程
是个空构造,但是有默认的变量;
group方法将boss和worker传入
bootstrap.group(bossGroup,workerGroup)
线程组和线程类型设置完成后,需要设置服务端Channel用于监听端口和客户端链路接入;
channel方法channel(NioServerSocketChannel.class)
对于服务端,需要创建NioServerSocketChannel。因此通过指定channel的类型创建channel工厂。
option方法option(ChannelOption.SO_BACKLOG,128)
指定NioServerChannel后,需要设置TCP的一些参数,服务端主要是设置TCP的backlog参数。
backlog指定了内核为此套接口排队的最大连接个数,对于给定的监听套接字,内核维护两个队列:未连接队列和已连接队列,根据TCP三次握手分隔两个队列。
- 服务器处于listen状态时,收到客户端syn(connect)时在未完成队列中创建一个新的条目;
- 服务器syn响应客户端;
- 客户端对服务器syn的ack。
- 如果三次握手完成,上述条目将从未完成队列搬到已完成连接队列尾部。
当进程调用accept时将从已完成队列的头部取出一个条目给进程,当已完成队列为空时进程将睡眠,直到已经完队列中有条目才会唤醒;
backlog被规定为两个队列总和的最大值,大多数实现默认值是5。在高并发web服务器需要设置的更大:客户端syn和客户端ack的延时导致未完成队列长度增大;
handler方法和childHandler方法
childHandler()来自于启动辅助类ServerBootstrap,是NioServerSocketChannel对应的ChannelPipeline的Handler;
handler()来自于其父类AbstractBootstrap;是客户端新接入的连接SocketChannel对应的ChannelPipeline的Handler。
ServerBootstrap中的Handler是NioServerSocketChannel使用的,所有连接该监听端口的客户端都会执行它;父类AbstractBootstrap中的Handler是个工厂类,为每个新接入的客户端都创建一个新的Handler;
3、bind方法
- initAndRegister 初始化 NioServerSocketChannel 通道并注册各个 handler,返回一个 future。
- 执行 doBind0 方法,完成对端口的绑定。
3.1 initAndRegister 方法
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
channel = this.channelFactory.newChannel();
this.init(channel);
} catch (Throwable var3) {
if (channel != null) {
channel.unsafe().closeForcibly();
return (new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE)).setFailure(var3);
}
return (new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE)).setFailure(var3);
}
ChannelFuture regFuture = this.config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
- 通过通道工厂反射创建一个NioServerSocketChannel;
- init初始化这个NioServerSocketChannel;
- config().group().register(channel) 通过 ServerBootstrap 的 bossGroup 注册 NioServerSocketChannel。
- 最后,返回这个异步执行的占位符。
channelFactory.newChannel() 方法
init方法
void init(Channel channel) throws Exception {
Map<ChannelOption<?>, Object> options = this.options0();
synchronized(options) {
setChannelOptions(channel, options, logger);
}
Map<AttributeKey<?>, Object> attrs = this.attrs0();
synchronized(attrs) {
Iterator var5 = attrs.entrySet().iterator();
while(true) {
if (!var5.hasNext()) {
break;
}
Entry<AttributeKey<?>, Object> e = (Entry)var5.next();
AttributeKey<Object> key = (AttributeKey)e.getKey();
channel.attr(key).set(e.getValue());
}
}
ChannelPipeline p = channel.pipeline();
final EventLoopGroup currentChildGroup = this.childGroup;
final ChannelHandler currentChildHandler = this.childHandler;
final Entry[] currentChildOptions;
synchronized(this.childOptions) {
currentChildOptions = (Entry[])this.childOptions.entrySet().toArray(newOptionArray(this.childOptions.size()));
}
final Entry[] currentChildAttrs;
synchronized(this.childAttrs) {
currentChildAttrs = (Entry[])this.childAttrs.entrySet().toArray(newAttrArray(this.childAttrs.size()));
}
p.addLast(new ChannelHandler[]{new ChannelInitializer<Channel>() {
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = ServerBootstrap.this.config.handler();
if (handler != null) {
pipeline.addLast(new ChannelHandler[]{handler});
}
ch.eventLoop().execute(new Runnable() {
public void run() {
pipeline.addLast(new ChannelHandler[]{new ServerBootstrap.ServerBootstrapAcceptor(ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs)});
}
});
}
}});
}
- 设置 NioServerSocketChannel 的 TCP 属性。
- 由于 LinkedHashMap 是非线程安全的,使用同步进行处理。
- 对 NioServerSocketChannel 的 ChannelPipeline 添加 ChannelInitializer 处理器。
- 这里调用了他的 addLast 方法,也就是将整个 handler 插入到 tail 的前面,因为 tail 永远会在后面,需要做一些系统的固定工作。
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
final AbstractChannelHandlerContext newCtx;
synchronized(this) {
checkMultiplicity(handler);
newCtx = this.newContext(group, this.filterName(name, handler), handler);
this.addLast0(newCtx);
if (!this.registered) {
newCtx.setAddPending();
this.callHandlerCallbackLater(newCtx, true);
return this;
}
EventExecutor executor = newCtx.executor();
if (!executor.inEventLoop()) {
newCtx.setAddPending();
executor.execute(new Runnable() {
public void run() {
DefaultChannelPipeline.this.callHandlerAdded0(newCtx);
}
});
return this;
}
}
this.callHandlerAdded0(newCtx);
return this;
}
- 检查该 handler 是否符合标准,如果没有 Sharable 注解且已经被使用过了,就抛出异常;
- 创建一个 AbstractChannelHandlerContext 对象;
- 然后将 Context 添加到链表中。也就是追加到 tail 节点的前面。
- 最后,同步或者异步或者晚点异步的调用 callHandlerAdded0 方法,在该方法中,调用之前的 handler 的 handlerAdded 方法,而该方法内部调用了之前的 ChannelInitializer 匿名类的 initChannel 方法;
pipeline 的 addLast 方法,实际上创建一个 Context 对象包装了 pipeline 和 handler,然后通过同步或者异步的方式,间接执行 handler 的 自定义方法-------initChannel 方法。而这个 context 也加入到了 pipeline 的链表节点中。
config().group().register(channel) 方法
3.2 doBind0()
private static void doBind0(final ChannelFuture regFuture, final Channel channel, final SocketAddress localAddress, final ChannelPromise promise) {
channel.eventLoop().execute(new Runnable() {
public void run() {
if (regFuture.isSuccess()) {
channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
} else {
promise.setFailure(regFuture.cause());
}
}
});
}
来到了 NioServerSocketChannel 的 pipeline 的 tail 节点的 bind 方法,该方法首先找到出站节点,然后执行出站节点的 invokeBind 方法。
private void invokeBind(SocketAddress localAddress, ChannelPromise promise) {
if (this.invokeHandler()) {
try {
((ChannelOutboundHandler)this.handler()).bind(this, localAddress, promise);
} catch (Throwable var4) {
notifyOutboundHandlerException(var4, promise);
}
} else {
this.bind(localAddress, promise);
}
}
4、总结
- 首先创建2个 EventLoopGroup 线程池数组。数组默认大小CPU*2,方便chooser选择线程池时提高性能。
- BootStrap 将 boss 设置为 group属性,将 worker 设置为 childer 属性。
- 通过 bind 方法启动,内部重要方法为 initAndRegister 和 dobind 方法。
- initAndRegister 方法会反射创建 NioServerSocketChannel 及其相关的 NIO 的对象, pipeline , unsafe,同时也为 pipeline 初始了 head 节点和 tail 节点。同时也含有 NioServerSocketChannelConfig 对象。然后向 pipeline 添加自定义的处理器和 ServerBootstrapAcceptor 处理器。这个处理器用于分配接受的 请求给 worker 线程池。每次添加处理器都会创建一个相对应的 Context 作为 pipeline 的节点并包装 handler 对象。注册过程中会调用 NioServerSocketChannel 的 doRegister 方法注册读事件。
- 在register0 方法成功以后调用在 dobind 方法中调用 doBind0 方法,该方法会 调用 NioServerSocketChannel 的 doBind 方法对 JDK 的 channel 和端口进行绑定,之后在调用 pipeline 的fireChannelActive 最后会调用 NioServerSocketChannel 的 doBeginRead 方法,将感兴趣的事件设置为Accept,完成 Netty 服务器的所有启动,并开始监听连接事件。