Netty服務端啟動(一)——建立和初始化channel
以netty的一個小demo為例(使用的原始碼版本為4.1.50.Final)
從b.bind(PORT)
跟進
最終呼叫到AbstractBootstrap#doBind
這個方法
private ChannelFuture doBind(final SocketAddress localAddress) {
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
if (regFuture.isDone()) {
// At this point we know that the registration was complete and successful.
ChannelPromise promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise);
return promise;
} else {
// Registration future is almost always fulfilled already, but just in case it's not.
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;
}
}
這裡就是服務端建立的主要程式碼
建立Channel
主要概括如下
1.通過demo裡傳入的NioServerSocketChannel
類反射建立channel
2.在NioServerSocketChannel
的建構函式裡
通過java底層的SelectorProvider建立ServerSocketChannel 呼叫父類建構函式設定阻塞模式,建立id,unsafe,pipeline等 建立NioServerSocketChannelConfig,便於進行引數配置
方法體裡第一行程式碼的initAndRegister
方法就是在建立和初始化channel
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
channel = channelFactory.newChannel(); //建立channel,本節分析
init(channel);
} catch (Throwable t) {
//省略
}
ChannelFuture regFuture = config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
建立channel就在channelFactory.newChannel()
這行程式碼裡,這裡會呼叫到ReflectiveChannelFactory#newChannel
這個方法
public T newChannel() {
try {
return constructor.newInstance();
} catch (Throwable t) {
//省略
}
}
可以看到這裡用反射建立一個channel,也就是說channel是通過channelFactory
的constructor
來反射建立的,而channelFactory
是在demo裡呼叫channel方法時初始化的,如下
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) //在這裡初始化了`channelFactory
//省略
在AbstractBootstrap#channel
方法裡
public B channel(Class<? extends C> channelClass) {
return channelFactory(new ReflectiveChannelFactory<C>(
ObjectUtil.checkNotNull(channelClass, "channelClass")
));
}
最終呼叫到AbstractBootstrap#channelFactory
方法裡
public B channelFactory(ChannelFactory<? extends C> channelFactory) {
ObjectUtil.checkNotNull(channelFactory, "channelFactory");
if (this.channelFactory != null) {
throw new IllegalStateException("channelFactory set already");
}
this.channelFactory = channelFactory;
return self();
}
既然這裡是通過反射建立了一個NioServerSocketChannel
,接下來看看NioServerSocketChannel
這個類的建構函式
public NioServerSocketChannel() {
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
先看看NioServerSocketChannel#newSocket
這個方法
private static ServerSocketChannel newSocket(SelectorProvider provider) {
try {
return provider.openServerSocketChannel(); //provider就是DEFAULT_SELECTOR_PROVIDER,為SelectorProvider.provider()
} catch (IOException e) {
//省略
}
}
上面的建構函式呼叫到了另一個建構函式
public NioServerSocketChannel(ServerSocketChannel channel) {
//注意這裡傳入SelectionKey.OP_ACCEPT,用於之後註冊accept事件
super(null, channel, SelectionKey.OP_ACCEPT);
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
super方法呼叫父類做一些簡單的初始化,主要在AbstractNioChannel
和AbstractChannel
這兩個類這種
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent);
this.ch = ch;
this.readInterestOp = readInterestOp;
try {
ch.configureBlocking(false);
} catch (IOException e) {
//省略
}
}
protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
NioServerSocketChannelConfig
則是傳入java底層的ServerSocketChannel方便以後對做一些配置
初始化channel
先概括一下,主要做了這幾件事
將使用者設定的options和attrs設定到channel的config裡 向channel的pipeline裡新增使用者傳入的handler 建立一個特殊的handler即ServerBootstrapAcceptor(用於處理新連線接入)並新增進pipeline裡
接下來分析初始化channel的過程,即init(channel)
這行程式碼
這裡會呼叫到ServerBootstrap#init
這個方法
void init(Channel channel) {
//newOptionsArray這個方法拿到使用者設定的options設定到channel的config裡
setChannelOptions(channel, newOptionsArray(), logger);
//attrs0這個方法拿到使用者設定的attrs設定到channel裡
setAttributes(channel, attrs0().entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY));
ChannelPipeline p = channel.pipeline();
final EventLoopGroup currentChildGroup = childGroup;
final ChannelHandler currentChildHandler = childHandler;
final Entry<ChannelOption<?>, Object>[] currentChildOptions;
//拿到用於配置childHandler的childOptions
synchronized (childOptions) {
currentChildOptions = childOptions.entrySet().toArray(EMPTY_OPTION_ARRAY);
}
//拿到用於配置childHandler的childAttrs
final Entry<AttributeKey<?>, Object>[] currentChildAttrs = childAttrs.entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY);
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) {
final ChannelPipeline pipeline = ch.pipeline();
//拿到demo裡呼叫childHandler方法時新增的handler
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
//ServerBootstrapAcceptor是一個特殊的handler,用於處理新連線接入
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}