上一篇文章中,研究了 NioEventLoopGroup
到底是什么,本文主要从bind方法入手。
属性设置
属性设置即设置为channel设置一些参数:
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(group)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childOption(ChannelOption.TCP_NODELAY, true)
.localAddress(new InetSocketAddress(port))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new HelloServerHandler());
}
});
总结可以分为以下几种:
- 设置
group
,即设置boss和woker的线程组,上面例子中,boss和woker公用线程组。 - 指定
channel
,即指定具体的channelFactory
,即由于是服务端,所以此时channelFactory
为NioServerSocketChannel
,实际是通过反射泛型来创建对应channel。 - 设置option,即设置server的一些tcp的参数。
- 设置childOption,即设置server的client的一些参数。
- 给新channel设置childerhandler,即每连接一个channel,netty会制定一个对应channel与之对应,此时channel中初始化的handler就是源自于此。
localAddress
方法 绑定端口- 通过
handler
方法给server的handler增加处理逻辑。
…
bind操作
首先进入的是AbstractBootstrap
中bind
方法,核心逻辑在doBind
方法中:
private ChannelFuture doBind(final SocketAddress localAddress) {
final ChannelFuture regFuture = initAndRegister(); // 初始化并且注册到selector中
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 PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
regFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
Throwable cause = future.cause();
if (cause != null) {
// 失败了
promise.setFailure(cause);
} else {
// 使用监听来注册
// See https://github.com/netty/netty/issues/2586
promise.registered();
doBind0(regFuture, channel, localAddress, promise);
}
}
});
return promise;
}
}
上面代码只是定义了大体的框架,但不难理解:
- 初始化channel
- 注册channel
- 绑定channel
initAndRegister
简化后的代码如下:
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
channel = channelFactory.newChannel();
init(channel);
} catch (Throwable t) {
if (channel != null) {
// channel can be null if newChannel crashed (eg SocketException("too many open files"))
channel.unsafe().closeForcibly();
// as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
}
return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
}
ChannelFuture regFuture = config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
- 通过channelFactory反射获取一个channel,如果失败了,可能会抛出
SocketException("too many open files")
异常。 config().group().register(channel)
给channel选择一个reactor线程,并注册进selector。- 根据不同状态,发起不同通知。
init
上一步中,获取了channel,这一步则继续为channel设置一些属性,对于 ServerSocketChannel
来说,他也是一个netty中的channel,所以也需要设置对应handler。
void init(Channel channel) throws Exception {
... 省略设置option
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));
}
// 添加一个channelInitializer
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline(); // 此时传参的channel是一个NioServerSocketChannel
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
// 给server的channel增加一个handler接收处理器
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}
上面主要是构造了NioServerSocketChannel
数据,设置一些channel参数,并增加一个 ChannelInitializer
并将一个 ServerBootstrapAcceptor
作为server端接收器传入。
group和register
group
即获取当前定义的NioEventLoopGroup
,看看register
方法:
public ChannelFuture register(Channel channel) {
return next().register(channel);
}
前一个 next()
主要为通过负载策略,来将channel绑定到一个EventLoop上面,即这个channel中事件,都由该EventLoop处理。具体的策略为上一篇看到的 EventExecutorChooserFactory.EventExecutorChooser
。
再往下看 register
:
public ChannelFuture register(Channel channel) {
return register(new DefaultChannelPromise(channel, this));
}
和
public ChannelFuture register(final ChannelPromise promise) {
ObjectUtil.checkNotNull(promise, "promise");
promise.channel().unsafe().register(this, promise);
return promise;
}
此时channel为:NioServerSocketChannel
unsafe为:AbstractNioMessageChannel.NioMessageUnsafe
而register方法则是进入到 AbstractChannel.AbstractUnsafe
中,里面register方法,除了前面校验注册保证幂等性外,就是通过register0来进行实际的调用:
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
... 省略校验部分
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.inEventLoop()
判断是否在reactor线程中,从而决定register0的执行位置。关于这个后面文章单独分析。
总结
从整体代码复杂度来说,netty还是比较容易理解的。本文主要是以一些框架性源码进行分析,并未深入到细节,下一篇则主要对 register0
和 doBind0
两个方法进行研究。
关注博主公众号: 六点A君。
哈哈哈,一起研究Netty: