客户端
基本流程:
- 创建BootStrap对象;
- 通过group方法设置线程池;
- 通过channel方法设置IO通道类型;
- 通过handler方法设置、添加handler。
- connect连接
源码的分析也从以上几个步骤为切入点。
BootStrap
顾名思义,一个启动类,用于客户端建立连接、启动通道。
继承了AbstractBootStrap抽象类,AbstractBootStrap有两个实现,BootStrap和ServerBootStrap,分别用于客户端和服务端,包含一些build方法,用于设置一系列属性。同时AbstractBootStrap提供了bind相关的方法,用于服务端绑定socket,客户端可用于建立无连接的udp传输。
BootStrap,提供了connect等连接相关的方法,用于客户端建立连接。
NioEventLoopGroup
使用启动类的group建造者方法设置为AbstractBootstrap中的属性。
实现了ScheduledExecutorService等定时执行器相关的接口;同时实现了Iterable接口。可以看作是一个线程池,创建时如果不设置线程数,默认使用系统属性设置/CPU核数*2;
同时,在创建NioEventLoop时,初始化并优化创建了Selector,用于后面注册事件使用。
// NioEventLoopGroup
DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
"io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
// MultithreadEventExecutorGroup
children = new EventExecutor[nThreads];
children[i] = newChild(executor, args);
// NioEventLoopGroup
return new NioEventLoop(this, executor, (SelectorProvider) args[0],
((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2], queueFactory);
// NioEventLoop::openSelector()
unwrappedSelector = provider.openSelector();
// ...优化封装了java的SelectorImpl
selectedKeysField.set(unwrappedSelector, selectedKeySet);
publicSelectedKeysField.set(unwrappedSelector, selectedKeySet);
提供了next()方法按照调用顺序轮询选择一个EventLoop/EventExecutor返回。
NioSocketChannel
Bootstrap对象通过channel方法传入Class<? extends Channel>对象,netty提供了许多Channel实现类,如客户端/服务端、TCP/UDP/SCTP、阻塞/非阻塞等不同适用的实现类。
AbstractBootStrap拿到Class对象后,默认创建一个根据类型反射创建Channel的工厂,赋值给channelFactory属性。
return channelFactory(new ReflectiveChannelFactory<C>(ObjectUtil.checkNotNull(channelClass, "channelClass")));
ChannelHandler
通道处理器,用于处理Socket的读写数据、业务逻辑等。
Bootstrap对象通过handler方法传入通道处理器设置AbstractBootstrap的handler属性。
启动连接
connect方法经过参数校验、Socket地址创建等流程都会使用确定的remoteAddress,调用doResolveAndConnect方法,其核心的两句代码:
final ChannelFuture regFuture = initAndRegister();
return doResolveAndConnect0(channel, remoteAddress, localAddress, channel.newPromise());
initAndRegister方法的核心如下,先利用上面提到的channelFactory创建了通道,在利用上面提到的group调用了注册通道的方法。
channel = channelFactory.newChannel();
init(channel);
ChannelFuture regFuture = config().group().register(channel);
创建通道即利用反射调用了NioSocketChannel的空构造器,主要做了以下事情:
SelectorProvider到SocketChannel
// NioSocketChannel
private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider(); // 根据不同系统不同
public NioSocketChannel() {
this(DEFAULT_SELECTOR_PROVIDER);
}
// 创建java NIO中的 SocketChannel
return provider.openSocketChannel();
// AbstractNioChannel
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent);
this.ch = ch;
this.readInterestOp = readInterestOp;
ch.configureBlocking(false);
}
// AbstractChannel
this.parent = parent;
id = newId();
unsafe = newUnsafe();
pipeline = newChannelPipeline();
关于SelectorProvider.provider()的获取,首先尝试从系统属性设置的实现类、ServiceLoader加载获取(系统类加载器能够加载的类/spi扩展),如果获取不到则使用DefaultSelectorProvider进行创建,Linux下与Mac下创建的SelectorProvider实例并不一致:
// DefaultSelectorProvider,Linux下
public static SelectorProvider create() {
String var0 = (String)AccessController.doPrivileged(new GetPropertyAction("os.name"));
if (var0.equals("SunOS")) {
return createProvider("sun.nio.ch.DevPollSelectorProvider");
} else {
return (SelectorProvider)(var0.equals("Linux") ? createProvider("sun.nio.ch.EPollSelectorProvider") : new PollSelectorProvider());
}
}
// Mac下
public static SelectorProvider create() {
return new KQueueSelectorProvider();
}
SelectorProvider提供了创建Selector的方法,对应不同平台创建EPollSelectorImpl、PollSelectorImpl、KQueueSelectorImpl等。抽象类SelectorImpl抽象出select、selectedKeys等方法方便应用调用。
TODO,如何调用底层的等分析,使用hotspot源码未找到makePipe等调用
通过SelectorProvider创建SocketChannel后,继续实例化NioSocketChannel,可以看到主要将SocketChannel赋值给ch属性,创建了Unsafe对象,创建了通道的pipeline。
ChannelPipeline的创建
protected DefaultChannelPipeline(Channel channel) {
succeededFuture = new SucceededChannelFuture(channel, null);
voidPromise = new VoidChannelPromise(channel, true);
tail = new TailContext(this);
head = new HeadContext(this);
head.next = tail;
tail.prev = head;
}
双向链表,其中头节点、尾节点已创建完成,TailContext实现了ChannelInboundHandler,未处理的数据会打一条debug日志;HeadContext实现了ChannelInboundHandler、ChannelOutboundHandler,使用上面提到的channel的unsafe进行bind、connect、write、read等操作。
同时在Channel实例化完成后,会调用init(Channel)方法,Bootstrap拿到channel后执行handler的添加操作和channel的属性设置;handler会被包装成一个AbstractChannelHandlerContext后加入到pipeline双向链表的tail的前一个节点,其中AbstractChannelHandlerContext主要用来做一些回调事件的触发操作。
channel.pipeline().addLast(config.handler());
Channel的注册
回到上面连接,initAndRegister的init部分主要创建并初始化了channel,register主要是用使用EventLoopGroup使用channel做为参数调用register方法。
// MultithreadEventLoopGroup
return next().register(channel);
// SingleThreadEventLoop
return register(new DefaultChannelPromise(channel, this));
promise.channel().unsafe().register(this, promise);
// Unsafe 异步 + 一些回调事件
eventLoop.execute(()->register0(promise));
// AbstractNioChannel doRegister
selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this); // todo 0 是什么意思
register将从EventLoopGroup中选择一个EventLoop调用register方法,将channel参数传入,EventLoop将自己做为Executor构建ChannelPromise,最终调用Unsafe的register方法。
Unsafe内部类对象处理使用eventLoop中的线程执行注册操作(此处实现了调用线程异步,通过ChannelPromise通信),然后负责做参数校验并处理pipeline的一系列钩子事件,在其中调用AbstractChannel的doREgister方法。doRegister方法在AbstractNioChannel中,可以看到使用java Nio的方式将该channel对应的java SocketChannel注册到了EventLoop的selector上。
猜想上述将register方法教给EventLoop线程执行的原因是,NioEventLoop在execute方法中开启了线程任务的执行,这里注册并开启了线程的轮询。
private void execute(Runnable task, boolean immediate) {
addTask(task);
startThread();
}
Handler的添加
Unsafe执行注册channel时,在调用完成doRegister后,会调用
pipeline.invokeHandlerAddedIfNeeded();
// 最终会调用pipeline链表中的每个context的每个handlerAdded方法
handler().handlerAdded(this);
经常用到的ChannelInitializer便在调用handlerAdded时执行了initChannel(ctx)方法,执行了我们在外面写的一些列pipeline.addLast方法,然后将自己移除。
initChannel((C) ctx.channel());
pipeline.remove(this);
执行连接
initAndRegister后,doResolveAndConnect0方法中调用了doConnect方法
// Bootstrap
doConnect(remoteAddress, localAddress, promise);
// 同样交给通道的eventLoop执行连接,异步操作
channel.eventLoop().execute();
// channel -> pipeline -> handler
channel.connect(remoteAddress, localAddress, connectPromise);
return pipeline.connect(remoteAddress, localAddress, promise);
return tail.connect(remoteAddress, localAddress, promise);
// AbstractChannelHandlerContext,从尾部找到能够找到处理connect的第一个ChannelOutboundHandler执行connect
final AbstractChannelHandlerContext next = findContextOutbound(MASK_CONNECT);
next.invokeConnect(remoteAddress, localAddress, promise);
// 最终找到pipeline双向链表的头节点HeadContext,在其中使用unsafe执行
unsafe.connect(remoteAddress, localAddress, promise);
// unsafe最终调用了主类 NioSocketChannel 的doConnect方法,开始调用java的SocketChannel的方法进行绑定和连接
SocketUtils.bind(javaChannel(), localAddress);
boolean connected = SocketUtils.connect(javaChannel(), remoteAddress);
服务端
服务端启动的流程与客户端相似:
- 对应启动类为ServerBootstrap,同样继承了AbstractBoostrap;
- group需要使用两个,创建两个NioEventLoopGroup分别给bossGroup、workerGroup
- 使用的Channel类型为NioServerSocketChannel,可以类比客户端,其创建、初始化、注册过程结合了java中的ServerSocketChannel,对应pipeline
- 每个客户端连接时都会产生一个SocketChannel,对应另一个pipeline,区别ServerSocketChannel,使用ServerBootstrap的childHander方法添加handler
SocketChannel的产生、注册
服务端启动的过程与客户端不一样的一个地方,客户端NioSocketChannel创建时直接用SelectorProvider.openSocketChannel,而服务端对应NioServerSocketChannel创建时使用的是provider.openServerSocketChannel();得到ServerSocketChannel,只能在建立连接时才会accept产生SocketChannel。
// NioServerSocketChannel.newSocket
return provider.openServerSocketChannel();
NioServerSocketChannel相关事件最终会使用其内部类-Unsafe的子类NioMessageUnsafe进行read操作,最终调用的是NioServerSocketChannel的doReadMessages方法
// NioServerSocketChannel::doReadMessages
SocketChannel ch = SocketUtils.accept(javaChannel());
buf.add(new NioSocketChannel(this, ch));
可以看到,这里仍然是java NIO的方式产生了SocketChannel,并创建为NioSocketChannel添加到了事件对象列表中,NioMessageUnsafe在读取完成后,会触发读取完成的事件
pipeline.fireChannelReadComplete();
即将数据交给了NioServerSocketChannel的handler。
childHandler添加
初始化channel和pipeline时,ServerBootstrap实现了AbstractBoostrap的一个抽象方法init(Channel channel),在其中向NioServerSocketChannel的pipeline添加了一个handler ServerBootstrapAcceptor
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
ServerBootstrapAcceptor是ServerBootstrap的一个内部类,是一个ChannelInboundHandlerAdapter,在NioServerSocketChannel有相关读数据事件时会出发相关回调,NioSocketChannel将会被unsafe读取完成传递到这里。
public void channelRead(ChannelHandlerContext ctx, Object msg) {
final Channel child = (Channel) msg;
child.pipeline().addLast(childHandler);
setChannelOptions(child, childOptions, logger);
setAttributes(child, childAttrs);
childGroup.register(child);
}
这便是熟悉的操作,添加childHandler,注册到childGroup的EventLoop上。