最近花了较多时间来看netty,本来是打算先搞本书看看的,放弃了。《Netty实战》这本书其实写的挺好的,主要还是翻译水平可能只比我好一点的原因,但是翻翻源码,了解了一些netty服务启动,建立连接和读取数据等部分后,再来看这本书还是不错的。那边权威指南,强烈不推荐看。另外netty官网推荐的闪电侠的博客写的真的不错,本篇博客有不少地方都是借鉴来着。
public class NettyServer {
public static void main(String[] args) {
NioEventLoopGroup parentGroup = new NioEventLoopGroup();
NioEventLoopGroup childGroup = new NioEventLoopGroup(3);
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(parentGroup, childGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.handler(new SimpleServerHandler())
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ServerHandler());
}
});
ChannelFuture channelFuture = bootstrap.bind(8081).sync();
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
parentGroup.shutdownGracefully();
childGroup.shutdownGracefully();
}
}
}
private static class SimpleServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelActive");
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelRegistered");
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
System.out.println("handlerAdded");
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("channelRead");
// 如果不继续往下调用channelRead, 会导致NioSocketChannel服务注册, 最终导致,服务端的子handler全部失效.
ctx.fireChannelRead(msg);
}
}
ServerBootstrap
是服务端的一个启动辅助类,通过给他设置一系列参数来绑定端口启动服务。
group(bossGroup, workerGroup)
我们需要两种类型的人干活,一个是老板,一个是工人,老板负责从外面接活,接到的活分配给工人干,放到这里,bossGroup
的作用就是不断地accept到新的连接,将新的连接丢给workerGroup
来处理
.channel(NioServerSocketChannel.class)
表示服务端启动的是nio相关的channel,channel在netty里面是一大核心概念,可以理解为一条channel就是一个连接或者一个服务端bind动作,后面会细说
.handler(new SimpleServerHandler()
表示服务器启动过程中,需要经过哪些流程,这里SimpleServerHandler
最终的顶层接口为ChannelHander
,是netty的一大核心概念,表示数据流经过的处理器,可以理解为流水线上的每一道关卡
childHandler(new ChannelInitializer)...
表示一条新的连接进来之后,该怎么处理,也就是上面所说的,老板如何给工人配活
ChannelFuture f = b.bind(8888).sync();
这里就是真正的启动过程了,绑定8888端口,等待服务器启动完毕,才会进入下行代码
f.channel().closeFuture().sync();
等待服务端关闭socket
源码分析
我们从入口方法bind进入, 一路点进去,最后停在doBind
AbstractBootstrap
public ChannelFuture bind(int inetPort) {
return bind(new InetSocketAddress(inetPort));
}
private ChannelFuture doBind(final SocketAddress localAddress) {
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
// 忽略部分代码
doBind0(regFuture, channel, localAddress, promise);
}
从方法initAndRegister
进入, 看名字就知道这个方法是进行初始化以及注册的, 来看具体是怎么做的
AbstractBootstrap
final ChannelFuture initAndRegister() {
// (1) 通过反射创建channel
Channel channel = channelFactory.newChannel();
// (2) 初始化channel
init(channel);
// (3) 进行注册
ChannelFuture regFuture = config().group().register(channel);
return regFuture;
}
来看(1) 通过反射创建channel, 首先得知道channelFactory是如何创建的
public B channel(Class<? extends C> channelClass) {
return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}
public B channelFactory(ChannelFactory<? extends C> channelFactory) {
// 忽略校验
this.channelFactory = channelFactory;
return (B) this;
}
显然创建channelFactory是从我们的用户代码.channel(NioServerSocketChannel.class)开始的, 最终channelFactory=new ReflectiveChannelFactory(channelClass), 来看该方法如何创建channel
public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {
private final Class<? extends T> clazz;
public ReflectiveChannelFactory(Class<? extends T> clazz) {
this.clazz = clazz;
}
@Override
public T newChannel() {
return clazz.newInstance();
}
}
所以可以知道通过newChannel得到的是NioServerSocketChannel对象, 需要注意的是这里通过反射创建的对象必须有无参构造器, 我们深入类NioServerSocketChannel来看看创建的时候做了哪些事情
NioServerSocketChannel
private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
// 除了这个构造方法, 剩下的api都是由NIO提供
public NioServerSocketChannel() {
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
// 创建 ServerSocketChannel
private static ServerSocketChannel newSocket(SelectorProvider provider) {
return provider.openServerSocketChannel();
}
继续往下走
public NioServerSocketChannel(ServerSocketChannel channel) {
// 构造父类, 指定感兴趣的事件为连接事件SelectionKey.OP_ACCEPT
super(null, channel, SelectionKey.OP_ACCEPT);
// 创建netty的配置参数类
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
AbstractNioMessageChannel
protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent, ch, readInterestOp);
}
AbstractNioChannel
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent);
this.ch = ch;
this.readInterestOp = readInterestOp;
ch.configureBlocking(false);
}
绑定了AbstractNioChannel的内部channel为NioServerSocketChannel中创建的NIO的ServerSocketChannel, parent还是null, 感兴趣的事件this.readInterestOp = SelectionKey.OP_ACCEPT, 设置ServerSocketChannel为非阻塞. 接着看服务构造方法
AbstractChannel
protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
这个unsafe和pipeline相当重要,基本上贯穿整个服务启动和客户端连接, 暂时简单看下
AbstractNioMessageChannel
@Override
protected AbstractNioUnsafe newUnsafe() {
return new NioMessageUnsafe();
}
private final class NioMessageUnsafe extends AbstractNioUnsafe {
private final List<Object> readBuf = new ArrayList<Object>();
@Override
public void read() {
assert eventLoop().inEventLoop();
final ChannelConfig config = config();
final ChannelPipeline pipeline = pipeline();
final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
allocHandle.reset(config);
boolean closed = false;
Throwable exception = null;
do {
int localRead = doReadMessages(readBuf);
if (localRead == 0) {
break;
}
if (localRead < 0) {
closed = true;
break;
}
allocHandle.incMessagesRead(localRead);
} while (allocHandle.continueReading());
int size = readBuf.size();
for (int i = 0; i < size; i ++) {
readPending = false;
pipeline.fireChannelRead(readBuf.get(i));
}
readBuf.clear();
allocHandle.readComplete();
pipeline.fireChannelReadComplete();
}
}
protected DefaultChannelPipeline newChannelPipeline() {
return new DefaultChannelPipeline(this);
}
DefaultChannelPipeline
protected DefaultChannelPipeline(Channel channel) {
this.channel = ObjectUtil.checkNotNull(channel, "channel");
// ..
tail = new TailContext(this);
head = new HeadContext(this);
head.next = tail;
tail.prev = head;
}
从构造方法中可以看到, 创建pipeline的时候会绑定channel, 同时构建双向链表, 后续的pipeline.addLast(hander)的时候, 该处理器就添加到链表尾部(tail的前一个节点)
简单总结下通过工厂的反射创建NioServerSocketChannel做了哪些事:
- 继承关系:NioServerSocketChannel -> AbstractNioMessageChannel -> AbstractNioChannel -> AbstractChannel -> Channel
- NioServerSocketChannel内部会持有NIO的ch=ServerSocketChannel(javvaSocket()返回)设置为了非阻塞,感兴趣的事件readInterestOp=SelectionKey.OP_ACCEPT; pipeline,unsafe,config=NioServerSocketChannelConfig
注意到,在上面说到了netty的配置类NioServerSocketChannelConfig
,其继承自DefaultServerSocketChannelConfig
,可以认为几乎是一样的,内部会持有很多netty配置相关的参数。
DefaultServerSocketChannelConfig
public class DefaultServerSocketChannelConfig extends DefaultChannelConfig
implements ServerSocketChannelConfig {
protected final ServerSocket javaSocket;
private volatile int backlog = NetUtil.SOMAXCONN;
/**
* Creates a new instance.
*/
public DefaultServerSocketChannelConfig(ServerSocketChannel channel, ServerSocket javaSocket) {
super(channel);
if (javaSocket == null) {
throw new NullPointerException("javaSocket");
}
this.javaSocket = javaSocket;
}
@Override
public <T> boolean setOption(ChannelOption<T> option, T value) {
validate(option, value);
if (option == SO_RCVBUF) {
setReceiveBufferSize((Integer) value);
} else if (option == SO_REUSEADDR) {
setReuseAddress((Boolean) value);
} else if (option == SO_BACKLOG) {
setBacklog((Integer) value);
} else {
// 父类的设置也是类似,只是拥有更多的属性,比如设置最大超时时间
return super.setOption(option, value);
}
return true;
}
@Override
public ServerSocketChannelConfig setReceiveBufferSize(int receiveBufferSize) {
try {
javaSocket.setReceiveBufferSize(receiveBufferSize);
} catch (SocketException e) {
throw new ChannelException(e);
}
return this;
}
@Override
public ServerSocketChannelConfig setBacklog(int backlog) {
if (backlog < 0) {
throw new IllegalArgumentException("backlog: " + backlog);
}
this.backlog = backlog;
return this;
}
}
再来看(2) 初始化channel
ServerBootstrap
@Override
void init(Channel channel) throws Exception {
ChannelPipeline p = channel.pipeline();
// 设置channel的option和attr
final Map<ChannelOption<?>, Object> options = options();
synchronized (options) {
// 遍历options,设置到config中
setChannelOptions(channel, options, logger);
}
// 关于io的channel的选项设置
// 忽略部分
// 这部分相当重要, 后续讲解 在注册的时候会调用到
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
// 该handler为用户代码中设置的.handler(new SimpleServerHandler())
ChannelHandler handler = config.handler();
if (handler != null) {
// 加入到pipeline
pipeline.addLast(handler);
}
// 往任务队列添加任务
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
// ServerBootstrapAcceptor用来绑定IOchannel, 后面介绍
pipeline.addLast(new ServerBootstrapAcceptor(
currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}
static void setChannelOptions(
Channel channel, Map<ChannelOption<?>, Object> options, InternalLogger logger) {
for (Map.Entry<ChannelOption<?>, Object> e: options.entrySet()) {
setChannelOption(channel, e.getKey(), e.getValue(), logger);
}
}
private static void setChannelOption(
Channel channel, ChannelOption<?> option, Object value, InternalLogger logger) {
try {
if (!channel.config().setOption((ChannelOption<Object>) option, value)) {
logger.warn("Unknown channel option '{}' for channel '{}'", option, channel);
}
} catch (Throwable t) {
logger.warn(
"Failed to set channel option '{}' with value '{}' for channel '{}'", option, value, channel, t);
}
}
总结一下init:获取child相关参数,比如childGroup,childHandler,options,attrs,设置到ServerBootstrapAcceptor这个处理器中,然后添加到pipeline中。当然在这之前需要将boss的初始化处理器加到 pipeline中。之后在注册的时候会调用initChannel方法,将两个处理器加到pipeline中。
再说简单点就是为channel的pipeline中设置初始化的处理器。当注册的时候执行initChannel方法,将两个处理器再加入到pipeline中,其中一个是用于处理数据读取 的。
最后来看 (3) 进行注册
AbstractBootstrap
ChannelFuture regFuture = group().register(channel);
group方法返回的就是NioEventLoopGroup这个bossGroup,创建这个的时候就已经创建好了的NioEventLoop孩子children[默认的核数*2], 每个孩子里面都有个相同的线程池这个在后面会详细讲到
MultithreadEventLoopGroup
public ChannelFuture register(Channel channel) {
return next().register(channel);
}
// 继承自MultithreadEventExecutorGroup
public EventExecutor next() {
return chooser.next();
}
// MultithreadEventExecutorGroup的内部类,next()返回children数组中的一个NioEventLoop, 即轮询得到n个中的一个
private final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser {
@Override
public EventExecutor next() {
return children[childIndex.getAndIncrement() & children.length - 1];
}
}
所以说注册用的是NioEventLoop, 因为从next返回的是NioEventLoop,它是继承自SingleThreadEventLoop
SingleThreadEventLoop
@Override
public ChannelFuture register(Channel channel) {
return register(channel, new DefaultChannelPromise(channel, this));
}
@Override
public ChannelFuture register(final Channel channel, final ChannelPromise promise) {
channel.unsafe().register(this, promise);
return promise;
}
获取channel的unsafe,进行注册。这类实际是NioMessageUnsafe
补充: Unsafe类为Channel的内部接口, 通常是不给外层使用的, 注意这里的不安全是针对外部来说的.
AbstractChannel.AbstractUnsafe
@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
AbstractChannel.this.eventLoop = eventLoop;
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
// 往任务队列添加任务, 如果是首次会绑定线程
eventLoop.execute(new Runnable() {
@Override
public void run() {
// 真正进行注册, 所以在执行任务队列中的任务的时候才会进行注册
register0(promise);
}
});
}
}
eventLoop.inEventLoop()判断当前线程(这里是main)是否等于NioEventLoop中持有的线程,初始肯定是false
接着我们来看eventLoop.execute
SingleThreadEventExecutor
public void execute(Runnable task) {
// 判断当前线程是否为EventLoop持有的线程,前面说了初始启动肯定是false
boolean inEventLoop = inEventLoop();
if (inEventLoop) {
addTask(task);
} else {
// (1) 启动线程
startThread();
// (2)添加任务到任务队列
addTask(task);
}
if (!addTaskWakesUp && wakesUpForTask(task)) {
wakeup(inEventLoop);
}
}
先来看(1) 启动线程干了哪些事
private void startThread() {
if (STATE_UPDATER.get(this) == ST_NOT_STARTED) {
if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {
doStartThread();
}
}
}
private void doStartThread() {
assert thread == null;
executor.execute(new Runnable() {
@Override
public void run() {
thread = Thread.currentThread();
boolean success = false;
SingleThreadEventExecutor.this.run();
success = true;
}
});
}
我们还是先看下executor.execute(runnable)是怎么回事
@Override
public void execute(Runnable command) {
threadFactory.newThread(command).start();
}
这里和4.0版本有点出入, 4.0的doStartThread方法就是thread.start,thread是内部持有的,在构建eventloop的时候就构建好了,利用的是线程工厂。其实 4.1版本虽然用的线程池,其实最终也还是使用的threadFactory.newThread(command)。道理是一样的。4.0版本构建的thread内部的runnable逻辑也和上面一样。
newThread得到的是一个FastThreadLocalThread, 暂时不介绍了, 之后便执行线程的start()方法, 即执行command.run() -> 执行了SingleThreadEventExecutor.this.run(), 由于当前对象是NioEventLoop,所以执行的是该对象的run方法, 我们进去一览
NioEventLoop
protected void run() {
for (;;) {
// 事件轮询 内部是个for死循环, 判断是否有任务或者定时任务或者监听到事件
select(wakenUp.getAndSet(false));
// 处理SelectedKey
processSelectedKeys();
// 执行任务队列中的任务
runAllTasks();
}
}
服务刚启动的时候, 我们就往任务队列里面加入了任务, 比如前面的注册任务register0(promise); 但是并不会监听到连接事件, 所以 select在得知任务队列的时候会跳出循环, 进入到processSelectedKeys()后,由于没有key所以会接着退出执行runAllTasks(), 加入断点也能发现这里执行的就是我们注册的任务register0(promise). 那么我们就来看看是如何注册的吧.
ps: 关于processSelectedKeys是非常重要的, 在后续客户端进行连接的时候会详细介绍, 那会就会进入该方法了.
AbstractChannel
private void register0(ChannelPromise promise) {
doRegister();
// 执行SimpleServerHandler.handlerAdded(ctx)
pipeline.invokeHandlerAddedIfNeeded();
// 执行SimpleServerHandler.channelRegistered(ctx)
pipeline.fireChannelRegistered();
}
doRegister则是真正的进行注册
AbstractNioChannel
protected void doRegister() throws Exception {
for (;;) {
selectionKey = javaChannel().register(eventLoop().selector, 0, this);
return;
}
}
javaChannel()获得的就是NIO的ServerSocketChannel, 后面的register等就是NIO中的channel注册到selector的代码了(由于是异步的, debug后一直走发现还有两个, 一个是最开始的init中添加的, 还有个是最后的doBind0()添加的).
我们再回头看下如何执行所有任务的
SingleThreadEventExecutor
// 从任务队列中不断获取任务并执行
protected boolean runAllTasks(long timeoutNanos) {
// 获取第一个任务
Runnable task = pollTask();
long lastExecutionTime;
for (;;) {
// 执行任务
safeExecute(task);
// 循环获取任务
task = pollTask();
if (task == null) {
lastExecutionTime = ScheduledFutureTask.nanoTime();
break;
}
}
this.lastExecutionTime = lastExecutionTime;
return true;
}
AbstractEventExecutor
// 最初执行的task就是register0(promise)
protected static void safeExecute(Runnable task) {
task.run();
}
到这里我们做个较长的总结:
- 首先是使用channel.unsafe进行注册,这个注册需要传入eventLoop,设置为channel的成员变量。
- 接着执行eventLoop的execute方法,继承自Executor框架,传入注册的任务Runnable(register0(promise)。
- eventLoop.execute中,首先是判断是不是当前线程也就是main线程是不是等于eventLoop中的线程工厂创建的线程,第一次进来的时候肯定不是。所以需要启动线程,即执行startThread()方法
- startThread()方法其实就是启动线程,里面的任务则是轮询,处理轮询到的keys,处理任务队列。注册的任务是在这步之前添加到任务队列中的,所以启动线程后会进行任务的注册。
- 那么注册register0(promise)主要做了什么事呢
- 首先是nio的注册
javaChannel().register(eventLoop().selector, 0, this);
this为channel,进行了attachement,JavaChannel就是ServerSocketChannel - 然后设置registered = true,执行pipeline的invokeHandlerAddedIfNeede和fireChannelRegistered() 这两个方法很重要,在最开始初始化的时候有部分代码就在这里被执行。
- 首先是nio的注册
handler的invokeHandlerAddedIfNeede()分析:
调用pipeline中的各个处理器的handlerAdded方法,注意ChannelInitializer
的handlerAdded是已经实现了的,是个模板方法,其中需要实现initChannel,而之前在init方法中是往pipeline中加了
ChannelInitializer
这么个处理器的,所以在invokeHandlerAddedIfNeede()方法中最终会被执行。init的那部分代码再次贴出来
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = handler();
if (handler != null) {
pipeline.addLast(handler);
}
// 添加到任务队列
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
一直往下跟
DefaultChannelPipeline
final void invokeHandlerAddedIfNeeded() {
assert channel.eventLoop().inEventLoop();
if (firstRegistration) {
firstRegistration = false;
callHandlerAddedForAllHandlers();
}
}
看名字和注释就知道,channel注册了EventLoop后,需要调用所有处理器的added方法
private void callHandlerAddedForAllHandlers() {
final PendingHandlerCallback pendingHandlerCallbackHead;
synchronized (this) {
assert !registered; // pipeline中和channel都是各持各的registered
registered = true;
pendingHandlerCallbackHead = this.pendingHandlerCallbackHead;
// Null out so it can be GC'ed.
this.pendingHandlerCallbackHead = null;
}
PendingHandlerCallback task = pendingHandlerCallbackHead;
while (task != null) {
task.execute();
task = task.next;
}
}
可能你会问this.pendingHandlerCallbackHead;是啥, 什么时候赋值的; 其实这些都是在pipeline.addLast(handler)中做的, 还是先来看看这个addLast
public final ChannelPipeline addLast(String name, ChannelHandler handler) {
return addLast(null, name, handler);
}
@Override
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
final AbstractChannelHandlerContext newCtx;
synchronized (this) {
// 创建包裹了处理器handler的DefaultChannelHandlerContext
newCtx = newContext(group, filterName(name, handler), handler);
// 将ctx添加到pipeline的尾部
addLast0(newCtx);
// If the registered is false it means that the channel was not registered on an eventloop yet.
// In this case we add the context to the pipeline and add a task that will call
// ChannelHandler.handlerAdded(...) once the channel is registered.
// 未注册才会调用
if (!registered) {
// 这里形成单向链表
callHandlerCallbackLater(newCtx, true);
return this;
}
// 忽略部分代码
}
// 调用实现了handlerAdded方法的handler
callHandlerAdded0(newCtx);
return this;
}
private AbstractChannelHandlerContext newContext(EventExecutorGroup group, String name, ChannelHandler handler) {
return new DefaultChannelHandlerContext(this, childExecutor(group), name, handler);
}
private void addLast0(AbstractChannelHandlerContext newCtx) {
AbstractChannelHandlerContext prev = tail.prev;
newCtx.prev = prev;
newCtx.next = tail;
prev.next = newCtx;
tail.prev = newCtx;
}
// ctx封装成PendingHandlerCallback, 拼接成单向链表
private void callHandlerCallbackLater(AbstractChannelHandlerContext ctx, boolean added) {
// 区分是addTask还是removeTask
PendingHandlerCallback task = added ? new PendingHandlerAddedTask(ctx) : new PendingHandlerRemovedTask(ctx);
PendingHandlerCallback pending = pendingHandlerCallbackHead;
if (pending == null) { // 如果是初次addLast那么就设置头部
pendingHandlerCallbackHead = task;
} else {
// Find the tail of the linked-list.
while (pending.next != null) {
pending = pending.next;
}
pending.next = task;
}
}
总结一下addLast: 封装handler成context, 并且添加到pipeline的链表尾部; 同时如果发现是还未注册, 那么构建PendingHandlerAddedTask封装ctx, 并且拼接成单向链表. 注意到由于是未注册的时候才会拼接这个单向链表,所以在前面的init中显然是将ChannelInitialier拼接进来了,所以在后面callHandlerAddedForAllHandlers方法会得到执行。
简单点说就是,在还未注册,即初始化的时候将处理器,主要是ChannelInitialier构建成ctx添加到pipeline中,同时封装成PendingHandlerCallback来构成单向链表,后续注册完后会对链表各节点进行调用。
我们再来看一下DefaultChannelHandlerContext
DefaultChannelHandlerContext
final class DefaultChannelHandlerContext extends AbstractChannelHandlerContext {
private final ChannelHandler handler;
DefaultChannelHandlerContext(
DefaultChannelPipeline pipeline, EventExecutor executor, String name, ChannelHandler handler) {
super(pipeline, executor, name, isInbound(handler), isOutbound(handler));
if (handler == null) {
throw new NullPointerException("handler");
}
this.handler = handler;
}
@Override
public ChannelHandler handler() {
return handler;
}
private static boolean isInbound(ChannelHandler handler) {
return handler instanceof ChannelInboundHandler;
}
private static boolean isOutbound(ChannelHandler handler) {
return handler instanceof ChannelOutboundHandler;
}
}
它继承自AbstractChannelHandlerContext
, 内部持有处理器,提供了两个方法用来判断是入队方法还是出队方法
AbstractChannelHandlerContext
private final boolean inbound;
private final boolean outbound;
private final DefaultChannelPipeline pipeline;
private final String name;
AbstractChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutor executor, String name,
boolean inbound, boolean outbound) {
this.name = ObjectUtil.checkNotNull(name, "name");
this.pipeline = pipeline;
this.executor = executor;
this.inbound = inbound;
this.outbound = outbound;
}
@Override
public Channel channel() {
return pipeline.channel();
}
@Override
public ChannelPipeline pipeline() {
return pipeline;
}
@Override
public EventExecutor executor() {
if (executor == null) {
return channel().eventLoop();
} else {
return executor;
}
}
每个context中是拥有pipeline的,这点很重要,同时还可以获取channel。 由于可以获取到channel,而channel中又在注册的时候绑定了EventLoop,所以根据context可以获取eventLoop
我们再回到callHandlerAddedForAllHandlers
方法
PendingHandlerCallback task = pendingHandlerCallbackHead;
while (task != null) {
task.execute();
task = task.next;
}
这一部分就是不停的从单向链表获取节点PendingHandlerCallback来执行execute,显然介绍到这可以知道这个单向链表中目前就只有一个节点,即在init方法中添加进来的。
我们来看PendingHandlerCallback的实现类PendingHandlerAddedTask
DefaultChannelPipeline
private abstract static class PendingHandlerCallback implements Runnable {
final AbstractChannelHandlerContext ctx;
PendingHandlerCallback next;
PendingHandlerCallback(AbstractChannelHandlerContext ctx) {
this.ctx = ctx;
}
abstract void execute();
}
private final class PendingHandlerAddedTask extends PendingHandlerCallback {
PendingHandlerAddedTask(AbstractChannelHandlerContext ctx) {
super(ctx);
}
@Override
public void run() {
callHandlerAdded0(ctx);
}
@Override
void execute() {
EventExecutor executor = ctx.executor();
if (executor.inEventLoop()) {
callHandlerAdded0(ctx);
} else {
// ...
}
}
}
内部持有一个ctx以及指向下一个节点的指针。注意ctx.executor()
返回的是NioEventLoop,因为每个ctx中是有pipeline的,从pipeline获取channel,从channel获取之前绑定的eventloop。
继续往下走可以看到
private void callHandlerAdded0(final AbstractChannelHandlerContext ctx) {
ctx.handler().handlerAdded(ctx);
}
这里的得到的处理器是ChannelInitializer, 就是init方法中添加到pipeline中的
ChannelInitializer
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
if (ctx.channel().isRegistered()) {
initChannel(ctx);
}
}
@SuppressWarnings("unchecked")
private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
if (initMap.putIfAbsent(ctx, Boolean.TRUE) == null) { // Guard against re-entrance.
try {
initChannel((C) ctx.channel());
} catch (Throwable cause) {
} finally {
remove(ctx); // 注意这里,执行完了以后会将这个ctx,这里就是ChannelInitializer在pipeline的双向链表中移除掉。
}
return true;
}
return false;
}
protected abstract void initChannel(C ch) throws Exception;
initChannel就是我们的init方法里面添加到pipeline中的处理器ChannelInitializer来实现的,所以从这里点进去就到了下面这个地方(又贴了一次)
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = handler();
if (handler != null) {
pipeline.addLast(handler);
}
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
进入到initChannel中后,将设置给ServerBootStrap中的处理器也加到pipeline中. 然后构建任务加到任务队列中。
注意这里pipeline.addLast(handler);会导致调用一次该处理器SimpleServerHandler的handlerAdded方法。
接下来的execute方法会将处理器添加到队列中,在select方法中轮询出来会继续执行
我们再回到最初的AbstractBootstrap的doBind0(regFuture, channel, localAddress, promise);
AbstractBootstrap
private static void doBind0(
final ChannelFuture regFuture, final Channel channel,
final SocketAddress localAddress, final ChannelPromise promise) {
// This method is invoked before channelRegistered() is triggered.
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());
}
}
});
}
AbstractChannel
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
return pipeline.bind(localAddress, promise);
}
DefaultPipelineChannel
@Override
public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
return tail.bind(localAddress, promise);
}
最终在
AbstractChannelHandlerContext
public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise){
// DefaultChannelPipeline$HeadContext
final AbstractChannelHandlerContext next = findContextOutbound();
// NioEventLoop
EventExecutor executor = next.executor();
next.invokeBind(localAddress, promise);
return promise;
}
// 获取头部Context 此时已经有了两个中间节点了,一个是自定义的SimpleChannelHandler构成的context,一个是init方法中的ServerBootstrapAcceptor
private AbstractChannelHandlerContext findContextOutbound() {
AbstractChannelHandlerContext ctx = this;
do {
ctx = ctx.prev;
} while (!ctx.outbound);
return ctx;
}
点进去invokeBind, 最终调用到HeadContext
HeadContext
@Override
public void bind(
ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
throws Exception {
// unsafe = pipeline.channel().unsafe();,在构建head的时候构建出来的
unsafe.bind(localAddress, promise);
}
再往下走到NioServerSocketChannel
@Override
protected void doBind(SocketAddress localAddress) throws Exception {
if (PlatformDependent.javaVersion() >= 7) {
javaChannel().bind(localAddress, config.getBacklog());
} else {
javaChannel().socket().bind(localAddress, config.getBacklog()); // 绑定并设置最大连接数
}
}
javaChannel().bind(localAddress, config.getBacklog());也就是NIO中的地址绑定
到这里我们再总结一下:前面我们说了启动线程后会进行轮询,判断队列里面有任务之后,执行队列里面的任务。注意eventLoop.execute会将任务加到队列,第一次没启动线程的时候则先启动线程。
任务队列里面首先是一个注册的任务,注册要干三件事:利用selector进行注册,执行单向链表pendingHandlerCallbackHead中的execut方法,最终会调用到其中包含的ctx.handler的handlerAdded方法,而此时 该单向链表中仅有一个ctx,就是init方法中调用了pipeline.addLast加入进来的处理器ChannelInitializer,注意:只有当还未注册的时候调用pipeline.addLast才会去构建那个单向列表。
而处理器ChannelInitializer的handlerAdded方法中会调用initChannel方法,这是够抽象方法,这init里面的已经实现了。调用该方法会将构建ServerBootstrap的handler(SimpleChannelHandler)加入到pipeline 中。并且执行eventLoop.execute将任务加入到任务队列中,该任务为将ServerBootstrapAcceptor加入到pipeline中。
在后续doBind中又会构建任务加入到任务队列中,所以此时任务队列中有两个任务了。轮询出来后会依次执行这两个任务,一个是将ServerBootstrapAcceptor加入到pipeline中,记住此时pipeline中有两个处理器了; 一个是执行地址绑定。