服务端启动流程
我们以下面的demo作为本次netty源码分析的入口
public class Server {
public static void main(String[] args) throws Exception{
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
//p.addLast(new LoggingHandler(LogLevel.INFO));
p.addLast(new EchoServerHandler());
}
});
// Start the server.
ChannelFuture f = b.bind(10001).sync();
// Wait until the server socket is closed.
f.channel().closeFuture().sync();
} finally {
// Shut down all event loops to terminate all threads.
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
我们先来看下首先创建的两个NioEventLoopGroup,这两个其实就是netty的线程池。服务端分为两个线程组,一个是boss线程组,主要负责客户端接入,另一个work线程组主要是为了处理客户端的读写请求。我们先来分析一下NioEventLoop的创建
public NioEventLoopGroup() {
this(0);
}
public NioEventLoopGroup(int nThreads) {
this(nThreads, (Executor) null);
}
public NioEventLoopGroup(int nThreads, Executor executor) {
//线程数 null nio底层provider
this(nThreads, executor, SelectorProvider.provider());
}
public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,
final SelectStrategyFactory selectStrategyFactory) {
//线程数 null nio底层provider 默认选择策略 拒绝策略
super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());
}
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
//判断传入的线程数如果为0则nThreads=core * 2 executor=null
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));
}
//默认为null,重新创建executor
if (executor == null) {
//1.newDefaultThreadFactory默认创建FastThreadLocalThread的线程工厂
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
//创建EventExecutor线程组,大小为core*2,用来存放NioEventLoop
children = new EventExecutor[nThreads];
for (int i = 0; i < nThreads; i ++) {
boolean success = false;
try {
//初始化每个事件线程
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 {
if (!success) {
for (int j = 0; j < i; j ++) {
children[j].shutdownGracefully();
}
for (int j = 0; j < i; j ++) {
EventExecutor e = children[j];
try {
while (!e.isTerminated()) {
e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
}
} catch (InterruptedException interrupted) {
// Let the caller handle the interruption.
Thread.currentThread().interrupt();
break;
}
}
}
}
}
//创建线程选择器
chooser = chooserFactory.newChooser(children);
final FutureListener<Object> terminationListener = new FutureListener<Object>() {
@Override
public void operationComplete(Future<Object> future) throws Exception {
if (terminatedChildren.incrementAndGet() == children.length) {
terminationFuture.setSuccess(null);
}
}
};
for (EventExecutor e: children) {
e.terminationFuture().addListener(terminationListener);
}
Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
Collections.addAll(childrenSet, children);
readonlyChildren = Collections.unmodifiableSet(childrenSet);
}
首先调用NioEventLoopGroup的构造函数,然后逐级调用重载的构造函数,其中如果创建NioEventLoopGroup时传入的线程数为0,则默认线程数为core*2,然后调用父类MultithreadEventExecutorGroup的构造函数,首先会判断executor是不是null,如果是就创建一个ThreadPeerTaskExecutor,这里传入了一个newDefaultThreadFactory,这是一个创建线程的线程工厂,我们跟进到它的构造函数中
public DefaultThreadFactory(String poolName, boolean daemon, int priority, ThreadGroup threadGroup) {
if (poolName == null) {
throw new NullPointerException("poolName");
}
if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY) {
throw new IllegalArgumentException(
"priority: " + priority + " (expected: Thread.MIN_PRIORITY <= priority <= Thread.MAX_PRIORITY)");
}
//线程前缀
prefix = poolName + '-' + poolId.incrementAndGet() + '-';
//是否为daemon线程
this.daemon = daemon;
//线程级别
this.priority = priority;
this.threadGroup = threadGroup;
}
根据线程池的类型创建线程池的名称和线程的优先级,默认优先级为5,这里的线程池名称就是启动的时候我们会看到的以NioEventLoopGroup开头的线程池,比如:NioEventLoopGroup-2-1,这里的2就是poolId,而1就是线程的id,我们创建线程就是依赖于这个线程池工厂。继续回到eventLoop的构造函数中,这里创建了一个nThreads个数的EventExecutor数组,然后循环初始化每个事件线程,我们跟着到newChild方法
//初始化eventExecutor
@Override
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
//this指向NioEventLoopGroup
return new NioEventLoop(this, executor, (SelectorProvider) args[0],
((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
}
这里直接创建了NioEventLoop,我们进入它的构造函数中
NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
//this指向NioEventLoopGroup DEFAULT_MAX_PENDING_TASKS默认可以接收的最大长度个数为16
super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
//校验代码已省略
provider = selectorProvider;
//根据是否优化nio的selectedKeys创建selectorTuple
final SelectorTuple selectorTuple = openSelector();
selector = selectorTuple.selector;
unwrappedSelector = selectorTuple.unwrappedSelector;
selectStrategy = strategy;
}
首先会调用父类的构造方法,赋值parent属性,parent属性就是我们创建NioEventLoop时传入的NioEventLoopGroup
protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
boolean addTaskWakesUp, int maxPendingTasks,
RejectedExecutionHandler rejectedHandler) {
super(parent);//赋值所属的NioEventLoopGroup
this.addTaskWakesUp = addTaskWakesUp;//false
this.maxPendingTasks = Math.max(16, maxPendingTasks);//16
this.executor = ObjectUtil.checkNotNull(executor, "executor");
taskQueue = newTaskQueue(this.maxPendingTasks);//LinkedBlockingQueue(16)
//拒绝策略
rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
}
进入到SingleThreadEventExecutor的构造函数,这里赋值最大接收消息条数为16次,并且创建了一个长度为16的阻塞队列
继续回到NioEventLoop的构造函数,这里的openSelector()方法会创建nio底层的selector对象,熟悉nio的同学应该知道这个就是多路复用的选择器,我们进入openSelector方法
private SelectorTuple openSelector() {
final Selector unwrappedSelector;
try {
//创建nio的选择器
unwrappedSelector = provider.openSelector();
} catch (IOException e) {
throw new ChannelException("failed to open a new selector", e);
}
//如果没有开启优化,则直接返回原生的Selector
if (DISABLE_KEYSET_OPTIMIZATION) {
return new SelectorTuple(unwrappedSelector);
}
//创建netty优化的替代对象
final SelectedSelectionKeySet selectedKeySet = new SelectedSelectionKeySet();
//通过授权操作,反射获取sun.nio.ch.SelectorImpl
Object maybeSelectorImplClass = AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
try {
return Class.forName(
"sun.nio.ch.SelectorImpl",
false,
PlatformDependent.getSystemClassLoader());
} catch (Throwable cause) {
return cause;
}
}
});
//如果获取失败了,返回nio原生的selector
if (!(maybeSelectorImplClass instanceof Class) ||
// ensure the current selector implementation is what we can instrument.
!((Class<?>) maybeSelectorImplClass).isAssignableFrom(unwrappedSelector.getClass())) {
//反射获取发生异常
if (maybeSelectorImplClass instanceof Throwable) {
Throwable t = (Throwable) maybeSelectorImplClass;
logger.trace("failed to instrument a special java.util.Set into: {}", unwrappedSelector, t);
}
//不优化返回原生的selector
return new SelectorTuple(unwrappedSelector);
}
final Class<?> selectorImplClass = (Class<?>) maybeSelectorImplClass;
//通过授权,反射将selectedKeys和publicSelectedKeys属性替换为netty优化后的SelectedSelectionKeySet
Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
try {
Field selectedKeysField = selectorImplClass.getDeclaredField("selectedKeys");
Field publicSelectedKeysField = selectorImplClass.getDeclaredField("publicSelectedKeys");
Throwable cause = ReflectionUtil.trySetAccessible(selectedKeysField);
if (cause != null) {
return cause;
}
cause = ReflectionUtil.trySetAccessible(publicSelectedKeysField);
if (cause != null) {
return cause;
}
selectedKeysField.set(unwrappedSelector, selectedKeySet);
publicSelectedKeysField.set(unwrappedSelector, selectedKeySet);
return null;
} catch (NoSuchFieldException e) {
return e;
} catch (IllegalAccessException e) {
return e;
}
}
});
if (maybeException instanceof Exception) {
selectedKeys = null;
Exception e = (Exception) maybeException;
logger.trace("failed to instrument a special java.util.Set into: {}", unwrappedSelector, e);
return new SelectorTuple(unwrappedSelector);
}
selectedKeys = selectedKeySet;
logger.trace("instrumented a special java.util.Set into: {}", unwrappedSelector);
return new SelectorTuple(unwrappedSelector,
//netty优化重写的selector,继承了Selector
new SelectedSelectionKeySetSelector(unwrappedSelector, selectedKeySet));
}
这个方法比较长,其实主要目的就是判断需不需要对selectImpl类中的selectedKeys结构做优化,如果需要优化就会用SelectedSelectionKeySet替换selectedKeys结构,SelectedSelectionKeySet中的属性
SelectionKey[] keys;
selectImpl中的属性
protected Set<SelectionKey> selectedKeys = new HashSet();
protected HashSet<SelectionKey> keys = new HashSet();
private Set<SelectionKey> publicKeys;
private Set<SelectionKey> publicSelectedKeys;
var foo = 'bar';
其实可以看到就是将hashSet结构变成数组结构,数组根据索引遍历的速度会更快,这里是netty对jdk底层nio的优化。每一个NioEventLoop实例中都会有selector
我们回到MultithreadEventExecutorGroup的构造函数中创建线程选择器
//创建线程选择器
chooser = chooserFactory.newChooser(children);
public EventExecutorChooser newChooser(EventExecutor[] executors) {
//根据线程数是不是2的幂选择不同的线程选择器
if (isPowerOfTwo(executors.length)) {
return new PowerOfTwoEventExecutorChooser(executors);
} else {
return new GenericEventExecutorChooser(executors);
}
}
判断传入的线程数是不是2的幂数,创建不同的线程选择器,如果是则返回PowerOfTwoEventExecutorChooser,如果不是就创建一个GenericEventExecutorChooser,线程选择器主要的方法就是next方法
public EventExecutor next() {
//与运算速度更快
return executors[idx.getAndIncrement() & executors.length - 1];
}
public EventExecutor next() {
//求余
return executors[Math.abs(idx.getAndIncrement() % executors.length)];
}
经过对比我们可以发现,如果是2的幂,就通过与的方式获取余数值速度更快,如果不是就按照求余的方式获取下一个线程,这里可以看到获取线程是一个循环的过程
服务端的启动时通过ServerBootStrap引导类完成的,首先引导类绑定了两个eventLoopGroup,bossGroup主要负责客户端的接入,workGroup主要负责客户端的数据读写,然后通过channel方法绑定了处理的方式,如案例中就是nio模式,服务端绑定的就是NioServerSocketChannel,然后通过childHandler绑定一个channel初始化器,初始化器主要是为了完成channelHandler的添加,启动类的入口方法是bind方法,我们通过该方法分析服务端的启动流程,bind方法一步步执行会走到io.netty.bootstrap.AbstractBootstrap#doBind方法
//服务端绑定操作
private ChannelFuture doBind(final SocketAddress localAddress) {
//1.初始化channel并完成注册
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
//2.如果发生了错误返回promise
if (regFuture.cause() != null) {
return regFuture;
}
//3.注册完成,执行bind方法
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);
//4.如果注册还没有完成,添加一个回调,等注册完成了再执行operationComplete中的方法
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();
doBind0(regFuture, channel, localAddress, promise);
}
}
});
return promise;
}
}
这个方法比较长,我们分开来分析,首先分析initAndRegister方法
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
//1.通过反射创建对象,channel是socket的抽象
//NioServerSocketChannel
channel = channelFactory.newChannel();
//2.创建完成后初始化
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);
}
//3.调用boss线程池的注册方法
ChannelFuture regFuture = config().group().register(channel);
//4.如果注册失败了关闭channel
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
首先通过反射创建了我们前面通过启动类的channel绑定的channel
public T newChannel() {
try {
//反射创建对象
return clazz.getConstructor().newInstance();
} catch (Throwable t) {
throw new ChannelException("Unable to create Channel from class " + clazz, t);
}
}
这里可以看到通过反射拿到无参构造函数然后调用,我们跟踪NioServerSocketChannel的构造函数中
public NioServerSocketChannel() {
//创建nio底层的ServerSocketChannel
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
public NioServerSocketChannel(ServerSocketChannel channel) {
//1.调用父类构造方法
super(null, channel, SelectionKey.OP_ACCEPT);
//2.创建配置类
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
首先调用父类的构造函数,传入上面创建的nio的ServerSocketChannel和关注的连接事件OP_ACCEPT
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent);
this.ch = ch;//nio的ServerSocketChannel
this.readInterestOp = readInterestOp;//连接事件
try {
ch.configureBlocking(false);//设置非阻塞模式
} catch (IOException e) {
try {
ch.close();//发生异常就关闭channel
} 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;//null
//1.channel唯一标识id
id = newId();
//2.unsafe是channel的内部类,封装了很多底层nio的操作
unsafe = newUnsafe();
//3.每创建一个channel就会创建一个channelPipe用于组织handler
pipeline = newChannelPipeline();
}
这里逐级调用父类的构造函数,最终走到AbstractChannel中,我们可以看到为每一个channel创建了一个唯一id,然后创建了一个unsafe对象,Unsafe是AbstractChannel的内部接口,里面封装了大量的nio底层操作。然后创建了一个pipeline对象,每一个channel对应一个pipeline,这是一个流水线,用来组织channelHandler,我们继续跟踪发现返回的是一个DefaultChannelPipeline对象,我们跟踪到这个类的构造方法中
protected DefaultChannelPipeline(Channel channel) {
//1.pipeline和channel关联起来
this.channel = ObjectUtil.checkNotNull(channel, "channel");
//2.关联succeededFuture
succeededFuture = new SucceededChannelFuture(channel, null);
//通道连接情况
voidPromise = new VoidChannelPromise(channel, true);
//4.双向链表,pipeline中组织的就是ChannelHandlerContext
tail = new TailContext(this);
head = new HeadContext(this);
head.next = tail;
tail.prev = head;
}
这里可以看到首先将channel和pipeline关联起来,然后创建了succeededFuture和voidPromise实例,然后创建了一个TailContext对象和HeadContext对象,熟悉链表的同学应该能看出来,这里面将这个两个ChannelHandlerContext连接起来形成了双向链表,这里我们只需要知道channelPipeline是为了组织channelHandler的就行,后续在做分析。回到AbstractNioChannel构造函数,首先对ch属性进行赋值,传入的ch就是前面通过provider创建的ServerSocketChannel,从这点我们可以看到每个NioServerSocketChannel内部都会持有一个nio底层的ServerSocketChannel,我们先记住这一点对我们理解会有帮助,然后赋值acceprt事件,并且将ServerSocketChannel设置为非阻塞模式,这个就是我们熟悉的nio代码。我们继续跟踪代码,回到NioServerSocketChannel构造函数,上面完成了父构造函数的调用,现在我们看看NioServerSocketChannelConfig的创建
public DefaultServerSocketChannelConfig(ServerSocketChannel channel, ServerSocket javaSocket) {
super(channel);
if (javaSocket == null) {
throw new NullPointerException("javaSocket");
}
this.javaSocket = javaSocket;
}
public DefaultChannelConfig(Channel channel) {
//byteBuf分配器
this(channel, new AdaptiveRecvByteBufAllocator());
}
跟踪到DefaultServerSocketChannelConfig的构造函数,我们可以看到这里创建了一个ByteBuf的分配器AdaptiveRecvByteBufAllocator里面维护了一个数组,数组内存放了需要分配的ByteBuf的大小,这里我们先知道AdaptiveRecvByteBufAllocator是用于分配ByteBuf的。Bytebuf可以理解就是对nio原始的ByteBuffer的封装,后续我们在详细介绍。这里我们可以思考一下为什么通过反射创建NioServerSocketChannel?主要是为了扩展性,可以创建不同的channel完成模式的切换。到这里NioServerSocketChannel已经创建完成,我们继续回到initAndRegister方法。
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
//1.通过反射创建对象,channel是socket的抽象
//NioServerSocketChannel
channel = channelFactory.newChannel();
//2.创建完成后初始化options和
init(channel);
} catch (Throwable t) {
if (channel != null) {
channel.unsafe().closeForcibly();
return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
}
return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
}
//3.调用boss线程池的注册方法
ChannelFuture regFuture = config().group().register(channel);
//4.如果注册失败了关闭channel
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
NioServerSocketChannel创建完成后对其进行初始化,这里我们分析的是服务端,所以进入服务端引导类的init方法
void init(Channel channel) throws Exception {
//设置配置和属性
final Map<ChannelOption<?>, Object> options = options0();
synchronized (options) {
setChannelOptions(channel, options, logger);
}
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());
}
}
//获取pipeline
ChannelPipeline p = channel.pipeline();
//work线程池,处理读写事件
final EventLoopGroup currentChildGroup = childGroup;
//channelInitializer,初始化的channelHandler
final ChannelHandler currentChildHandler = childHandler;
final Entry<ChannelOption<?>, Object>[] currentChildOptions;
final Entry<AttributeKey<?>, Object>[] currentChildAttrs;
//工作eventLoopGroup的配置和属性
synchronized (childOptions) {
currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size()));
}
synchronized (childAttrs) {
currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));
}
//向channelPipeline中添加一个ChannelInitializer,等待register时执行initChannel方法
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
//1.添加handlers
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
//2.通过内部线程添加一个用于处理连接的channelHandler
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}
通过源码可以看到首先对bossGroup的属性和配置进行设置子,然后对一些属性进行初始化,最后想pipeline中添加了一个ChannelInitializer其实也是一个channelHandler,我们跟踪到addLast方法中
public final ChannelPipeline addLast(EventExecutorGroup executor, ChannelHandler... handlers) {
if (handlers == null) {
throw new NullPointerException("handlers");
}
//循环添加ChannelHandler
for (ChannelHandler h: handlers) {
if (h == null) {
break;
}
addLast(executor, null, h);
}
return this;
};
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
final AbstractChannelHandlerContext newCtx;
synchronized (this) {//防止有其他用户线程在执行添加channelHandler操作
checkMultiplicity(handler);
//1.将传入的handler包装到DefaultChannelHandlerContext中
newCtx = newContext(group, filterName(name, handler), handler);
//2.把DefaultChannelHandlerContext添加到双向链表中
addLast0(newCtx);
//3.如果channel还没有完成注册,就添加一个PendingHandlerAddedTask到
// pendingHandlerCallbackHead单向链表上
if (!registered) {
newCtx.setAddPending();
callHandlerCallbackLater(newCtx, true);
return this;
}
//4.如果channel已经注册,而且不是内部线程在执行addLast操作,将callHandlerAdded0包装成一个任务
//添加到NioEventLoop的普通任务队列中,等待执行
EventExecutor executor = newCtx.executor();
if (!executor.inEventLoop()) {
newCtx.setAddPending();
executor.execute(new Runnable() {
@Override
public void run() {
callHandlerAdded0(newCtx);
}
});
return this;
}
}
//5.如果已经完成注册而且是内部线程,直接调用handlerAdded方法
callHandlerAdded0(newCtx);
return this;
}
我们可以看到首先将传入的handler包装到DefaultChannelHandlerContext中,这是一个channelHandler的上下文,第二步将这个ChannelHandlerContext添加到pipeline初始化时候创建的双向链表中,第三步判断当前channel是不是完成注册,如果channel还没有完成注册,就添加一个PendingHandlerAddedTask回调任务到pendingHandlerCallbackHead单向链表上,第四步判断如果channel已经注册,而且不是内部线程在执行addLast操作,将callHandlerAdded0包装成一个任务添加到NioEventLoop的普通任务队列中,等待执行,最后判断如果已经完成注册而且是内部线程,直接调用handlerAdded方法。我们继续回到init方法ChannelInitializer中的init方法在register时会回调执行,在服务端的pipeline中添加了一个ServerBootstrapAcceptor,这个类从名称就可以看出来是一个处理连接接入的类,这个给类其实就是一个channelHandler,这个方法会将创建连接的channel转给workGroup,然后workGroup也会执行register,然后就可以处理客户端的读写事件。完成了NioServerSocketChannel的创建和初始化,开始调用真正的register方法
//3.调用boss线程池的注册方法
ChannelFuture regFuture = config().group().register(channel);
调用config方法获取引导类配置,然后获取bossGroup,然后调用注册方法,我们继续跟进
public ChannelFuture register(Channel channel) {
//1.通过线程选择器选择一个线程执行register
return next().register(channel);
}
}
public EventExecutor next() {
return chooser.next();
}
首先调用next方法从线程池中获取一个线程调用register方法,这里的chooser我们在之前已经分析过了,netty针对创建的NioEventLoopGroup中的线程数是不是2的幂创建了普通的线程选择器和优化后的线程选择器,最终都是从线程池中获取一个线程然后执行register,进入到SingleThreadEventLoop中
public ChannelFuture register(Channel channel) {
//1.创建一个DefaultChannelPromise用于返回执行结果
return register(new DefaultChannelPromise(channel, this));
}
可以看到这里首先创建了一个DefaultChannelPromise,并且传入了channel和this,DefaultChannelPromise是netty对jdk原生的future的扩展,里面扩展了很多方法,这里主要是为了记录register的结构,我们会在后续详细分析,我们继续关注注册方法
public ChannelFuture register(final ChannelPromise promise) {
ObjectUtil.checkNotNull(promise, "promise");//校验
//调用unsafe的register方法
promise.channel().unsafe().register(this, promise);
return promise;
}
这里通过promise获取channel,通过channel获取其内部类unfase然后调用它的register方法,我们前面已经提到过,unsafe提供了很多底层的方法,我们继续跟踪到AbstractChannel的register方法
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
if (eventLoop == null) {
throw new NullPointerException("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;
}
//关联channel和eventLoop
AbstractChannel.this.eventLoop = eventLoop;
//1.当前运行的线程是eventLoopGroup线程,及内部线程
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
//2.调用execute方法将register0方法包装成一个任务放入NioEventLoop的
//任务队列中,由NioEventLoop线程执行
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
} catch (Throwable t) {
logger.warn(
AbstractChannel.this, t);
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
}
}
我们跳过上面的验证方法,首先判断当前当前执行的线程是不是内部线程,如果是就直接调用register0方法,这里我们是通过main线程调用的所以不是内部线程,会走到else语句中,调用execute方法把任务放入到eventLoop的普通任务队列中,待后续执行。我们跟踪到eventLoop.execute方法,这个方法会开启NioEventLoop线程,这里走到SingleThreadEventExecutor#execute方法
public void execute(Runnable task) {
//校验
if (task == null) {
throw new NullPointerException("task");
}
//1.判断是不是EventLoop线程
boolean inEventLoop = inEventLoop();
//2.如果是eventLoop线程直接添加一个任务到任务队列中
if (inEventLoop) {
addTask(task);
} else {
//3.如果是外部线程,而且eventLoop线程还没有启动则启动该线程
startThread();
addTask(task);
if (isShutdown() && removeTask(task)) {
reject();
}
}
//4.判断是不是需要将NioEventLoop线程,从阻塞的select操作中唤醒
if (!addTaskWakesUp && wakesUpForTask(task)) {
wakeup(inEventLoop);
}
}
首先判断是不是EventLoop线程调用,如果是就直接添加一个任务到任务队列中,如果不是就判断当前的NioEventLoop线程是不是已经启动,没有则启动线程,最后判断是不是需要将NioEventLoop线程从阻塞的select方法中唤醒,唤醒后我们加入任务队列的任务就可以被执行,我们来看看启动线程方法
private void startThread() {
//如果没有启动线程
if (state == ST_NOT_STARTED) {
//CAS修改状态为已启动状态
if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {
try {
//启动线程
doStartThread();
} catch (Throwable cause) {
STATE_UPDATER.set(this, ST_NOT_STARTED);
PlatformDependent.throwException(cause);
}
}
}
}
这里可以看到会判断当前线程的状态是不是已经启动,如果没有就通过CAS改变线程状态为启动状态,然后执行启动方法
private void doStartThread() {
assert thread == null;
executor.execute(new Runnable() {
@Override
public void run() {
//1.首先将thread赋值为当前线程
thread = Thread.currentThread();
if (interrupted) {
thread.interrupt();
}
boolean success = false;
//2.更新最后一次执行时间
updateLastExecutionTime();
try {
//3.执行SingleThreadEventExecutor的run方法
SingleThreadEventExecutor.this.run();
success = true;
} catch (Throwable t) {
logger.warn("Unexpected exception from an event executor: ", t);
} finally {
for (;;) {
int oldState = state;
if (oldState >= ST_SHUTTING_DOWN || STATE_UPDATER.compareAndSet(
SingleThreadEventExecutor.this, oldState, ST_SHUTTING_DOWN)) {
break;
}
}
// Check if confirmShutdown() was called at the end of the loop.
if (success && gracefulShutdownStartTime == 0) {
logger.error("Buggy " + EventExecutor.class.getSimpleName() + " implementation; " +
SingleThreadEventExecutor.class.getSimpleName() + ".confirmShutdown() must be called " +
"before run() implementation terminates.");
}
try {
// Run all remaining tasks and shutdown hooks.
for (;;) {
if (confirmShutdown()) {
break;
}
}
} finally {
try {
cleanup();
} finally {
STATE_UPDATER.set(SingleThreadEventExecutor.this, ST_TERMINATED);
threadLock.release();
if (!taskQueue.isEmpty()) {
logger.warn(
"An event executor terminated with " +
"non-empty task queue (" + taskQueue.size() + ')');
}
terminationFuture.setSuccess(null);
}
}
}
}
});
}
这里又执行了execute方法我们先忽略Runnable内容,我们继续跟踪来到ThreadPerTaskExecutor#execute方法
public void execute(Runnable command) {
//通过线程工厂创建线程,启动并执行command
threadFactory.newThread(command).start();
}
public Thread newThread(Runnable r) {
//创建线程,设置线程名
Thread t = newThread(FastThreadLocalRunnable.wrap(r), prefix + nextId.incrementAndGet());
try {
if (t.isDaemon() != daemon) {
t.setDaemon(daemon);
}
if (t.getPriority() != priority) {
t.setPriority(priority);
}
} catch (Exception ignored) {
// Doesn't matter even if failed to set.
}
return t;
}
//创建一个FastThreadLocalThread线程
protected Thread newThread(Runnable r, String name) {
return new FastThreadLocalThread(threadGroup, r, name);
我们终于找到了正在的start方法,这里我们可以看到通过我们之前分析的线程工厂创建了一个线程然后执行start方法启动,然后执行传入的runnable执行单元,这里从创建的线程其实是一个FastThreadLocalThread,这个类基础了Thread,主要是为了使用FastThreadLocal,这个是netty对ThreadLocal的优化,如果要体现出FastThreadLocal的优势,必须要使用FastThreadLocalThread线程才可以,这个我们后面单独分析。
线程启动后开始执行run方法,首先将启动后的线程赋值当前的thread属性,然后更新最后一次的执行时间,然后开始执行NioEventLoop的run方法,这个run方法是一个循环,当线程状态是isShuttingDown的时候跳出当前线程,然后执行一些退出操作。我们这里只需要知道,run方法主要执行了三个事情:1.执行阻塞或者非阻塞的select方法。2.执行runAllTasks方法,执行我们通过execute方法添加的任务或者是定时时间已经到的定时任务。3.处理select方法已经就绪的selectionKeys的事件。所以这里线程启动后会执行我们的register0方法。
private void register0(ChannelPromise promise) {
try {
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
//1.设置第一此执行注册标志
boolean firstRegistration = neverRegistered;
//2.完成channel注册,返回连接selectionKey
doRegister();
neverRegistered = false;
registered = true;
//3.检查是否触发HandlerAdded事件
pipeline.invokeHandlerAddedIfNeeded();
//4.设置promise注册成功
safeSetSuccess(promise);
//5.触发ChannelRegistered事件
pipeline.fireChannelRegistered();
//6.检查channel是不是已经属于连接成功状态,这里channel还没有bind,所以返回false
if (isActive()) {
if (firstRegistration) {
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
//7.给channel注册读事件
beginRead();
}
}
} catch (Throwable t) {
// Close the channel directly to avoid FD leak.
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
}
首先可以看到先设置了第一次执行注册方法的标志,然后开始执行真正的注册方法doRegister,我们跟进去会进入到AbstractNioChannel#doRegister方法
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
//1.执行nio底层的register方法,并且attch了channel,不关系任何事件
selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
return;
} catch (CancelledKeyException e) {
if (!selected) {
//2.强行执行一次selectNow,取消了的SelectionKey可能还在缓存中未消除
eventLoop().selectNow();
selected = true;
} else {
throw e;
}
}
}
}
这里我们可以看到获取nio的channel然后调用注册方法,将channel注册到selector上,并且附带了一个this对象,而这个this对象指向的就是NioServerSocketChannel,然后这里不关注任何事件,我们回到register0方法中执行下面的逻辑
//3.检查是否触发HandlerAdded事件
pipeline.invokeHandlerAddedIfNeeded();
final void invokeHandlerAddedIfNeeded() {
assert channel.eventLoop().inEventLoop();
//1.判断是不是第一次执行注册,如果是触发事件
if (firstRegistration) {
firstRegistration = false;
callHandlerAddedForAllHandlers();
}
}
private void callHandlerAddedForAllHandlers() {
final PendingHandlerCallback pendingHandlerCallbackHead;
synchronized (this) {
assert !registered;
// This Channel itself was registered.
registered = true;
pendingHandlerCallbackHead = this.pendingHandlerCallbackHead;
// Null out so it can be GC'ed.
this.pendingHandlerCallbackHead = null;
}
//循环遍历PendingHandlerCallback单向链表触发execute方法
PendingHandlerCallback task = pendingHandlerCallbackHead;
while (task != null) {
task.execute();
task = task.next;
}
}
这里会进行判断如果是第一次调用register方法就会调用PendingHandlerCallback单向链表上的task任务,而我们前面分析了向单向链表中添加了一个PendingHandlerAddedTask任务,这里就会调用它的execute方法,而我们在前面分析init方法中,在最后就在pipeline中添加了一个ChannelInitializer,这个方法就包装在PendingHandlerAddedTask中,这时就会回调该方法,就是下面的方法
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
//1.如果已经注册
if (ctx.channel().isRegistered()) {
initChannel(ctx);
}
}
private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
//1.防止重复添加
if (initMap.putIfAbsent(ctx, Boolean.TRUE) == null) { // Guard against re-entrance.
try {
//2.执行重写的初始化操作
initChannel((C) ctx.channel());
} catch (Throwable cause) {
exceptionCaught(ctx, cause);
} finally {
//3.移除本身这个channelInitializer
remove(ctx);
}
return true;
}
return false;
}
我们可以看到通过调用handlerAdd方法最终会调用ChannelInitializer中的initChannel方法,而我们在上面已经说了我们在pipeline中添加了一个ChannelInitializer而且重写了它的initChannel方法,这里会调用到下
//向channelPipeline中添加一个ChannelInitializer,等待register时执行initChannel方法
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
//1.添加handlers
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
//2.通过内部线程添加一个用于处理连接的channelHandler
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
首先判断是不是有handler,如果有就添加到pipeline中,然后通过内部线程向pipeline中添加了一个用于处理连接的handler及ServerBootstrapAcceptor,这个handler会将已经连接成功的channel转给workGroup进行处理,专门处理读写事件。完成初始化后会将ChannelInitializer从pipeline移除。
我们继续回到register0方法中,接着设置promise为sucess,触发ChannelRegistered事件,然后将事件向后面的handler传播,然后判断当前cahnnel是不是已经活跃状态,这里当前channel还没有bind,所以返回false方法执行结束。
走到这里已经完成了channel的初始化和注册,下面开始执行bind操作,我们继续回到AbstractBootstrap的doBind方法,这里首先会判断promise状态是不是已经完成,我们在前面知道register完成对promise设置成了success状态,如果成功这里就会直接调用doBind0方法,如果没有就给promise添加一个listener回调,其实就是观察者模式,如果完成就会触发listener中的事件,完成回调。从代码可以看出,无论是执行if逻辑还是else逻辑最终都会执行doBind0方法,这个才是重点
private static void doBind0(
final ChannelFuture regFuture, final Channel channel,
final SocketAddress localAddress, final ChannelPromise promise) {
//1.通过包装一个任务交给eventLoop执行
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());
}
}
});
}
这里包装一个任务丢给内部线程执行,继续跟踪会走到AbstractChannelHandlerContext的bind方法
public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
if (localAddress == null) {
throw new NullPointerException("localAddress");
}
if (isNotValidPromise(promise, false)) {
// cancelled
return promise;
}
//1.从tail开始遍历双向链表,查询第一个inbound的ChannelHandlerContext,其实就是headContext
final AbstractChannelHandlerContext next = findContextOutbound();
//2.获取eventLoop
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);
}
return promise;
}
首先判断配置的localAddress不能为null,也就是最少应该配置服务端的port。然后遍历pipeline中的双向链表选择一个inbound的ChannelHandlerContext,其实也就是headContext,由于我们这里是内部线程调用,所以这里会走if中,直接调用invokeBind方法。我们走到headContext方法的bind方法中
public void bind(
ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
throws Exception {
unsafe.bind(localAddress, promise);
}
这里调用unsafe的bind方法
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
//一些判断省略
//1.channel是不是active状态,bind前为false
boolean wasActive = isActive();
try {
doBind(localAddress);
} catch (Throwable t) {
safeSetFailure(promise, t);
closeIfClosed();
return;
}
//2.bind的成功后开始执行ChannelActive事件
if (!wasActive && isActive()) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireChannelActive();
}
});
}
safeSetSuccess(promise);
}
首先判断是不是active状态,bind前channel连接还没完成,所以wasActive为false,然后开始执行真正的doBind方法
protected void doBind(SocketAddress localAddress) throws Exception {
//1.判断java版本执行不同的bind操作
if (PlatformDependent.javaVersion() >= 7) {
javaChannel().bind(localAddress, config.getBacklog());
} else {
javaChannel().socket().bind(localAddress, config.getBacklog());
}
}
这里根据不同JDK版本执行不同的绑定方法,绑定成功后则包装一个fireChannelActive方法到eventLoop的普通任务队列中等待执行,唤醒阻塞的select操作,执行新加入的任务,最后尝试将promise设置为success状态。到这里channel已经完成了注册和绑定,但是我们在前面分析过,channel在注册的时候并没有注册任何关心的事件,那是怎么处理的呢?我们继续跟进fireChannelActive方法。最终还是走到headContext的channelActive方法中
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//1.继续往后触发ChannelActive事件
ctx.fireChannelActive();
//2.设置OP_ACCEPT事件
readIfIsAutoRead();
}
可以看到首先继续将channel活跃事件传递到后面的channelHandler,然后设置OP_ACCEPT事件,我们继续跟进
private void readIfIsAutoRead() {
//1.执行read方法
if (channel.config().isAutoRead()) {
channel.read();
}
}
public boolean isAutoRead() {
return autoRead == 1;
}
private volatile int autoRead = 1;
这里继续调用read方法
public ChannelHandlerContext read() {
//1.从双向链表的查找outbound属性为true的ChannelHandlerContext,这里返回的就是headContext
final AbstractChannelHandlerContext next = findContextOutbound();
//2.获取eventLoop
EventExecutor executor = next.executor();
//3.由于是线程内部调用直接执行invokeRead
if (executor.inEventLoop()) {
next.invokeRead();
} else {
Runnable task = next.invokeReadTask;
if (task == null) {
next.invokeReadTask = task = new Runnable() {
@Override
public void run() {
next.invokeRead();
}
};
}
executor.execute(task);
}
return this;
}
这里继续调用headContext的read方法
public void read(ChannelHandlerContext ctx) {
unsafe.beginRead();
}
最终走到了AbstractNioChannel的doBeginRead方法
protected void doBeginRead() throws Exception {
// Channel.read() or ChannelHandlerContext.read() was called
//1.判断selectionKey是不是有效的
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
}
readPending = true;
//2.获取关心的事件,这里其实什么都没关注
final int interestOps = selectionKey.interestOps();
//3.如果什么事件都不关系,则设置关注OP_ACCEPT事件
if ((interestOps & readInterestOp) == 0) {
selectionKey.interestOps(interestOps | readInterestOp);
}
}
这里可以看到首先判断selectionKey是不是有效的,然后获取关心的事件,由于之前注册的时候什么事件都没关注,所以这里添加关注OP_ACCEPT事件。最终将记录结果的promise返回,到这里服务端的启动流程分析就结束了。如果有分析错误的地方还请不吝指正。感谢!!!