服务端的启动过程
ServerBootstrap和Bootstrap的类图如下所示:
可以看到ServerBootstrap和Bootstrap都是继承自抽象类AbstractBootstrap。因为 ServerBootstrap 和 Bootstrap 大部分的方法和职责都是相同的,所以公共的逻辑由AbstractBootstrap类实现。
本文仅分享 ServerBootstrap 启动 Netty 服务端的过程
服务端启动代码如下:
// 创建 boss 线程组 用于服务端接受客户端的连接
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
// 创建 worker 线程组 用于进行 SocketChannel 的数据读写
EventLoopGroup workerGroup = new NioEventLoopGroup();
// 创建 EchoServerHandler 对象
final EchoServerHandler serverHandler = new EchoServerHandler();
try {
// 创建 ServerBootstrap 对象
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup) // 设置使用的 EventLoopGroup
.channel(NioServerSocketChannel.class) // 设置要被实例化的为 NioServerSocketChannel 类
.option(ChannelOption.SO_BACKLOG, 100) // 设置 NioServerSocketChannel 的可选项
.handler(new LoggingHandler(LogLevel.INFO)) // 设置 NioServerSocketChannel 的处理器
.childHandler(new ChannelInitializer<SocketChannel>() {
//设置连入服务端的 Client 的 SocketChannel 的处理器
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(serverHandler);
}
});
// 绑定端口,并同步等待成功,即启动服务端
ChannelFuture f = b.bind(8090).sync();
// 监听服务端关闭,并阻塞等待
f.channel().closeFuture().sync();
} finally {
// 优雅关闭两个 EventLoopGroup 对象
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
服务端启动代码主要包含两块逻辑:
1、设置启动器类(ServerBootstrap)的属性
2、绑定端口,启动服务端
1、设置启动器类(ServerBootstrap)的属性
1.1、设置线程组
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
super.group(parentGroup);//设置boss线程组
if (childGroup == null) {
throw new NullPointerException("childGroup");
}
if (this.childGroup != null) {
throw new IllegalStateException("childGroup set already");
}
this.childGroup = childGroup;//设置work线程组
return this;
}
1.2、设置服务端Channel
channel(Class<? extends C> channelClass)方法的主要逻辑是创建一个Channel实例的反射工厂,并将该反射工厂实例设置到ServerBootstrap的channelFactory属性中。
public B channel(Class<? extends C> channelClass) {
if (channelClass == null) {//合法性校验
throw new NullPointerException("channelClass");
}
return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}
//反射工厂类,简单工厂
public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {
private final Class<? extends T> clazz;
public ReflectiveChannelFactory(Class<? extends T> clazz) {
if (clazz == null) {
throw new NullPointerException("clazz");
}
this.clazz = clazz;
}
@Override
public T newChannel() {
try {
return clazz.getConstructor().newInstance();
} catch (Throwable t) {
throw new ChannelException("Unable to create Channel from class " + clazz, t);
}
}
@Override
public String toString() {
return StringUtil.simpleClassName(clazz) + ".class";
}
}
//设置反射工厂
public B channelFactory(io.netty.channel.ChannelFactory<? extends C> channelFactory) {
return channelFactory((ChannelFactory<C>) channelFactory);
}
//重载方法
public B channelFactory(ChannelFactory<? extends C> channelFactory) {
if (channelFactory == null) {
throw new NullPointerException("channelFactory");
}
if (this.channelFactory != null) {
throw new IllegalStateException("channelFactory set already");
}
this.channelFactory = channelFactory;//将反射工厂类设置到ServerBootstrap的channelFactory属性
return self();
}
1.3、设置服务端通道的可选项
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();
}
1.4、设置服务端通道的处理器
public B handler(ChannelHandler handler) {
if (handler == null) {
throw new NullPointerException("handler");
}
this.handler = handler;
return self();
}
1.5、设置连入服务端的客户端连接的处理器
public ServerBootstrap childHandler(ChannelHandler childHandler) {
if (childHandler == null) {
throw new NullPointerException("childHandler");
}
this.childHandler = childHandler;
return this;
}
1.6 设置启动器类(ServerBootstrap)的属性小结
1、因为ServerBootstrap类的属性众多,故netty的作者使用了建造者设计模式消除构造函数参数过多的问题
2、有关ServerBootstrap属性设置的代码大多数都是直接给成员变量赋值,没有复杂的逻辑
2、绑定端口,启动服务端
因为绑定端口是ServerBootstrap和bootstrap共有的Api,故由其父类提供实现
调用父类AbstractBootstrap的bind(int inetPort)方法
public ChannelFuture bind(int inetPort) {
return bind(new InetSocketAddress(inetPort));
}
// 调用重载的bind(SocketAddress localAddress)方法
public ChannelFuture bind(SocketAddress localAddress) {
validate();//合法性校验
if (localAddress == null) {
throw new NullPointerException("localAddress");
}
return doBind(localAddress);
}
//调用doBind(final SocketAddress localAddress)方法
private ChannelFuture doBind(final SocketAddress localAddress) {
//初始化并注册,返回一个ChannelFuture对象。因为是异步执行,所以ChannelFuture可以理解为一个执行结果的
//占位符,在将来的某个时刻可以获得执行结果
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
//因为initAndRegister()方法是异步的,所以执行到下面代码时通道的注册和初始化工作有可能尚未完成,
//故需要对此进行分类讨论
//如果此时 初始化和注册 已完成,则执行执行绑定端口方法
if (regFuture.isDone()) {
ChannelPromise promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise);
return promise;
} else {
//如果此时初始化和注册操作未完成,则创建promise,并设置监听器,等待初始化和注册操作完成后的回调
final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
regFuture.addListener(new ChannelFutureListener() {
//操作完成后回调,如果初始化和注册的过程中未发生任何异常,则会调用doBind0方法执行绑定操作
@Override
public void operationComplete(ChannelFuture future) throws Exception {
Throwable cause = future.cause();
if (cause != null) {//如果抛出异常
promise.setFailure(cause);
} else {//未抛出异常则执行绑定操作
promise.registered();
doBind0(regFuture, channel, localAddress, promise);
}
}
});
return promise;
}
}
可以看到doBind方法逻辑为:
1、初始化并注册通道
2、绑定到指定的端口
接下来对上述两个步骤进行详细分析
2.1初始化并注册通道
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
//调用1.2中设置的channel反射工厂创建对应channel的实例,在这里就是创建NioServerSocketChannel
channel = channelFactory.newChannel();
init(channel);//初始化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);
}
//初始化完成后将当前通道实例注册到对应线程组(EventLoopGroup)中的线程(EventLoop)中
ChannelFuture regFuture = config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
initAndRegister()方法的大致逻辑为:
1、创建并初始化Channel实例
2、注册 Channel 到 EventLoopGroup 中
2.1.1创建并初始化Channel实例
因为服务端ServerBootstrap和客户端Bootstrap的初始化逻辑不同,故init的逻辑由子类实现,AbstractBootstrap类只提供抽象方法。
@Override
void init(Channel channel) throws Exception {
//设置ChannelOption
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(0));
}
synchronized (childAttrs) {
currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(0));
}
p.addLast(new ChannelInitializer<Channel>() {
//在Channel注册完成会被回调
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
// 添加用户配置的 ChannelHandler 到 pipeline 中。
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
//添加 ServerBootstrapAcceptor到pipeline
//确保是由channel绑定的eventLoop来执行
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}
2.1.2 注册 Channel 到 EventLoopGroup中
通过config().group() 获取boss线程组,调用register(Channel channel)方法完成注册
ChannelFuture regFuture = config().group().register(channel);
调用MultithreadEventLoopGroup的register(Channel channel)方法
@Override
public ChannelFuture register(Channel channel) {
return next().register(channel);
}
next()方法会调用父类的事件执行选择器EventExecutorChooser在线程组中选择一个EventLoop执行注册操作
调用SingleThreadEventLoop#register(Channel channel)方法执行注册
@Override
public ChannelFuture register(Channel channel) {
return register(new DefaultChannelPromise(channel, this));
}
//调用重载的register方法
@Override
public ChannelFuture register(final ChannelPromise promise) {
ObjectUtil.checkNotNull(promise, "promise");
//调用Channel的Unsafe的具体实现类(AbstractChannel类的内部类AbstractUnsafe)完成注册
promise.channel().unsafe().register(this, promise);
return promise;
}
AbstractChannel类的内部类AbstractUnsafe的register(EventLoop eventLoop, final ChannelPromise promise)方法
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
if (eventLoop == null) {
throw new NullPointerException("eventLoop");
}
//判断当前channel是否已经被注册过,如果已经注册过则抛出非法状态异常
if (isRegistered()) {
promise.setFailure(new IllegalStateException("registered to an event loop already"));
return;
}
//类型校验,校验 Channel 和 eventLoop 匹配,此时应该是NioEventLoop的子类
if (!isCompatible(eventLoop)) {
promise.setFailure(
new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
return;
}
AbstractChannel.this.eventLoop = eventLoop;
//如果当前线程是channel绑定的EventLoop,则执行注册。否则,调用对应eventLoop执行
//因为当前是服务端的启动,所以当前线程一定是Main线程,故会将注册任务交给绑定的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);
}
}
}
private void register0(ChannelPromise promise) {
try {
//校验当前通道是否处于打开状态
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
boolean firstRegistration = neverRegistered;
//调用底层jdk的api将通道实例注册到对应的Selector上
doRegister();
neverRegistered = false;
registered = true;
//如果是第一次注册的话,则会执行一遍pipeline中的所有handler
pipeline.invokeHandlerAddedIfNeeded();
safeSetSuccess(promise);
//触发通道注册事件
pipeline.fireChannelRegistered();
if (isActive()) {
if (firstRegistration) {
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
beginRead();
}
}
} catch (Throwable t) {
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
}
//实际注册的方法
@Override
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
//调用jdk的Channel实例注册到选择器中,并且声明感兴趣的事件为 0
selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
return;
} catch (CancelledKeyException e) {
if (!selected) {
eventLoop().selectNow();
selected = true;
} else {
throw e;
}
}
}
}
DefaultChannelPipeline的invokeHandlerAddedIfNeeded()方法
final void invokeHandlerAddedIfNeeded() {
assert channel.eventLoop().inEventLoop();//当前线程必须是channel绑定的eventLoop
if (firstRegistration) {
firstRegistration = false;
//回调所有已经添加到pipeline中的Handler
callHandlerAddedForAllHandlers();
}
}
触发通道注册事件DefaultChannelPipeline#fireChannelRegistered(),在本例中主要作用就是调用LoggingHandler类打印日志
@Override
public final ChannelPipeline fireChannelRegistered() {
AbstractChannelHandlerContext.invokeChannelRegistered(head);
return this;
}
2.2绑定到指定的端口
初始化和注册通道的逻辑完成后接着会执行绑定端口操作,启动服务端
private static void doBind0(
final ChannelFuture regFuture, final Channel channel,
final SocketAddress localAddress, final ChannelPromise promise) {
//调用channel通道对应的eventLoop执行绑定方法
//此处调用channel.eventLoop()执行任务的原因是,此处代码会在register0()方法中的 //pipeline.fireChannelRegistered()方法之前执行,
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());
}
}
});
}
通道绑定端口的调用链如下所示:
AbstractChannel#bind(SocketAddress localAddress, ChannelPromise promise)
—>DefaultChannelPipeline#bind(SocketAddress localAddress, ChannelPromise promise)
—>AbstractChannelHandlerContext#bind(final SocketAddress localAddress, final ChannelPromise promise)
—>AbstractChannelHandlerContext#invokeBind(SocketAddress localAddress, ChannelPromise promise)
—>LoggingHandler#bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
—>AbstractChannelHandlerContext#bind(final SocketAddress localAddress, final ChannelPromise promise)
—>AbstractChannelHandlerContext#invokeBind(SocketAddress localAddress, ChannelPromise promise)
—>DefaultChannelPipeline#bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
—>AbstractUnsafe#bind(final SocketAddress localAddress, final ChannelPromise promise)
@Override
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
assertEventLoop();//必须是和channel相关联的Eventloop
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();//当前Channel是否绑定对应端口
try {
doBind(localAddress);
} catch (Throwable t) {
safeSetFailure(promise, t);
closeIfClosed();
return;
}
//如果成功绑定指定端口,则触发通道Active事件
if (!wasActive && isActive()) {
invokeLater(new Runnable() {
@Override
public void run() {
//触发通道Active事件
pipeline.fireChannelActive();
}
});
}
safeSetSuccess(promise);
}
/**
* 调用jdk底层的Channel对象绑定指定端口
*
*/
@Override
protected void doBind(SocketAddress localAddress) throws Exception {
if (PlatformDependent.javaVersion() >= 7) {
javaChannel().bind(localAddress, config.getBacklog());
} else {
javaChannel().socket().bind(localAddress, config.getBacklog());
}
}
触发通道Active事件
@Override
public final ChannelPipeline fireChannelActive() {
AbstractChannelHandlerContext.invokeChannelActive(head);
return this;
}
AbstractChannelHandlerContext.invokeChannelActive(head);方法最终会调用AbstractUnsafe的beginRead()方法
@Override
public final void beginRead() {
// 判断是否在 EventLoop 的线程中。
assertEventLoop();
// Channel 必须激活
if (!isActive()) {
return;
}
// 执行开始读取
try {
doBeginRead();
} catch (final Exception e) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireExceptionCaught(e);
}
});
close(voidPromise());
}
}
// AbstractNioMessageChannel.java
@Override
protected void doBeginRead() throws Exception {
if (inputShutdown) {
return;
}
super.doBeginRead();
}
// AbstractNioChannel.java
@Override
protected void doBeginRead() throws Exception {
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
}
readPending = true;
final int interestOps = selectionKey.interestOps(); //此时interestOps应该为0
if ((interestOps & readInterestOp) == 0) {
//修改当前selector感兴趣的事件为Accept事件
//也就说,设置完成后服务端就可以开始处理客户端的连接事件了
selectionKey.interestOps(interestOps | readInterestOp);
}
}