在Netty的io.netty.example包下有很多Netty的案例,可以用于源码分析和学习。现在使用该包下的Echo案例分析Netty的启动过程,入口代码如下:
public final class EchoServer {
static final boolean SSL = System.getProperty("ssl") != null;
static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));
public static void main(String[] args) throws Exception {
// 配置 SSL
final SslContext sslCtx;
if (SSL) {
SelfSignedCertificate ssc = new SelfSignedCertificate();
sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
} else {
sslCtx = null;
}
// 配置服务端
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
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(new LoggingHandler(LogLevel.INFO));
// 添加处理器
p.addLast(new EchoServerHandler());
}
});
// 绑定监听端口,启动服务
ChannelFuture f = b.bind(PORT).sync();
// Wait until the server socket is closed.
f.channel().closeFuture().sync();
} finally {
// 关闭所有的事件循环以终止所有的线程
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
1、创建了两个EventLoopGroup对象:
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
这两个对象是Netty的核心对象,Netty的整个运行过程都依赖于它们。bossGroup用于接收TCP请求,并将接收的请求交给workerGroup处理;workerGroup会获取到真正的连接,然后通过连接和客户端通信,完成读写、编解码等工作。
EventLoopGroup,即事件循环组(线程组)。其中包含多个 EventLoop,EventLoop可以注册channel,用于在事件循环中进行选择(和选择器有关)。
这里的1表示bossGroup事件循环组中只开1个线程用于接收请求,而workerGroup默认会开启CPU核数的2倍个线程:
private static final int DEFAULT_EVENT_LOOP_THREADS;
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);
}
}
由上面的代码可知,我们可以通过设置 io.netty.eventLoopThreads 的值来自定义默认开启的线程个数。
深入到 new NioEventLoopGroup() 的内部,代码如下:
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
EventExecutorChooserFactory chooserFactory, Object... args) {
if (nThreads <= 0) {
throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
}
//初始化线程池
if (executor == null) {
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
// 创建EventExecutor数组
children = new EventExecutor[nThreads];
for (int i = 0; i < nThreads; i ++) {
boolean success = false;
try {
// 为EventExecutor数组中的每个元素赋值,赋值的每一个对象都是NioEventLoop类型
children[i] = newChild(executor, args);
success = true;
} catch (Exception e) {
// TODO: Think about if this is a good exception type
throw new IllegalStateException("failed to create a child event loop", e);
} finally {
if (!success) {
for (int j = 0; j < i; j ++) {
children[j].shutdownGracefully();
}
for (int j = 0; j < i; j ++) {
EventExecutor e = children[j];
try {
while (!e.isTerminated()) {
e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
}
} catch (InterruptedException interrupted) {
// Let the caller handle the interruption.
Thread.currentThread().interrupt();
break;
}
}
}
}
}
chooser = chooserFactory.newChooser(children);
final FutureListener<Object> terminationListener = new FutureListener<Object>() {
@Override
public void operationComplete(Future<Object> future) throws Exception {
if (terminatedChildren.incrementAndGet() == children.length) {
terminationFuture.setSuccess(null);
}
}
};
for (EventExecutor e: children) {
e.terminationFuture().addListener(terminationListener);
}
Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
Collections.addAll(childrenSet, children);
readonlyChildren = Collections.unmodifiableSet(childrenSet);
}
先初始化一个EventExecutor数组,再为EventExecutor[]的每一个元素赋值:
// 初始化EventExecutor数组
children = new EventExecutor[nThreads];
for (int i = 0; i < nThreads; i ++) {
...
try {
//为EventExecutor[]的每一个元素赋值
children[i] = newChild(executor, args);
success = true;
} catch (Exception e) {
// TODO: Think about if this is a good exception type
throw new IllegalStateException("failed to create a child event loop", e);
} finally {
...
}
...
}
这里给EventExecutor[]的每一个元素赋的值都是一个NioEventLoop对象:NioEventLoop实现了EventLoop和EventExecutor接口
@Override
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
return new NioEventLoop(this, executor, (SelectorProvider) args[0],
((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
}
2、完成创建两个 EventLoopGroup 对象之后,接着创建了一个 ServerBootstrap 对象,即服务端引导对象:该对象的主要作用是限制紧接着要在bind()中创建的ServerSocketChannel(代码中指定为NioServerSocketChannel类型)和后续客户端连接时要创建的SocketChannel对象,限制的内容包括ServerSocketChannel的实际类型、TCP参数、需绑定的handler等
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
// 限制ServerSocketChannel的类型为NioServerSocketChannel
.channel(NioServerSocketChannel.class)
// 设置一些TCP参数,option()可以调用多次设置多个参数
.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(new LoggingHandler(LogLevel.INFO));
// 添加处理器
p.addLast(new EchoServerHandler());
}
});
...
} finally {
...
}
ServerBootstrap 是一个引导对象,用于启动服务端和引导整个程序的初始化。它和ServerChannel关联,ServerChannel 继承了 Channel(通过方法channel(NioServerSocketChannel.class)指定channel类型,即指定了服务端接收请求的ServerSocketChannel的类型,它不限制客户端连接时创建的SocketChannel类型),可以通过ServerChannel获取到远程IP等信息:
public interface ServerSocketChannel extends ServerChannel {
@Override
ServerSocketChannelConfig config();
@Override
InetSocketAddress localAddress();
@Override
InetSocketAddress remoteAddress();
}
需要注意的是在创建ServerBootstrap 对象时虽没有传入参数,但创建的对象是有一些默认属性(包括从父类继承的属性)的:
private final Map<ChannelOption<?>, Object> childOptions = new LinkedHashMap<ChannelOption<?>, Object>();
private final Map<AttributeKey<?>, Object> childAttrs = new LinkedHashMap<AttributeKey<?>, Object>();
// config对象后面会经常用到
private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);
private volatile EventLoopGroup childGroup;
private volatile ChannelHandler childHandler;
这里只是通过反射创建了一个ChannelFactory对象,并没有真正创建Channel对象(创建Channel对象是在bind()方法中完成的),这里只是给ChannelFactory指定了类型,以确定后续通过ChannelFactory创建哪种类型的Channel:
public B channel(Class<? extends C> channelClass) {
if (channelClass == null) {
throw new NullPointerException("channelClass");
}
return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}
通过option()设置TCP参数(option()方法可以调用多次,以向options中添加或移除配置),options的本质是一个Map:
private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();
public <T> B option(ChannelOption<T> option, T value) {
if (option == null) {
throw new NullPointerException("option");
}
if (value == null) {
synchronized (options) {
options.remove(option);
}
} else {
synchronized (options) {
options.put(option, value);
}
}
return self();
}
完成TCP的参数设置后,接着为bossGroup和workerGroup设置处理业务或者说处理事件的handler,其中handler()方法中设置的handler和bossGroup关联(或者说会注册到bossGroup的EventLoop上的ServerSocketChannel上,在后面的bind()方法中会完成该注册绑定),childHandler()方法中设置的handler和workerGroup关联(或者说会注册到workerGroup的EventLoop上的SocketChannel上,这个绑定将在有客户端连接时创建SocketChannel时完成)。
3、绑定端口,等待客户端连接:在bind()方法中会先初始化ServerSocketChannel并为ServerSocketChannel绑定handler,而SocketChannel则是在有客户端连接时创建,其handler也将在彼时绑定
// 绑定监听端口,启动服务端
ChannelFuture f = b.bind(PORT).sync();
bind()方法最终会调用doBind()方法:
private ChannelFuture doBind(final SocketAddress localAddress) {
// 初始化并注册ServerSocketChannel
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
if (regFuture.isDone()) {
// 到这儿,注册工作就算成功完成了
ChannelPromise promise = channel.newPromise();
//执行doBind0(),完成端口绑定
doBind0(regFuture, channel, localAddress, promise);
return promise;
} else {
// 到这,注册工作基本上总是可以成功,但为以防万一,进行了容错处理
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;
}
}
doBind()方法的第一行就是用之前通过反射(channel(NioServerSocketChannel.class))初始化好的ChannelFactory来创建ServerSocketChannel(类型为指定的NioServerSocketChannel),创建后初始化并将该channel注册到bossGroup的EventLoop(即上文提到的NioEventLoop)上:
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
//创建channel
channel = channelFactory.newChannel();
//初始化channel,设置option中的参数等
init(channel);
} catch (Throwable t) {
...
}
// 注册channel,这里将channel注册到了bossGroup上
ChannelFuture regFuture = config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
init(channel)源码如下:给每一个channel设置指定的属性和绑定handler
@Override
void init(Channel channel) throws Exception {
final Map<ChannelOption<?>, Object> options = options0();
synchronized (options) {
setChannelOptions(channel, options, logger);
}
final Map<AttributeKey<?>, Object> attrs = attrs0();
synchronized (attrs) {
for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
@SuppressWarnings("unchecked")
AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
channel.attr(key).set(e.getValue());
}
}
ChannelPipeline p = channel.pipeline();
final EventLoopGroup currentChildGroup = childGroup;
final ChannelHandler currentChildHandler = childHandler;
final Entry<ChannelOption<?>, Object>[] currentChildOptions;
final Entry<AttributeKey<?>, Object>[] currentChildAttrs;
synchronized (childOptions) {
currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size()));
}
synchronized (childAttrs) {
currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));
}
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
// 绑定handler:这里绑定的是在handler()方法中设置的handler
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));
}
});
}
});
}
pipeLine是一个双向链表,且它本身初始化了head和tail,这里调用addLast(),是将新的handler插入到tail的前面,tail永远会在最后,做一些系统的固定工作:
@Override
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
final AbstractChannelHandlerContext newCtx;
synchronized (this) {
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()) {
newCtx.setAddPending();
executor.execute(new Runnable() {
@Override
public void run() {
callHandlerAdded0(newCtx);
}
});
return this;
}
}
callHandlerAdded0(newCtx);
return this;
}
addLast0()的源码:
private void addLast0(AbstractChannelHandlerContext newCtx) {
AbstractChannelHandlerContext prev = tail.prev;
newCtx.prev = prev;
newCtx.next = tail;
prev.next = newCtx;
tail.prev = newCtx;
}
创建、初始化并注册ServerSocketChannel之后会执行doBind0()方法,最终会调用到AbstractChannelHandlerContext的bind()方法:
@Override
public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
if (localAddress == null) {
throw new NullPointerException("localAddress");
}
if (isNotValidPromise(promise, false)) {
// cancelled
return promise;
}
final AbstractChannelHandlerContext next = findContextOutbound();
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
// 调用具体handler的bind方法
next.invokeBind(localAddress, promise);
} else {
safeExecute(executor, new Runnable() {
@Override
public void run() {
next.invokeBind(localAddress, promise);
}
}, promise, null);
}
return promise;
}
AbstractChannelHandlerContext的bind()方法会被调用多次,调用的次数和注册的handler的个数有关,会调用注册的handler的个数+1次,最后的1次调用的是DefaultChannelPipeline的bind()方法:
@Override
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
throws Exception {
unsafe.bind(localAddress, promise);
}
接着会调用AbstractChannel的bind():
@Override
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
assertEventLoop();
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
...
boolean wasActive = isActive();
try {
//绑定端口
doBind(localAddress);
} catch (Throwable t) {
safeSetFailure(promise, t);
closeIfClosed();
return;
}
...
}
最后通过doBind()方法调用NioServerSocketChannel的doBind()完成端口监听,剩下的就是等待客户端连接了:
@Override
protected void doBind(SocketAddress localAddress) throws Exception {
if (PlatformDependent.javaVersion() >= 7) {
javaChannel().bind(localAddress, config.getBacklog());
} else {
javaChannel().socket().bind(localAddress, config.getBacklog());
}
}
这样服务端就启动了,然后会一直阻塞直到服务端的socket关闭(服务端停止运行),最后会优雅的关闭事件循环组中的所有线程资源。
关于任务或者说事件的执行,见NioEventLoop中的run():
@Override
protected void run() {
for (;;) {
try {
switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
case SelectStrategy.CONTINUE:
continue;
case SelectStrategy.SELECT:
select(wakenUp.getAndSet(false));
if (wakenUp.get()) {
selector.wakeup();
}
// fall through
default:
}
cancelledKeys = 0;
needsToSelectAgain = false;
final int ioRatio = this.ioRatio;
if (ioRatio == 100) {
try {
processSelectedKeys();
} finally {
// Ensure we always run tasks.
runAllTasks();
}
} else {
final long ioStartTime = System.nanoTime();
try {
processSelectedKeys();
} finally {
// Ensure we always run tasks.
final long ioTime = System.nanoTime() - ioStartTime;
runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
}
}
} 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);
}
}
}
通过for(;;)一直循环轮询看是否有新连接进入或有任务需要执行。
Netty启动过程梳理:
①创建2个 EventLoopGroup 线程池数组,数组默认大小 CPU * 2,方便chooser选择线程池时提高性能
②BootStrap 将 boss 设置为 group属性,将 worker 设置为 childGroup 属性
③通过 bind 方法启动,内部重要的方法为 initAndRegister 和 dobind 方法
④initAndRegister 方法会通过反射创建 NioServerSocketChannel 及其相关的 NIO 的对象、pipeline、unsafe,同时也为 pipeline 初始了 head 节点和 tail 节点
⑤在register0 方法成功以后接着调用 dobind 方法,在其中又调用了 doBind0 方法,该方法会调用 NioServerSocketChannel 的 doBind 方法对 JDK 的 channel 和端口进行绑定, 完成 Netty 服务器的所有启动,并开始监听连接事件