netty echo服务端源码分析(一) 和netty echo服务端源码分析(二)分析完后,我们目前只构造了bossGroup和workGroup两个对象,同时把一些配置信息保存到了启动辅助类ServerBootStrap中,现在看看ServerBootStrap如何启动服务端。对用户来讲,启动服务端很简单,只要执行例子中的这条语句
// Start the server.
ChannelFuture f = b.bind(PORT).sync();
跟下去,最终会调用类AbstractBootstrap
的doBind(final SocketAddress localAddress)
函数,这个函数我们主要关心调用的下面两个函数:
// 创建NioServerSocketChannel实例,初始化,注册ServerSocketChannel到event loop的selector上
final ChannelFuture regFuture = initAndRegister();
// NioServerSocketChannel已经注册到 NioEventloop的selector上了
ChannelPromise promise = channel.newPromise();
// 为Server socket 绑定ip和端口号
doBind0(regFuture, channel, localAddress, promise);
先分析函数initAndRegister()
,代码如下:
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
// 对于server, 通过反射返回NioServerSocketChannel的实例
// 相当于channel = new NioServerSocketChannel()
channel = channelFactory.newChannel();
// ServerBootstrap.init
init(channel);
} catch (Throwable t) {
// 无关代码省略
}
// config().group()返回的是ServerBootstrap的parent group, 即 bossGroup
ChannelFuture regFuture = config().group().register(channel);
if (regFuture.cause() != null) {
// 无关代码省略
}
return regFuture;
}
根据netty echo服务端源码分析(二)的分析,channelFactory是类ReflectiveChannelFactory
的实例,channelFactory.newChannel()
会通过反射的方式创建NioServerSocketChannel
的实例。
因为NioServerSocketChannel
是服务端的核心类,我们先简单分析一下这个类。
先看看它的继承体系
// NioServerSocketChannel构造函数
public NioServerSocketChannel() {
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
public NioServerSocketChannel(ServerSocketChannel channel) {
super(null, channel, SelectionKey.OP_ACCEPT);
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;
try {
ch.configureBlocking(false);
} catch (IOException e) {
try {
ch.close();
} 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);
}
}
// AbstractChannel构造函数
protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
分析上面的代码可知,newSocket(DEFAULT_SELECTOR_PROVIDER)
通过SelectorProvider.provider().openServerSocketChannel()
打开一个ServerSocketChannel,在linux下,我们可以理解为JVM会调用linux操作系统的socket()函数创建了一个socket。这个地方要注意区别它跟前面NioServerSocketChannel
实例的关系,他们都是一个socket channel,但是是两个不同的东西。newSocket(DEFAULT_SELECTOR_PROVIDER)
返回的channel更多的是与操作系统socket相关联的一个东西,后面代码中经常遇到的通过javaChannel()
返回的channel就是它,所以我们要进行底层的socket操作时,就是通过它。
然后设置channel需要监听的事件为OP_ACCEPT,也就是接受client的连接请求。将channel配置为非阻塞。最后在父类AbstractChannel
构造函数中初始化了几个重要的属性域。ChannelId id
初始化为DefaultChannelId
的实例,用来唯一的标识一个channel,具体表达方式为:machineid+processid+sequence+timestamp+random。Unsafe = new NioMessageUnsafe()
也是netty框架非常重要的一个。unsafe是netty又一个非常重要的对象,当我们要操作JVM的一些函数或功能时,就是通过unsafe来调用的。newUnsafe()
函数的实现为:
protected AbstractNioUnsafe newUnsafe() {
return new NioMessageUnsafe();
}
pipeline是netty框架的非常核心部分。netty的业务处理模型就是通过pipeline定义和实现。pipeline是一个有channel handler组成的业务处理链表,用户可以根据需要在线动态的添加业务处理、删除业务处理、重新调整业务处理顺序,从而灵活高效的实现不同的业务逻辑。newChannelPipeline()
函数的实现为:
protected DefaultChannelPipeline newChannelPipeline() {
return new DefaultChannelPipeline(this);
}
到此,netty服务端的三大核心组件eventloopgroup/eventloop、channel、pipeline/channelhandler都出现了,为了下面代码分析的方便,把他们之间的关系说明一下:
eventloopgroup包含一组eventloop,eventloop只属于一个eventloopgroup,channel注册到一个eventloop,它的所有事件和业务处理都在注册到的eventloop中进行,由于channel的所有业务处理都在一个线程中处理,不需要考虑多线程同步的问题,极大的简化了编程。每一个channel都有一个自己pipeline,pipeline是一个channelhandler链表。
initAndRegister()
函数的channel创建分析完了,我们继续往下分析初始化channel的init(channel)
这个函数调用。
init(Channel channel)
函数在ServerBootstrap
类中实现,我们重点分析下为channel的pipeline添加channelHandler的功能,代码如下:
// 返回的是DefaultChannelPipeline的实例
ChannelPipeline p = channel.pipeline();
// 为 ServerSocketChannel 的pipeline添加handler
p.addLast(new ChannelInitializer() {
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
// 通过ServerBootstarp配置的handler添加到pipeline
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
// 将handler ServerBootstrapAcceptor添加到pipeline
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
p.addLast()
最终会调用DefaultChannelPipeline
中定义的这个函数:
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
final AbstractChannelHandlerContext newCtx;
synchronized (this) {
// 检查handler是否支持添加到多个pipeline
checkMultiplicity(handler);
newCtx = newContext(group, filterName(name, handler), handler);
// 将handler添加到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.
// channel还没有注册到selector,将状态设为pending,并放入pending队列
if (!registered) {
newCtx.setAddPending();
callHandlerCallbackLater(newCtx, true);
return this;
}
// 已注册,但不在eventloop线程中,将状态设为pending,并放入eventloop的task队列
EventExecutor executor = newCtx.executor();
if (!executor.inEventLoop()) {
newCtx.setAddPending();
executor.execute(new Runnable() {
@Override
public void run() {
callHandlerAdded0(newCtx);
}
});
return this;
}
}
// channel已注册,并且当前线程就是eventloop线程,立即执行
callHandlerAdded0(newCtx);
return this;
}
从函数中得知,先会检查要添加的handler是否支持同时添加到多个pipeline,如果不支持,同时已经添加过,则不允许添加。对于允许shared的handler要求是线程安全的,否则可能会导致程序出错。
然后将创建一个DefaultChannelHandlerContext
实例,将handler封装到handlercontext中,然后将handlercontext添加到pipeline链表的末尾,即tailcontext的前面。
由于当前channel还没有注册,会进入分支
if (!registered) {
newCtx.setAddPending();
callHandlerCallbackLater(newCtx, true);
return this;
}
这个分支先会将handlercontext的状态设为ADD_PENDING
, 然后添加到任务链表pendingHandlerCallbackHead
末尾,待后续处理。
好了,init(channel)
分析完了,继续回到initAndRegister()
函数,继续往下分析channel注册。
// config().group()返回的是ServerBootstrap的parent group, 即 bossGroup
ChannelFuture regFuture = config().group().register(channel);
跟踪config().group()
可知,返回的就是netty echo服务端源码分析(一)中的bossGroup,跟踪它的register(channel)
函数,最后调用到MultithreadEventLoopGroup
类中的register()
,内容如下:
public ChannelFuture register(Channel channel) {
return next().register(channel);
}
next()
函数的功能就是通过netty echo服务端源码分析(一)介绍的chooser使用round-robin算法选择一个eventloop。
在NioEventLoop
中跟踪register(channel)
,调用到SingleThreadEventLoop
的
public ChannelFuture register(final ChannelPromise promise) {
ObjectUtil.checkNotNull(promise, "promise");
promise.channel().unsafe().register(this, promise);
return promise;
}
在NioMessageUnsafe
中跟踪register()
,会发现调用的是AbstractUnsafe
的这个版本:
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;
}
AbstractChannel.this.eventLoop = eventLoop;
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
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的线程,则直接执行register0(promise)
,但知道现在为止,我们的代码一直在main()函数的线程中执行,所以执行的分支是
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
继续跟踪下去调用的是SingleThreadEventExecutor
的这个函数
public void execute(Runnable task) {
if (task == null) {
throw new NullPointerException("task");
}
boolean inEventLoop = inEventLoop();
if (inEventLoop) {
addTask(task);
} else {
startThread();
addTask(task);
if (isShutdown() && removeTask(task)) {
reject();
}
}
if (!addTaskWakesUp && wakesUpForTask(task)) {
wakeup(inEventLoop);
}
}
当前线程不是eventloop的线程,所以进入startThread()
这个分支,我们看看startThread()
的相关代码:
private void startThread() {
if (state == 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();
if (interrupted) {
thread.interrupt();
}
boolean success = false;
updateLastExecutionTime();
try {
SingleThreadEventExecutor.this.run();
success = true;
} catch (Throwable t) {
logger.warn("Unexpected exception from an event executor: ", t);
} finally {
// 省略代码。。。。
}
}
});
}
可见,如果线程状态为ST_NOT_STARTED
,则创建一个新的线程(还记得netty echo服务端源码分析(一)中的ThreadPerTaskExecutor
吗?),并在新创建的线程中执行NioEventLoop
的run()
函数。
eventloop线程创建并启动后,通过调用addTask()
将task任务添加到taskqueue
中。
我们先不管eventloop线程是如何调用taskqueue
中的任务的,看看register0(promise)
的具体逻辑。
private void register0(ChannelPromise promise) {
try {
// check if the channel is still open as it could be closed in the mean time when the register
// call was outside of the eventLoop
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
boolean firstRegistration = neverRegistered;
// 调用的函数 AbstractNioChannel.doRegister()
doRegister();
neverRegistered = false;
registered = true;
// Ensure we call handlerAdded(...) before we actually notify the promise. This is needed as the
// user may already fire events through the pipeline in the ChannelFutureListener.
pipeline.invokeHandlerAddedIfNeeded();
safeSetSuccess(promise);
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);
}
}
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
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;
}
}
}
}
到此,doRegister()
通过javaChannel().register()
函数将channel注册到了selector,只是还没有将监听的OP_ACCEPT事件注册上去。
然后我们分析一下这行代码:
pipeline.invokeHandlerAddedIfNeeded();
我们分析把channel handler添加到pipeline中时,由于channel还没有注册,所以将一个任务添加到了pendingHandlerCallbackHead
链表中,这个函数会将registered
标志设为true,同时在eventloop中执行pendingHandlerCallbackHead
链表中的所有任务:
PendingHandlerCallback task = pendingHandlerCallbackHead;
while (task != null) {
task.execute();
task = task.next;
}
继续往下分析:
safeSetSuccess(promise);
设置channelFuture,表示完成了注册,main()
函数的b.bind(PORT).sync()
调用可以返回了。
继续往下:
pipeline.fireChannelRegistered();
调用链如下代码,可以看代码注释,了解处理过程
// DefaultChannelPipeline
public final ChannelPipeline fireChannelRegistered() {
AbstractChannelHandlerContext.invokeChannelRegistered(head);
return this;
}
// AbstractChannelHandlerContext
static void invokeChannelRegistered(final AbstractChannelHandlerContext next) {
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeChannelRegistered();
} else {
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelRegistered();
}
});
}
}
// HeadContext
private void invokeChannelRegistered() {
if (invokeHandler()) {
try {
// 返回的context的channel handler,对于head来说,因为head本身实现了channelhandler接口,返回的就是head自己
((ChannelInboundHandler) handler()).channelRegistered(this);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
fireChannelRegistered();
}
}
// 调用channel handler的相应函数
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
// 实际干活的函数
invokeHandlerAddedIfNeeded();
// 执行下一个context的相应函数
ctx.fireChannelRegistered();
}
public ChannelHandlerContext fireChannelRegistered() {
invokeChannelRegistered(findContextInbound());
return this;
}
private AbstractChannelHandlerContext findContextInbound() {
AbstractChannelHandlerContext ctx = this;
do {
ctx = ctx.next;
} while (!ctx.inbound);
return ctx;
}
这里可以总结一下pipleline的任务链传递和处理模式(XXX表示register、bind、read、write等具体事件):
pipeline.fireXXX -> headContext.invokeXXX -> channelHandler.XXX -> next Context.fireXXX -> next Context.invokeXXX -> next channelHandler.XXX
initAndRegister()
算是分析完了,到现在为止,经过曲折的调用关系,我们创建了eventloop线程并让它运行,同时将register0任务提交到它的任务队列,但是我们先不分析register0任务是如何执行的,还记得开头的dobind()
函数中除了调用initAndRegister()
还调用了dobind0()
函数吗,我们先来分析这个函数。
private static void doBind0(
final ChannelFuture regFuture, final Channel channel,
final SocketAddress localAddress, final ChannelPromise promise) {
// This method is invoked before channelRegistered() is triggered. Give user handlers a chance to set up
// the pipeline in its channelRegistered() implementation.
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());
}
}
});
}
原来跟register0()
任务一样,是将任务提交到eventloop线程的taskQueue队列,只不过这一次不用创建新的线程了。
下面分析channel的bind()
函数,具体实现代码在类AbstractChannel
中,这个函数的调用链如下,代码注释已经做了说明:
// AbstractChannel
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
return pipeline.bind(localAddress, promise);
}
// DefaultChannelPipeline
public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
// 因为bind是一个outbound事件,从pipeline链尾tailContext开始执行
return tail.bind(localAddress, promise);
}
// tail context的父类AbstractChannelHandlerContext
public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
if (localAddress == null) {
throw new NullPointerException("localAddress");
}
if (isNotValidPromise(promise, false)) {
// cancelled
return promise;
}
// 应用程序没有添加outbound的情况下,找到的next context是head context
final AbstractChannelHandlerContext next = findContextOutbound();
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;
}
// head context的父类AbstractChannelHandlerContext
private void invokeBind(SocketAddress localAddress, ChannelPromise promise) {
if (invokeHandler()) {
try {
((ChannelOutboundHandler) handler()).bind(this, localAddress, promise);
} catch (Throwable t) {
notifyOutboundHandlerException(t, promise);
}
} else {
bind(localAddress, promise);
}
}
// 还在head context的父类AbstractChannelHandlerContext
public void bind(
ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
throws Exception {
// 通过unsafe调用bind了,意味着会调用JVM的功能,操作底层的一些函数了
unsafe.bind(localAddress, promise);
}
// AbstractUnsafe
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
assertEventLoop();
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
// See: https://github.com/netty/netty/issues/576
if (Boolean.TRUE.equals(config().getOption(ChannelOption.SO_BROADCAST)) &&
localAddress instanceof InetSocketAddress &&
!((InetSocketAddress) localAddress).getAddress().isAnyLocalAddress() &&
!PlatformDependent.isWindows() && !PlatformDependent.maybeSuperUser()) {
// Warn a user about the fact that a non-root user can't receive a
// broadcast packet on *nix if the socket is bound on non-wildcard address.
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 {
// 做实际的bind工作
doBind(localAddress);
} catch (Throwable t) {
safeSetFailure(promise, t);
closeIfClosed();
return;
}
if (!wasActive && isActive()) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireChannelActive();
}
});
}
safeSetSuccess(promise);
}
// NioServerSocketChannel
protected void doBind(SocketAddress localAddress) throws Exception {
if (PlatformDependent.javaVersion() >= 7) {
// 最终通过JVM调用server socket的bind、listen等函数,启动服务端
javaChannel().bind(localAddress, config.getBacklog());
} else {
javaChannel().socket().bind(localAddress, config.getBacklog());
}
}