ServerBootstrap创建流程解析(1)
直奔主题
public final class EchoServer {
public static void main(String[] args) throws Exception {
//配置一个bossGroup.
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
//配置一个workerGroup
EventLoopGroup workerGroup = new NioEventLoopGroup(4);
try {
//创建一个server启动类
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new EchoServerHandler());
}
});
ChannelFuture f = b.bind(8007).sync();
f.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
EventLoopGroup的创建流程上篇文章已经分析完了,现在来分析
//创建一个服务端启动对象
ServerBootstrap b = new ServerBootstrap();
先来看下该类的继承结构
ServerBootstrap是用来组装构建服务端程序需要的资源对象,可以看出它包含EventLoopGroup属性,channelFactory属性等等,我们直接进入它的源码看看
//无参构造函数,什么都没做,不像之前创建group那样层层调用父类构造器
public ServerBootstrap() { }
//带有ServerBootstrap参数的构造器,即可以把一个创建好的ServerBootstrap对象转移到另一个 ServerBootstrap可以看出都是一些属性的赋值
private ServerBootstrap(ServerBootstrap bootstrap) {
super(bootstrap);
childGroup = bootstrap.childGroup;
childHandler = bootstrap.childHandler;
synchronized (bootstrap.childOptions) {
childOptions.putAll(bootstrap.childOptions);
}
childAttrs.putAll(bootstrap.childAttrs);
}
好了 ServerBootstrap b = new ServerBootstrap();创建过程就完成了没撒逻辑,也没有给构造参数传什么参数,肯定是通过另一种Builder模式给属性赋值,毕竟要传入的参数太多了,都通过构造器来创建不好看
//直接来分析创建完ServerBootstrap的属性赋值过程
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new EchoServerHandler());
}
});
//首先分析这个步骤,直接传入两个EventLoopGroup对象一个bossGroup,一个workerGroup我们知道其实这两个线程池对象完全是一样的,
//唯一的区别就是各自传入的线程数量不同,但是他在ServerBootstrap各自承担什么责任呢后面我们会具体说明,先看下它是如何被复制给ServerBootstrap的
b.group(bossGroup, workerGroup)
|
V
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
//通过名字就可以看出parentGroup是传给AbstractBootstrap的里面没什么逻辑你可用点进去看就一个属性赋值操纵
super.group(parentGroup);
//childGroup判断一下是不是已经设置过了childGroup
if (this.childGroup != null) {
throw new IllegalStateException("childGroup set already");
}
//childGroup判断一下是不是为空都是在赋值给ServerBootstrap之前做一些检测判断
this.childGroup = ObjectUtil.checkNotNull(childGroup, "childGroup");
//返回ServerBootstrap
return this;
}
//group分析完了现在分析.channel这步很关键,前面我们就说了netty封装了java nio在构造服务端的时候
//第一步就是创建ServerSocketChannel通过它来绑定端口,设置非阻塞注册相关监听事件等待。这个复制操作是不是就是封装了java nio的ServerSocketChannel对象呢我们来仔细分析一下
b.channel(NioServerSocketChannel.class)
|
V
//调用抽象父类AbstractBootstrap的channel方法创建了一个反射工厂来创建
//NioServerSocketChannel对象。这个方法就是给父类的ChannelFactory属性设置
//ReflectiveChannelFactory对象我们具体来看下这个对象的方法
public B channel(Class<? extends C> channelClass) {
return channelFactory(new ReflectiveChannelFactory<C>(
ObjectUtil.checkNotNull(channelClass, "channelClass")
)); }
这里我们来分析一下ReflectiveChannelFactory类具体作用
public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {
//构造器对象
private final Constructor<? extends T> constructor;
public ReflectiveChannelFactory(Class<? extends T> clazz) {
ObjectUtil.checkNotNull(clazz, "clazz");
try {
//这里关键点,这里将传入的NioServerSocketChannel Class对象的构造函数传给了属性constructor
//通过这个我们就可以调用下面的newChannel方法创建一个NioServerSocketChannel实例对象
//就是一个工厂设计模式
this.constructor = clazz.getConstructor();
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException("Class " + StringUtil.simpleClassName(clazz) +
" does not have a public non-arg constructor", e);
}
}
@Override
public T newChannel() {
try {
return constructor.newInstance();
} catch (Throwable t) {
throw new ChannelException("Unable to create Channel from class " + constructor.getDeclaringClass(), t);
}
}
@Override
public String toString() {
return StringUtil.simpleClassName(ReflectiveChannelFactory.class) +
'(' + StringUtil.simpleClassName(constructor.getDeclaringClass()) + ".class)";
}
}
接下来分析handle属性的赋值
//看方法名字就能看出一个给抽象父类AbstractBootstrap ChannelHandler属性赋值
//一个给ServerBootstrap本身的ChannelHandler
//这两个ChannelHandler有什么区别的,这设计到,ChannelHandler接口的作用,也是比较复杂的我们直接来分析一下ChannelHandler的体系结构和作用先跳过这里
b.handler(new LoggingHandler(LogLevel.INFO))
b.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new EchoServerHandler());
}
});
ChannelHandler作用分析(2)
首先我给你描述一下这个接口的大致作用,前面我们就说过Netty封装了Java NIO让我们的开发程序更加灵活简便,你可以去看我们之前实现Java NIO的时候当一个事件被促发,比如客户端传来了数据,服务端read事件被激活,这个时候我们都是用一个方法来处理被激活的事件并且提取传过来的内容。
Netty就不一样的,你只要实现该接口,当相应的事件被触发的时候他会自动调用实现了该接口类相应的方法。当然我描述的只是一部分内容,ChannelHandler的功能有很多之后我们在源码分析的时候碰到在分析。
要了解ChannelHandler我们最好结合ChannelPipeline、ChannelHandlerContext来分析
ChannelHandler的接口分析
public interface ChannelHandler {
//当一个ChannelHandler被添加到ChannelHandlerContext的时候这个接口方法被触发
void handlerAdded(ChannelHandlerContext ctx) throws Exception;
//当一个ChannelHandlerContext被ChannelHandlerContext移除的时候触发
void handlerRemoved(ChannelHandlerContext ctx) throws Exception;
//当在处理事件的过程中发生Throwable 则触发该方法
@Deprecated
void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
//自定义了一个@Sharable注解如果哪个ChannelHandler的实现类用了这个注解表示可以 添加到多个ChannelPipeline中
@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Sharable {
// no value
}
}
来看下ChannelHandler有那些主要的实现类
根据图片可以看出ChannelHandler接口分为两个实现方向。一个是Inbound另一个是Outbound,从图中也可以看出我们在编写netty的客户端和服务端实例的时候创建的EchoClientHandler处理类来接收服务端返回和消息和EchoServerHandler来接收客户端发来的消息
在netty中,IO 事件被分为 Inbound 事件和 Outbound 事件。in表示的是往里的方向,比如Inboud接口中的read事件,如果通道里面有数据可读那么我就会触发read事件,比如还有accept是否有连接过来。而Out表示出去,相关的操作write表示我要写数据给另一端,connect连接另一端,bind等等有out含义的操作
细心的你可能会发现你不是说read是属于入的方向,也就是Inbound吗,那为什么我看你的图ChannelOutboundHandler有个read方法啊。这不是矛盾了吗。这里开始我也很疑惑,通过查阅资料通过这个能实现一直反压机制。理解这个可能你具备网络协议和OS相关的知识并且需要明白netty的数据是怎么流动的。当然你可用通过这两个链接体现体会一下具体是怎么实现反压机制的链接1链接2
ChannelHandlerContext作用分析(3)
首先分析一下实现该接口的继承体系
主要看一下AbstractChannelHandlerContext和DefaultChannelHandlerContext,包含有pre next一个前驱指针和后驱指针,应该是用来构建链表的,还有一个在DefaultChannelHandlerContext中的handler属性,其实就是用来构建包含ChannelHandler处理链的双向链表。如何构建呢,则是通过ChannelPipeline的方法来构建一个链表
ChannelPipeline作用分析(4)
首先我们来看一下实现该接口的类的关系图
图有点长,你可以大致浏览一下方法有跟链表相关的方法比如add,remove,还有以fire开头的方法,我们说过HandleContext会构建包含ChannelHandle的链表,而ChannelHandle又包含了各种IO事件,当有相应的IO事件触发时,通过调用fire相关的方法可以准确的调用链表中符合被触发事件相关的方法。这些只是抽象的概念后面我们会通过源码具体分析。
回到ServerBootstrap创建流程
public final class EchoServer {
public static void main(String[] args) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup(4);
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new EchoServerHandler());
}
});
ChannelFuture f = b.bind(8007).sync();
f.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
handle的牵扯到的相关知识点就分析完毕了,现在我们回到主流程继续分析
.handler(new LoggingHandler(LogLevel.INFO))
//这里为ServerBootstrap的ChannelHandler赋值一个ChannelInitializer
//里面包含了一个initChannel的方法,在哪里会被调用呢?
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new EchoServerHandler());
}
});
到这里我们可以发现ServerBootstrap都是一些属性的赋值,没有什么逻辑,准确的说都是在准备初始化一些资源,用到这些资源从ServerBootstrap的bind()方法开始
//绑定一个端口,里面做的工作可不止绑定一个端口那么简单
ChannelFuture f = b.bind(8007).sync();
|
V
//和Java Nio一样也new了一个InetSocketAddress对象来绑定端口
public ChannelFuture bind(int inetPort) {
return bind(new InetSocketAddress(inetPort));
|
V
public ChannelFuture bind(SocketAddress localAddress) {
//验证一些属性是不是空的并抛出相应的异常。这里自己点进去看就行了
validate();
return doBind(ObjectUtil.checkNotNull(localAddress, "localAddress"));
}
return doBind(ObjectUtil.checkNotNull(localAddress, “localAddress”));这里详细分析一下
private ChannelFuture doBind(final SocketAddress localAddress) {
//从这里开始分析
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
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);
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;
}
}
final ChannelFuture regFuture = initAndRegister();
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
//这是我们前面设置的用来创建NioServerSocketChannel工厂类,弄了这么多逻辑终于才到了与JavaNIO
// ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();相同的逻辑
//当然这里创建的NioServerSocketChannel是netty的不过它里面肯定封装了java版本的ServerSocketChannel
//往下跳我们仔细分析这个逻辑
channel = channelFactory.newChannel();
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);
}
ChannelFuture regFuture = config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
//创建了一个NioServerSocketChannel类我们来看下它的无参构造函数里面有没有逻辑
channel = channelFactory.newChannel();
public NioServerSocketChannel() {
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
|
V
//通过newSocket创建了Java Nio中的ServerSocketChannel即和Java NIO构造服务端的
//ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();效果一样
private static ServerSocketChannel newSocket(SelectorProvider provider) {
try {
return provider.openServerSocketChannel();
} catch (IOException e) {
throw new ChannelException(
"Failed to open a server socket.", e);
}
}
|
V
public NioServerSocketChannel(ServerSocketChannel channel) {
//调用父类构造函数,传入null,创建好的ServerSocketChannel和一个监听事件的SelectionKey
//这个属性是用来注册accept=16,在这里只是赋值一下,真正的注册逻辑在后面
super(null, channel, SelectionKey.OP_ACCEPT);
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
|
V
protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
//paent=null,ch=ServerSocketChannel readInterestOp=16
super(parent, ch, readInterestOp);
}
|
V
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent);
//赋值ServerSocketChannel
this.ch = ch;
//readInterestOp=16用来注册该channel 16=accpect事件,但是这个英文翻译过来是读但是读应该是1.这里有点迷糊。需要分析后面的代码,这个值应该会被修改
this.readInterestOp = readInterestOp;
try {
//和Java NIO一样设置非阻塞
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);
}
}
|
V
//super(parent);
protected AbstractChannel(Channel parent) {
//parent=null
this.parent = parent;
id = newId();
unsafe = newUnsafe();
//重点这里,创建一个pipline,因此从这里可以得出一个channel包含一个pipeline包含一个HandleContex的双向链表包含ChannelHandle
pipeline = newChannelPipeline();
}
|
V
//创建了一个默认的DefaultChannelPipeline并把当前对象this传入
protected DefaultChannelPipeline newChannelPipeline() {
return new DefaultChannelPipeline(this);
}
|
V
protected DefaultChannelPipeline(Channel channel) {
this.channel = ObjectUtil.checkNotNull(channel, "channel");
succeededFuture = new SucceededChannelFuture(channel, null);
voidPromise = new VoidChannelPromise(channel, true);
//主要这里TailContext和HeadContext继承了AbstractChannelHandlerContext作为了链表的
//头部节点和尾部节点,他不想DefaultChannelHandlerContext类那样有个ChannelHandler属性
//但是TailContext实现了ChannelInboundHandler接口,因此如果有一个通道的入事件发生
//比如read那么他也会被触发相应的方法而HeadContext实现了ChannelOutboundHandler,
//和ChannelInboundHandler 只要有入站或出站事件发生都会执行相应的方法
tail = new TailContext(this);
head = new HeadContext(this);
head.next = tail;
tail.prev = head;
}
到这里先停一下总结一下,上面的逻辑创建了一个NioServerSocketChannel对象里面封装了ServerSocketChannel,并且初始化了一个AbstractChannelHandlerContext类型的双向链表.
+--------------------------------------------------------------+-----------+|
| NioServerSocketChannel |
| +---------------------------------------------------+---------------+ |
| | ChannelPipeline | |
| | | |
| | +---------------------+ | |
| | | | HeadContext | | | |
| | +----------+----------+ | |
| | | ^ | |
| | v | | |
| | +----------+----------+ | |
| | | TailContext | | |
| | +----------+----------+ | |
| | . | |
| |-------------------------------------------------------------------- | . | |
| |
+--------------------------------------------------------------+-----------+
//前面分析完了NioServerSocketChannel对象创建的过程,现在分析对NioServerSocketChannel一些初始化资源逻辑
init(channel);
@Override
void init(Channel channel) {
//设置管道的Option和一些属性。
setChannelOptions(channel, newOptionsArray(), logger);
setAttributes(channel, attrs0().entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY));
//获取ChannelPipeline对象
ChannelPipeline p = channel.pipeline();
final EventLoopGroup currentChildGroup = childGroup;
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);
//这里重点。在head—-->tail链表中插入一个ChannelInitializer。里面的initChannel方法会在某个地方被触发
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) {
final ChannelPipeline pipeline = ch.pipeline();
//我们设置的LoggingHandler对象
ChannelHandler handler = config.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));
}
});
}
});
}
//ChannelPipeline对象的方法这里我们详细分析一下,后面再出现就跳过了
//根据我们前面的知识,这里肯定将ChannelInitializer封装成HandleContext之后插入到headContext和tailContext之间并且调整相应的指针
p.addLast(ChannelInitializer)
@Override
//可变参数,因此你可以传入多个ChannelHandler类型他会给你封装成对应的数组
//这里就是handlers[0]=ChannelInitializer
public final ChannelPipeline addLast(ChannelHandler... handlers) {
return addLast(null, handlers);
}
|
V
@Override
public final ChannelPipeline addLast(EventExecutorGroup executor, ChannelHandler... handlers) {
//判断一下是不是为空
ObjectUtil.checkNotNull(handlers, "handlers");
//循环添加ChannelHandler直到为null
for (ChannelHandler h: handlers) {
if (h == null) {
break;
}
addLast(executor, null, h);
}
return this;
}
|
V
@Override
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
final AbstractChannelHandlerContext newCtx;
//同步加锁一下,防止并发导致链表中的节点丢失
synchronized (this) {
//判断该Handle是否能被共享 ,这里我们进入代码详细分析一下请跳到下一张图
checkMultiplicity(handler);
newCtx = newContext(group, filterName(name, handler), handler);
addLast0(newCtx);
if (!registered) {
newCtx.setAddPending();
callHandlerCallbackLater(newCtx, true);
return this;
}
EventExecutor executor = newCtx.executor();
if (!executor.inEventLoop()) {
callHandlerAddedInEventLoop(newCtx, executor);
return this;
}
}
callHandlerAdded0(newCtx);
return this;
}
private static void checkMultiplicity(ChannelHandler handler) {
//@Sharable
//public abstract class ChannelInitializer<C extends Channel> extends ChannelInboundHandlerAdapter
//可以看出ChannelInitializer是被@Sharable注解修饰了,表示这个handle可以被共享,如果没有被修饰并且这个ChannelInitializer已经被添加过了,则会抛出异常
if (handler instanceof ChannelHandlerAdapter) {
ChannelHandlerAdapter h = (ChannelHandlerAdapter) handler;
//是否是Sharable,是否被添加过added
if (!h.isSharable() && h.added) {
throw new ChannelPipelineException(
h.getClass().getName() +
" is not a @Sharable handler, so can't be added or removed multiple times.");
}
//标记added=true表示添加过
h.added = true;
}
}
//和我们描述的一样将handler封装成HadnleContext
newCtx = newContext(group, filterName(name, handler), handler);
|
V
private AbstractChannelHandlerContext newContext(EventExecutorGroup group, String name, ChannelHandler handler) {
return new DefaultChannelHandlerContext(this, childExecutor(group), name, handler);
}
//开始将创建的newCtx插入到链表中
addLast0(newCtx);
|
V
//很平常的往双向链表中插入一个元素操作
private void addLast0(AbstractChannelHandlerContext newCtx) {
AbstractChannelHandlerContext prev = tail.prev;
newCtx.prev = prev;
newCtx.next = tail;
prev.next = newCtx;
tail.prev = newCtx;
}
//该通道是否注册到eventLoop中,也就是我们创建的channel是否被线程管理,这里为false。还没有
//之前的所有操作都还是在准备资源和初始化资源
if (!registered) {
//设置一下正在添加的状态
newCtx.setAddPending();
//这里为回调handlerAdded()做准备,
//前面我们就分析了Channel接口当被添加到pipe的时候会触发handlerAdded方法
callHandlerCallbackLater(newCtx, true);
//返回
return this;
}
private void callHandlerCallbackLater(AbstractChannelHandlerContext ctx, boolean added) {
assert !registered;
//这里我们知道added为true即创建一个PendingHandlerAddedTask对象以ctx为参数
//如果为false即创建一个PendingHandlerRemovedTask
//一个用来准备回调hadnleadd一个用来回调handlerRemoved
PendingHandlerCallback task = added ? new PendingHandlerAddedTask(ctx) : new PendingHandlerRemovedTask(ctx);
PendingHandlerCallback pending = pendingHandlerCallbackHead;
if (pending == null) {
//设置一个头部指针,后面会形成一个单链表
pendingHandlerCallbackHead = task;
} else {
// 形成一个单链表,这个单链表存放了回调handleAdd方法的所有Handle对象
//当然现在还没有进行正常的回调,知识准备好了相应的动作,只差触发了
//pendingHandlerCallbackHead是DefaultChannelPipeline的一个属性,因此只要在适当时机
//获取这个属性就可以执行所有的回调
while (pending.next != null) {
pending = pending.next;
}
pending.next = task;
}
}
//这里我们挑PendingHandlerAddedTask来分析
//这个类作为PendingHandlerAddedTask继承的抽象父类,里面包含了一个AbstractChannelHandlerContext
//和一个PendingHandlerCallback对象并且实现了Runnable接口
//PendingHandlerCallback next属性是用来形成单链表的
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
//用来回调handleadd方法的
public void run() {
callHandlerAdded0(ctx);
}
@Override
//这个也是用来回调handleadd方法的。与上面的区别后面具体分析
void execute() {
EventExecutor executor = ctx.executor();
if (executor.inEventLoop()) {
callHandlerAdded0(ctx);
} else {
try {
executor.execute(this);
} catch (RejectedExecutionException e) {
if (logger.isWarnEnabled()) {
logger.warn(
"Can't invoke handlerAdded() as the EventExecutor {} rejected it, removing handler {}.",
executor, ctx.name(), e);
}
atomicRemoveFromHandlerList(ctx);
ctx.setRemoved();
}
}
}
}
到这里init(channel)方法的逻辑就将完了,和方法名一样的含义都是一些初始化的动作
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
channel = channelFactory.newChannel();
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);
}
//上面的异常逻辑我们就不考虑,直接接着正常逻辑分析
//初始化channel所需要的资源后,现在开始进行register
//这里就是将channel 注册到group线程
ChannelFuture regFuture = config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
ChannelFuture regFuture = config().group().register(channel);
//这里简单说config().group()获取的是AbstractBootstrap的goup线程池也就是开始我们创建的bossGroup
//workerGroup还没被使用
|
V
public ChannelFuture register(Channel channel) {
return next().register(channel);
|
V
//挑选出一个EventLoop来执行register操作
public EventLoop next() {
return (EventLoop) super.next();
}
|
V
//这个chooser就是前面我们说的,根据你传入的线程数量来创建响应的chooser
//由前面可知我们给bossGroup传入的是1=2^0因此choose=PowerOfTwoEventExecutorChooser
public EventExecutor next() {
return chooser.next();
}
}
//重点分析
register(channel);
|
@Override
//this=NioEventLoop和channel被封装到了DefaultChannelPromise对象
//DefaultChannelPromise实现了异步接口Future
public ChannelFuture register(Channel channel) {
return register(new DefaultChannelPromise(channel, this));
}
|
V
public ChannelFuture register(final ChannelPromise promise) {
ObjectUtil.checkNotNull(promise, "promise");
promise.channel().unsafe().register(this, promise);
return promise;
}
|直接来到AbstractChannel的register方法
V
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
ObjectUtil.checkNotNull(eventLoop, "eventLoop");
//判断该channel是否被注册过如果被注册过则抛出异常
//这里可以体现出一个channel只能被一个EventLoop 线程绑定
if (isRegistered()) {
promise.setFailure(new IllegalStateException("registered to an event loop already"));
return;
}
//这里判断一下该实例是否instanceof NioEventLoop如果不是则抛出异常
if (!isCompatible(eventLoop)) {
promise.setFailure(
new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
return;
}
//获取上下文的实现了AbstractChannel的实例类中的eventLoop对象
AbstractChannel.this.eventLoop = eventLoop;
//这里判断当前线程是不是eventLoop
//inEventLoop(Thread.currentThread());
//显然不是这里我们的线程是运行程序的main线程
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
//走到这里提交任务给eventLoop运行我们详细分析一下先跳过后面的逻辑
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);
}
}
}
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
}
public void execute(Runnable task) {
//判断任务是不是为空
ObjectUtil.checkNotNull(task, "task");
//task=promise (task instanceof LazyRunnable)表示该任务是不是不需要立即执行
//这里的task不属于LazyRunnable因此为false
//wakesUpForTask(task)该方法就返回一个True
execute(task, !(task instanceof LazyRunnable) && wakesUpForTask(task));
|
V
//immediate=true
private void execute(Runnable task, boolean immediate) {
//当前线程是否为inEventLoop=false
boolean inEventLoop = inEventLoop();
//先把任务添加到taskQueue队列里面如果失败则hrow new RejectedExecutionException("event executor terminated");
// protected void addTask(Runnable task) {
// ObjectUtil.checkNotNull(task, "task");
// if (!offerTask(task)) {
// reject(task);
// }
//}
addTask(task);
if (!inEventLoop) {
//既然你不是inEventLoop类型的线程,那我要自己创建了
startThread();
if (isShutdown()) {
boolean reject = false;
try {
if (removeTask(task)) {
reject = true;
}
} catch (UnsupportedOperationException e) {
}
if (reject) {
reject();
}
}
}
if (!addTaskWakesUp && immediate) {
wakeup(inEventLoop);
}
}
}
private void startThread() {
//第一次准备开启EventLoop线程先判断一下状态,默认state=1
//ST_NOT_STARTED=1;表示状态没有开启
if (state == ST_NOT_STARTED) {
//更新一下为ST_STARTED表示状态开启了
if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {
boolean success = false;
try {
//真正的开启一个EventLoop线程我们这里详细分析
doStartThread();
success = true;
} finally {
if (!success) {
STATE_UPDATER.compareAndSet(this, ST_STARTED, ST_NOT_STARTED);
}
}
}
}
}
private void doStartThread() {
assert thread == null;
//这个executor就是我们创建NioEventLoopGroup流程中的哪个线程工厂对象ThreadFactory
//提交这个任务就会被立马开启线程运行
// threadFactory.newThread(command).start();
executor.execute(new Runnable() {
@Override
public void run() {
//thread现在是nioEventLoop至少这里面是
thread = Thread.currentThread();
//interrupted=false没被中断过
if (interrupted) {
thread.interrupt();
}
boolean success = false;
//更新最近一次执行时间
updateLastExecutionTime();
try {
//执行NioEventLoop中的run方法,好不容易开启了一个nioEventLoop线程
//run方法肯定不会轻易返回的,肯定要被复用,没有任务的时候需要被挂起
//后面finally里的逻辑就不分析了,
SingleThreadEventExecutor.this.run();
success = true;
} catch (Throwable t) {
logger.warn("Unexpected exception from an event executor: ", t);
}
}
}
}
});
}
SingleThreadEventExecutor.this.run();
protected void run() {
int selectCnt = 0;
//一个无限循环
for (;;) {
try {
int strategy;
try {
//还记得我们创建NioEventLoop对象时候默认的DefaultSelectStrategy对象吗,
//这里我们就可以知道这个选择策略到底是什么作用
strategy = selectStrategy.calculateStrategy(selectNowSupplier, hasTasks());
|
V
-------------------------------------------------------------------
public int calculateStrategy(IntSupplier selectSupplier, boolean hasTasks) throws Exception {
//hasTasks根据前面我们是真的有个任务放在队列里面的因此为True
//selectSupplier.get() 是调用seletor选择器的selectNow方法来判断有没有通道准备好
//没有就立即返回0这里应该是0这回我们都没有注册感兴趣的事件
return hasTasks ? selectSupplier.get() : SelectStrategy.SELECT;
}
-------------------------------------------------------------------------
//strategy=0
// 表示应该调用select()方法进行阻塞直到有事件发生
// int SELECT = -1;
/**
* 表示应该重新执行循环
*/
// int CONTINUE = -2;
/**
* 指示IO循环以轮询新事件而不会阻塞
*/
// int BUSY_WAIT = -3;
//这里我们等于0因此走defalut
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:
long curDeadlineNanos = nextScheduledTaskDeadlineNanos();
if (curDeadlineNanos == -1L) {
curDeadlineNanos = NONE; // nothing on the calendar
}
nextWakeupNanos.set(curDeadlineNanos);
try {
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;
}
//selectCnt加一
selectCnt++;
cancelledKeys = 0;
needsToSelectAgain = false;
//这个ioRatio作为比率的含义是线程是先执行io操作和还是先执行队列里的任务比例
//默认ioRatio=50
final int ioRatio = this.ioRatio;
boolean ranTasks;
if (ioRatio == 100) {
try {
if (strategy > 0) {
processSelectedKeys();
}
} finally {
// Ensure we always run tasks.
ranTasks = runAllTasks();
}
} else if (strategy > 0) {
final long ioStartTime = System.nanoTime();
try {
processSelectedKeys();
} finally {
// Ensure we always run tasks.
final long ioTime = System.nanoTime() - ioStartTime;
ranTasks = runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
}
} else {
//由于开始的时候strategy=0运行任务
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 (Throwable t) {
handleLoopException(t);
}
// Always handle shutdown even if the loop processing threw an exception.
try {
if (isShuttingDown()) {
closeAll();
if (confirmShutdown()) {
return;
}
}
} catch (Throwable t) {
handleLoopException(t);
}
}
}
ranTasks = runAllTasks(0); // This will run the minimum number of tasks
//timeoutNanos设置一个超时事件,超过这个事件就停止运行队列里的任务,返回让线程去干别的事情
protected boolean runAllTasks(long timeoutNanos) {
//获取定时任务根据我们的流程我们是没有设置的因此直接返回
fetchFromScheduledTaskQueue();
//获取一个任务这里我们是有一个任务在队列中的
Runnable task = pollTask();
if (task == null) {
afterRunningAllTasks();
return false;
}
//deadline =0
final long deadline = timeoutNanos > 0 ? ScheduledFutureTask.nanoTime() + timeoutNanos : 0;
long runTasks = 0;
long lastExecutionTime;
for (;;) {
//这里我们来详细分析
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 = pollTask();
if (task == null) {
lastExecutionTime = ScheduledFutureTask.nanoTime();
break;
}
}
afterRunningAllTasks();
this.lastExecutionTime = lastExecutionTime;
return true;
}
//运行队列里的任务
safeExecute(task);
|
V
protected static void safeExecute(Runnable task) {
try {
//这个task run运行的就是我们下图中的代码。还记得我们提交的任务吗
task.run();
} catch (Throwable t) {
logger.warn("A task raised an exception. Task: {}", task, t);
}
}
扰了这么远又回到了register()方法,最关键的register0(promise)方法将开始将selector和channel结合并回调一系列的方法。
private void register0(ChannelPromise promise) {
try {
// 检测我们的channel是否保持打开状态,如果关闭了直接返回
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
//是否是第一次注册
boolean firstRegistration = neverRegistered;
//开始注册这里详细分析
doRegister();
neverRegistered = false;
registered = true;
//回调ChannelHandle handlerAdded方法就是在这里回调的
//这里我们也详细分析下
pipeline.invokeHandlerAddedIfNeeded();
//设置一下表示成功将channel注册到NioEventLoop
safeSetSuccess(promise);
//回调管道中的ChannelHandle 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()) {
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 {
//前面已经将channel设置了非阻塞模式,这里获取selector并且关联channel并注册0
//这里为什么不是注册的16=OP_ACCEPT,创建完应该立马监听客户端的连接建立事件啊
//不用着急后面肯定有哪个地方会修改的
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;
}
}
}
}
pipeline.invokeHandlerAddedIfNeeded();
//还记得我们添加ChannelInitializer到pipeline的那段代码嘛
//这里将会触发ChannelInitializer的handlerAdded方法
|
V
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
if (ctx.channel().isRegistered()) {
// 这里回调重写的initChannel方法
if (initChannel(ctx)) {
// We are done with init the Channel, removing the initializer now.
removeState(ctx);
}
}
} |
V
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) {
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = config.handler();
if (handler != null) {
//这在ChannelInitializer后面插入了一个logginhandle
pipeline.addLast(handler);
}
//将ServerBootstrapAcceptor添加到pipeline中,线程会从任务队列中获取这个任务来执行
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}
//上面的逻辑都是 initAndRegister()中的,到这里我们已经完成了channel的初始化和注册
//现在开始绑定端口
final ChannelFuture regFuture = initAndRegister();
private ChannelFuture doBind(final SocketAddress localAddress) {
//都是异步的操作
final ChannelFuture regFuture = initAndRegister();
//异步获取channel
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
if (regFuture.isDone()) {
// 到这里我们的注册已经成功完成
ChannelPromise promise = channel.newPromise();
//开始绑定一个端口给server这里我们仔细分析
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);
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;
}
}
doBind0(regFuture, channel, localAddress, promise);
private static void doBind0(
final ChannelFuture regFuture, final Channel channel,
final SocketAddress localAddress, final ChannelPromise promise) {
//提交任务给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());
}
}
});
}
@Override
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
return pipeline.bind(localAddress, promise);
}
|
V
public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
return tail.bind(localAddress, promise);
}
|
V
public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
ObjectUtil.checkNotNull(localAddress, "localAddress");
if (isNotValidPromise(promise, false)) {
// cancelled
return promise;
}
//通过前面我们真的bind的out操作,因此他会从tail开始往前找Outbound类型的handel并且
//调用他们的bind方法到这里你还记得pipie里的双向链表有哪些channelHandle吗
//最后肯定是达到head头部节点,即使headcontext的bind方法
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;
}
headcontext的bind方法
public void bind(
ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) {
unsafe.bind(localAddress, promise);
}
|
V
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
if (Boolean.TRUE.equals(config().getOption(ChannelOption.SO_BROADCAST)) &&
localAddress instanceof InetSocketAddress &&
!((InetSocketAddress) localAddress).getAddress().isAnyLocalAddress() &&
!PlatformDependent.isWindows() && !PlatformDependent.maybeSuperUser()) {
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 {
//和java nio一样进行利用java nio的ServerSocketChannel绑定一个端点
doBind(localAddress);
|
V
//protected void doBind(SocketAddress localAddress) throws Exception {
// if (PlatformDependent.javaVersion() >= 7) {
// javaChannel().bind(localAddress, config.getBacklog());
// } else {
// javaChannel().socket().bind(localAddress, config.getBacklog());
// }
// }
} catch (Throwable t) {
safeSetFailure(promise, t);
closeIfClosed();
return;
}
//重点这里还记得我们没有设置任何监听io事件吗
//这里就是设置为16表示监听客户端accept事件
if (!wasActive && isActive()) {
invokeLater(new Runnable() {
@Override
public void run() {
//遍历链表中所有Handle节点调用他们的ChannelActive();
pipeline.fireChannelActive();
}
});
}
safeSetSuccess(promise);
}
pipeline.fireChannelActive();
|
public final ChannelPipeline fireChannelActive() {
//头节点传入先调用头部
AbstractChannelHandlerContext.invokeChannelActive(head);
return this;
}
//HeadContex的ChaanelAction方法
@Override
public void channelActive(ChannelHandlerContext ctx) {
//channelActive是inbound事件
ctx.fireChannelActive();
//重点这里
//是否开启了自动读
readIfIsAutoRead();
|
V
private void readIfIsAutoRead() {
if (channel.config().isAutoRead()) {
channel.read();
|
V
public Channel read() {
//触发read事件属于inbound
pipeline.read();
return this;
}
}
}
}
//老样子直接查看HeadContext的read方法
pipeline.read();
|
V
protected void doBeginRead() throws Exception {
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
}
readPending = true;
final int interestOps = selectionKey.interestOps();
//这里设置成16 public static final int OP_ACCEPT = 1 << 4;
//即注册accept事件
//interestOps =0 readInterestOp这个值是我们创建Channel时候设置的16
if ((interestOps & readInterestOp) == 0) {
selectionKey.interestOps(interestOps | readInterestOp);
}
}
//到这里你可能会存在一个疑问,我们创建的workerGroup没用到啊。是的确实没用到
//那在哪里开始使用呢
//还记得前面这段初始化的代码吗
void init(Channel channel) {
//这里我们省略了无关代码
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
//链表里面添加了ServerBootstrapAcceptor
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
//我们再来看下ServerBootstrapAcceptor的ChannelRead方法
//这里就是关键不过这个这个方法的调用时机是在客户端发送了数据过来
//这里你可以打个断点并观察调用栈就能看出
public void channelRead(ChannelHandlerContext ctx, Object msg) {
//这个channel是我们客户端的套接字即NioSockertChannel
final Channel child = (Channel) msg;
//添加我们的childHandler也就是我们开始自己创建的EchoServerHandle
child.pipeline().addLast(childHandler);
setChannelOptions(child, childOptions, logger);
setAttributes(child, childAttrs);
try {
//重点这里使用我们的childGroup线程池来注册关联客户端套接字NioSockertChannel
//我们开始的时候创建了两个线程池一个bossGroup一个workerGroup
//前面逻辑都是用的bossGroup的线程池里的线程来处理监听NioSockertServerChannel
//这个workerGroup就是用来处理NioSockertChannel通过相关的Handle逻辑
childGroup.register(child).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
forceClose(child, future.cause());
}
}
});
} catch (Throwable t) {
forceClose(child, t);
}
}
netty的分析就到这里结束了,其实只分析了大概流程。里面其实还有很多细节问题的。主要是用于学习的目的,可能分析的很乱但确实要花费一些时间的。如有描述有错误的地方,可以留言指出。
到这里其实还有一些问题没有解决,如果我们关闭了AutoRead和OutBound的read方法有什么关联。反压机制到底怎么用才不会出现问题呢。笔者目前没有有个清晰的思路。如果有相关案例就好了