文章目录
1. 概述
使用2019.11.6日4.1版本最新源码。
源码下载:
链接:https://pan.baidu.com/s/19R-rtyT0ffyv3NAOrZKJ5Q 密码:5xhp
为了方便理解,文中代码做了简化。
2. 服务端创建
代码示例来自netty源码io.netty.example.echo包中EchoServer类
EventLoopGroup bossGroup = new MultithreadEventLoopGroup(1, NioHandler.newFactory());
EventLoopGroup workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory());
final EchoServerHandler serverHandler = new EchoServerHandler();
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();
if (sslCtx != null) {
p.addLast(sslCtx.newHandler(ch.alloc()));
}
p.addLast(serverHandler);
}
});
ChannelFuture f = b.bind(PORT).sync();
f.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
2.1 创建EventLoopGroup
我们直接看workerGroup的创建,因为bossGroup和workerGroup区别就在于,线程数量不同。在构造函数中,我们发现,调用了pickThreadCount(nThreads)这个方法。
protected MultithreadEventLoopGroup(int nThreads, Executor executor,
IoHandlerFactory ioHandlerFactory,
int maxPendingTasks, RejectedExecutionHandler rejectedHandler,
int maxTasksPerRun, Object... args) {
super(
pickThreadCount(nThreads),
executor == null ? new ThreadPerTaskExecutor(newDefaultThreadFactory()) : executor,
maxPendingTasks, rejectedHandler, merge(ioHandlerFactory, maxTasksPerRun, args));
}
2.1.1 选择线程数量
pickThreadCount()的函数定义如下。它是MultithreadEventLoopGroup的方法。worker EventLoopGroup传入了初始化线程数量,nThreads为传入的值,boss EventLoopGroup调用时默认传入0,那么nThreads的值为DEFAULT_EVENT_LOOP_THREADS常量的值。
protected static int pickThreadCount(int nThreads) {
// 如果nThreads是0,就更改为DEFAULT_EVENT_LOOP_THREADS常量值
return nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads;
}
我们发现MultithreadEventLoopGroup的静态代码块中初始化了常量DEFAULT_EVENT_LOOP_THREADS,最小是1,否则设置为当前电脑可用核心数的2倍。
static {
DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
"io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
if (logger.isDebugEnabled()) {
logger.debug("-Dio.netty.eventLoopThreads: {}", DEFAULT_EVENT_LOOP_THREADS);
}
}
此时调动栈如下。
2.1.2 创建selector
往后面看,最后实现创建的构造函数如下
protected MultithreadEventExecutorGroup(
int nThreads, Executor executor,
int maxPendingTasks, RejectedExecutionHandler rejectedHandler, Object... args) {
// nThreads 为刚才提到的线程数量,此处使用一个数组保存EventLoop
children = new EventExecutor[nThreads];
powerOfTwo = isPowerOfTwo(children.length);
for (int i = 0; i < nThreads; i ++) {
boolean success = false;
try {
children[i] = newChild(executor, maxPendingTasks, rejectedHandler, args);
success = true;
} catch (Exception e) {
throw new IllegalStateException("failed to create a child event executor", e);
} finally {
// 省略...
}
}
}
在newChild函数中,创建了NioHandler
@Override
protected final EventLoop newChild(
Executor executor, int maxPendingTasks,
RejectedExecutionHandler rejectedExecutionHandler, Object... args) {
return newChild(executor, maxPendingTasks, rejectedExecutionHandler,
((IoHandlerFactory) args[0]).newHandler()/*创建NioHandler*/, (Integer) args[1],
Arrays.copyOfRange(args, 2, args.length));
}
在NioHandler的构造函数中,创建了selector
private NioHandler(SelectorProvider selectorProvider, SelectStrategy strategy) {
provider = selectorProvider;
// 创建selector
final SelectorTuple selectorTuple = openSelector();
selector = selectorTuple.selector;
unwrappedSelector = selectorTuple.unwrappedSelector;
selectStrategy = strategy;
}
至此,函数调用栈如下:
总结一下,创建MultithreadEventLoopGroup有两件核心的事情。第一是,如果参数传入了线程数,就用传入的值,如果没有,就会使用电脑可用核心数的2倍作为线程数。第二个,selector是在MultithreadEventLoopGroup创建的时候创建的。
2.2 创建ServerBootstrap
创建ServerBootstrap,使用Builder模式。
首先看group方法。将两个EventLoopGroup分别保存。
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
// AbstractBootstrap类
super.group(parentGroup);
requireNonNull(childGroup, "childGroup");
if (this.childGroup != null) {
throw new IllegalStateException("childGroup set already");
}
this.childGroup = childGroup;
return this;
}
下面是channel方法。使用反射创建ServerChannel。
public ServerBootstrap channel(Class<? extends ServerChannel> channelClass) {
requireNonNull(channelClass, "channelClass");
return channelFactory(new ReflectiveServerChannelFactory<ServerChannel>(channelClass));
}
public ReflectiveServerChannelFactory(Class<? extends T> clazz) {
requireNonNull(clazz, "clazz");
try {
// 创建constructor
this.constructor = clazz.getConstructor(EventLoop.class, EventLoopGroup.class);
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException("Class " + StringUtil.simpleClassName(clazz) +
" does not have a public constructor that takes an EventLoop and EventLoopGroup instance", e);
}
}
@Override
public T newChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup) {
try {
// 生成实例
return constructor.newInstance(eventLoop, childEventLoopGroup);
} catch (Throwable t) {
throw new ChannelException(
"Unable to create ServerChannel from class " + constructor.getDeclaringClass(), t);
}
}
添加option,添加handler,添加childrenHandler。
public <T> B option(ChannelOption<T> option, T value) {
requireNonNull(option, "option");
if (value == null) {
options.remove(option);
} else {
options.put(option, value);
}
return self();
}
public B handler(ChannelHandler handler) {
requireNonNull(handler, "handler");
this.handler = handler;
return self();
}
public ServerBootstrap childHandler(ChannelHandler childHandler) {
requireNonNull(childHandler, "childHandler");
this.childHandler = childHandler;
return this;
}
2.3 绑定方法
之后执行这一句:
ChannelFuture f = b.bind(PORT).sync();
bind函数实际执行的是doBind函数,如下。
private ChannelFuture doBind(final SocketAddress localAddress) {
// 1. 首先创建并初始化channel
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
if (regFuture.isDone()) {
ChannelPromise promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise);
return promise;
} else {
final ChannelPromise promise = channel.newPromise();
regFuture.addListener((ChannelFutureListener) future -> {
Throwable cause = future.cause();
if (cause != null) {
promise.setFailure(cause);
} else {
// 注意这里,后面boss EventLoop线程会执行到这里。
doBind0(regFuture, channel, localAddress, promise);
}
});
return promise;
}
}
2.3.1 创建channel
我们先看一下initAndRegister函数,创建channel都有哪些内容。首先获取boss EventLoop线程,然后使用反射创建channel,最后使用boss EventLoop线程初始化channel。
final ChannelFuture initAndRegister() {
// loop是boss EventLoop
EventLoop loop = group.next();
final Channel channel;
try {
// 创建channel,这里使用了反射
channel = newChannel(loop);
} catch (Throwable t) {
return new FailedChannel(loop).newFailedFuture(t);
}
final ChannelPromise promise = channel.newPromise();
// 这里初始化channel,boss EventLoop线程来执行,会添加Handler到pipline中
loop.execute(() -> init(channel).addListener((ChannelFutureListener) future -> {
if (future.isSuccess()) {
channel.register(promise);
} else {
channel.unsafe().closeForcibly();
promise.setFailure(future.cause());
}
}));
return promise;
}
到此为止,我们已经完成了channel的创建,并且,让boss EventLoop线程完成了channel的初始化,main线程返回当前函数,返回regFuture,由于regFuture的任务并没有完成,所以,main函数注册了回调方法,执行doBind0函数,此时main函数返回 ChannelFuture f = b.bind(PORT).sync();执行sync,main线程就阻塞等待了。
2.3.2 初始化channel
在我们把目光转移到boss EventLoop (后面简称boss线程),刚才我们说到boss线程是在AbstractBootsrap类的initAndRegister方法中开启的。首先要完成channel的初始化工作。
loop.execute(() -> init(channel).addListener((ChannelFutureListener) future -> {...}));
调用下面的初始化方法。
ChannelFuture init(Channel channel) {
final ChannelPromise promise = channel.newPromise();
setChannelOptions(channel, options0().entrySet().toArray(newOptionArray(0)), logger);
setAttributes(channel, attrs0().entrySet().toArray(newAttrArray(0)));
ChannelPipeline p = channel.pipeline();
final ChannelHandler currentChildHandler = childHandler;
final Entry<ChannelOption<?>, Object>[] currentChildOptions =
childOptions.entrySet().toArray(newOptionArray(0));
final Entry<AttributeKey<?>, Object>[] currentChildAttrs =
childAttrs.entrySet().toArray(newAttrArray(0));
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) {
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
ch.eventLoop().execute(() -> {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildHandler, currentChildOptions, currentChildAttrs));
promise.setSuccess();
});
}
});
return promise;
}
我们看ChannelPipline的addLast方法,参数是可变参数的ChannelHandler
函数签名 : public final ChannelPipeline addLast(ChannelHandler… handlers)
ChannelInitializer 实现了 ChannelInboundHandler 接口 , ChannelInboundHandler 继承了 ChannelHandler
下面的代码中,调用了另一个addLast方法,虽然方法里面有循环,但是经过查看运行时数据,我们发现,参数handlers只有一个变量,ServerBootstrap类型。
@Override
public final ChannelPipeline addLast(ChannelHandler... handlers) {
requireNonNull(handlers, "handlers");
for (ChannelHandler h: handlers) {
if (h == null) {
break;
}
addLast(null, h);
}
return this;
}
经过下图中的调用栈,最后执行initChannel方法,也就是初始化开始ServerBootstrap类的init方法中,匿名内部类的方法,如下,将handler添加到pipeline中。
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) {
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
ch.eventLoop().execute(() -> {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildHandler, currentChildOptions, currentChildAttrs));
promise.setSuccess();
});
}
});
特别要说的是,在这之前,ServerBootStrap自己也在pipeline中,在调用栈的handlerAdded方法中,完成了其他handler的添加,才将自己删除,代码如下。
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
try {
initChannel((C) ctx.channel());
} catch (Throwable cause) {
exceptionCaught(ctx, cause);
} finally {
ChannelPipeline pipeline = ctx.pipeline();
if (pipeline.context(this) != null) {
// 删除自己
pipeline.remove(this);
}
}
}
删除之前,ServerBootstrap也在pipeline的handlers链中。
移除自己。
2.3.3 注册channel到selector
本以为到这里就结束了,但是我们在唤醒boss线程时,还注册了回调函数。
loop.execute(() -> init(channel).addListener((ChannelFutureListener) future -> {
if (future.isSuccess()) {
// 执行这里
channel.register(promise);
} else {
channel.unsafe().closeForcibly();
promise.setFailure(future.cause());
}
}));
通过下面的调用栈,我们调用到了NioHandler的register方法。我们可以看到,方法中调用了java NIO中的register方法,真正的注册方法。
@Override
public void register(Channel channel) throws Exception {
AbstractNioChannel nioChannel = cast(channel);
boolean selected = false;
for (;;) {
try {
// 真正的注册方法,要注意,此时监听的是0,而不是ACCEPT。
nioChannel.selectionKey = nioChannel.javaChannel().register(unwrappedSelector(), 0, nioChannel);
return;
} catch (CancelledKeyException e) {
// 省略...
}
}
}
2.3.4 真正的绑定方法
main线程还在doBind方法中注册了一个回调函数。
private ChannelFuture doBind(final SocketAddress localAddress) {
// 省略...
if (regFuture.isDone()) {
// ...
} else {
final ChannelPromise promise = channel.newPromise();
// 回调函数
regFuture.addListener((ChannelFutureListener) future -> {
Throwable cause = future.cause();
if (cause != null) {
promise.setFailure(cause);
} else {
// 执行这里
doBind0(regFuture, channel, localAddress, promise);
}
});
return promise;
}
}
在doBind0方法中,执行了channel的bind方法。
private static void doBind0(
final ChannelFuture regFuture, final Channel channel,
final SocketAddress localAddress, final ChannelPromise promise) {
channel.eventLoop().execute(() -> {
if (regFuture.isSuccess()) {
channel.bind(localAddress, promise)
.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
} else {
promise.setFailure(regFuture.cause());
}
});
}
随着下面的调用栈,我们找到NioServerSocketChannel类的doBind方法。
这里调用了Java NIO的bind方法。
@Override
protected void doBind(SocketAddress localAddress) throws Exception {
javaChannel().bind(localAddress, config.getBacklog());
}
下面我们往回走,回到调用栈的第二行,AbstractChannel类中的bind方法。
@Override
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
// 省略...
boolean wasActive = isActive();
try {
// 从这里返回
doBind(localAddress);
} catch (Throwable t) {
safeSetFailure(promise, t);
closeIfClosed();
return;
}
if (!wasActive && isActive()) {
invokeLater(() -> {
pipeline.fireChannelActive();
// 看这里
readIfIsAutoRead();
});
}
safeSetSuccess(promise);
}
我们跟随以下调用栈进入readIfIsAutoRead方法,最后来到AbstractNioChannel类的doBeginRead方法。
@Override
protected void doBeginRead() throws Exception {
// Channel.read() or ChannelHandlerContext.read() was called
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
}
readPending = true;
final int interestOps = selectionKey.interestOps();
if ((interestOps & readInterestOp) == 0) {
// 这里注册了创建连接事件
selectionKey.interestOps(interestOps | readInterestOp);
}
}
到此,netty就启动起来了。
总结一下,首先创建EventLoopGroup,其中选择线程数量,创建了selector。之后创建ServerBootstrap,使用Builder的建造模式设置属性值。最后执行绑定方法,创建并初始化channel,将channel注册到selector,但注意这里监听的是0,执行真正的绑定方法,并监听ACCEPT事件。
博主水平有限,文中难免出现错误,希望大家指正。