netty ServerBootStrap解析

netty 抽象BootStrap定义:[url]http://donald-draper.iteye.com/blog/2392492[/url]
[size=medium][b]引言:[/b][/size]
前面一篇文章我们看了抽象Bootstrap的定义,先来回顾一下:
抽象引导程序AbstractBootstrap,内部关联的一个事件循环组EventLoopGroup,一个通道处理器ChannelHandler,一个通道选项集和一个本地Socket地址及一个通道属性集。内部的方法主要配置事件循环组,通道处理,通道选项集,socket地址,及通道属性,通道注册,地址绑定。注册通道到事件循环组,首先由通道工厂创建通道实例,然后初始化通道,初始化工作待子类实现;然后将实际注册工作委托给事件循环组。绑定定socket地址,首先注册通道到事件循环组,待注册完成时,创建一个绑定任务线程完成地址绑定,实际将地址绑定工作委托给通道,并将绑定任务线程交由通道关联的事件循环的事件执行器执行。

今天我们来看服务端Bootstrap:
package io.netty.bootstrap;

import io.netty.channel.Channel;
import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.ServerChannel;
import io.netty.util.AttributeKey;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;

/**
* {@link Bootstrap} sub-class which allows easy bootstrap of {@link ServerChannel}
*
*/
public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(ServerBootstrap.class);
//通道选项及属性集
private final Map<ChannelOption<?>, Object> childOptions = new LinkedHashMap<ChannelOption<?>, Object>();
private final Map<AttributeKey<?>, Object> childAttrs = new LinkedHashMap<AttributeKey<?>, Object>();
private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);//当前配置
private volatile EventLoopGroup childGroup;//事件循环组
private volatile ChannelHandler childHandler;//通道处理器
public ServerBootstrap() { }
private ServerBootstrap(ServerBootstrap bootstrap) {
super(bootstrap);
childGroup = bootstrap.childGroup;
childHandler = bootstrap.childHandler;
synchronized (bootstrap.childOptions) {
childOptions.putAll(bootstrap.childOptions);
}
synchronized (bootstrap.childAttrs) {
childAttrs.putAll(bootstrap.childAttrs);
}
}
}

从上面可看出,服务端Bootstrap虽然继承与抽象Bootstrap,但他有自己的通道选项及属性集,事件循环组和通道处理器。
/**
* Specify the {@link EventLoopGroup} which is used for the parent (acceptor) and the child (client).
父监听器和孩子客户端同用一个事件循环组
*/
@Override
public ServerBootstrap group(EventLoopGroup group) {
return group(group, group);
}

/**
* Set the {@link EventLoopGroup} for the parent (acceptor) and the child (client). These
* {@link EventLoopGroup}'s are used to handle all the events and IO for {@link ServerChannel} and
* {@link Channel}'s.
设置父监听器和孩子客户端的事件循环,两者用于处理SeverChannel和Channel的所有事件循环
*/
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
super.group(parentGroup);
if (childGroup == null) {
throw new NullPointerException("childGroup");
}
if (this.childGroup != null) {
throw new IllegalStateException("childGroup set already");
}
this.childGroup = childGroup;
return this;
}

大胆猜测一下,parentGroup事件循环组用于监听器ServerChannel接受连接,childGroup事件循环组用于当Server通道接收客户端的连接时,产生一个通道用于与客户端交互,childGroup事件循环组用于处理与客户端交互的通道相关事件和IO操作。

/**
* Allow to specify a {@link ChannelOption} which is used for the {@link Channel} instances once they get created
* (after the acceptor accepted the {@link Channel}). Use a value of {@code null} to remove a previous set
* {@link ChannelOption}.
用于Server通道接收客户端的连接时,产生的通道选项配置
*/
public <T> ServerBootstrap childOption(ChannelOption<T> childOption, T value) {
if (childOption == null) {
throw new NullPointerException("childOption");
}
if (value == null) {
synchronized (childOptions) {
childOptions.remove(childOption);
}
} else {
synchronized (childOptions) {
childOptions.put(childOption, value);
}
}
return this;
}

从这个方法来看,上面的猜测是正确的。


/**
* Set the specific {@link AttributeKey} with the given value on every child {@link Channel}. If the value is
* {@code null} the {@link AttributeKey} is removed
客户端交互通道属性配置
*/
public <T> ServerBootstrap childAttr(AttributeKey<T> childKey, T value) {
if (childKey == null) {
throw new NullPointerException("childKey");
}
if (value == null) {
childAttrs.remove(childKey);
} else {
childAttrs.put(childKey, value);
}
return this;
}

/**
* Set the {@link ChannelHandler} which is used to serve the request for the {@link Channel}'s.
配置与客户端交互通道的通道处理器
*/
public ServerBootstrap childHandler(ChannelHandler childHandler) {
if (childHandler == null) {
throw new NullPointerException("childHandler");
}
this.childHandler = childHandler;
return this;
}
//验证通道配置是否有效
@Override
public ServerBootstrap validate() {
super.validate();
if (childHandler == null) {
throw new IllegalStateException("childHandler not set");
}
if (childGroup == null) {
logger.warn("childGroup is not set. Using parentGroup instead.");
childGroup = config.group();
}
return this;
}
//获取当前Server引导配置
@Override
public final ServerBootstrapConfig config() {
return config;
}

下面我们来看初始化通道,这个是重点:
@Override
void init(Channel channel) throws Exception {
final Map<ChannelOption<?>, Object> options = options0();
synchronized (options) {
//设置父Server通道选项
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();
//设置父Server通道属性
channel.attr(key).set(e.getValue());
}
}
//获取Server通道的Channel管道
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();
ChannelHandler handler = config.handler();
if (handler != null) {
//将通道处理器添加到通道内部的Channel管道内
pipeline.addLast(handler);
}

ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
//将Server引导配置监听器添加到通道内部的Channel管道内
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}

我们来看引导配置监听器,实际为一个Inbound通道处理器
 private static class ServerBootstrapAcceptor extends ChannelInboundHandlerAdapter {
private final EventLoopGroup childGroup;//与客户端交互通道注册的事件循环组
private final ChannelHandler childHandler;//与客户端交互通道的通道处理器
private final Entry<ChannelOption<?>, Object>[] childOptions;//与客户端交互通道的选项配置
private final Entry<AttributeKey<?>, Object>[] childAttrs;//与客户端交互通道的属性
private final Runnable enableAutoReadTask;

ServerBootstrapAcceptor(
final Channel channel, EventLoopGroup childGroup, ChannelHandler childHandler,
Entry<ChannelOption<?>, Object>[] childOptions, Entry<AttributeKey<?>, Object>[] childAttrs) {
this.childGroup = childGroup;
this.childHandler = childHandler;
this.childOptions = childOptions;
this.childAttrs = childAttrs;

// Task which is scheduled to re-enable auto-read.
// It's important to create this Runnable before we try to submit it as otherwise the URLClassLoader may
// not be able to load the class because of the file limit it already reached.
// 此任务用于开启通道自动读取配置,将会被所在的事件循环调度。
// See https://github.com/netty/netty/issues/1328
enableAutoReadTask = new Runnable() {
@Override
public void run() {
//开启通道自动读取配置
channel.config().setAutoRead(true);
}
};
}

@Override
@SuppressWarnings("unchecked")
public void channelRead(ChannelHandlerContext ctx, Object msg) {
//与客户端交互通道
final Channel child = (Channel) msg;
//配置与客户端交互通道的通道处理器
child.pipeline().addLast(childHandler);
//配置与客户端交互通道的选项
setChannelOptions(child, childOptions, logger);

for (Entry<AttributeKey<?>, Object> e: childAttrs) {
//配置与客户端交互通道的属性
child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
}

try {
//注册与客户端交互通道到childGroup事件循环组
childGroup.register(child).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
//注册失败,则关闭通道
forceClose(child, future.cause());
}
}
});
} catch (Throwable t) {
forceClose(child, t);
}
}
//关闭通道
private static void forceClose(Channel child, Throwable t) {
child.unsafe().closeForcibly();
logger.warn("Failed to register an accepted channel: {}", child, t);
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
final ChannelConfig config = ctx.channel().config();
if (config.isAutoRead()) {
// stop accept new connections for 1 second to allow the channel to recover
//发生异常,则停止接受连接请求1秒钟,允许通道恢复
// See https://github.com/netty/netty/issues/1328
config.setAutoRead(false);
ctx.channel().eventLoop().schedule(enableAutoReadTask, 1, TimeUnit.SECONDS);
}
// still let the exceptionCaught event flow through the pipeline to give the user
// a chance to do something with it
//触发异常
ctx.fireExceptionCaught(cause);
}
}


//ChannelConfig
/**
* Sets if {@link ChannelHandlerContext#read()} will be invoked automatically so that a user application doesn't
* need to call it at all. The default value is {@code true}.
*/
ChannelConfig setAutoRead(boolean autoRead);



回到服务端引导配置:
 ServerBootstrap serverBoot = new ServerBootstrap(); 
serverBoot.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
...
ChannelFuture f = serverBoot.bind(inetSocketAddress).sync();


结合前一篇定义抽象Bootstrap定义,我们来理一下ServerBootstrap绑定,完成的任务:
Server引导配置绑定socket地址,首先初始化通道,对于Server引导配置,这个通道为
NioServerSocketChannel,初始化通道,即初始化Server通道,从上面的初始化通道方法来看,初始化通道,首先将Server引导配置的父类抽象Bootstrap的选项和属性配置给Server通道,然后将ServerBootstrapAcceptor添加到Server通道内部的Channel管道内,然后将Server通道注册到parentGroup事件循环组中,然后通过Server通道#bind方法完成实际socket地址;
Server引导配置监听器实际为一个Inbound通道处理器,每当有客户端连接请求时,
则创建一个与客户端交互的通道,将child通道选项及属性配置给通道,并将通道注册到childGroup事件循环组,然后将通道处理器添加到与客户端交互的通道内部的Channel管道中。

再来看其他方法:
//创建属性和选项组
@SuppressWarnings("unchecked")
private static Entry<AttributeKey<?>, Object>[] newAttrArray(int size) {
return new Entry[size];
}
@SuppressWarnings("unchecked")
private static Map.Entry<ChannelOption<?>, Object>[] newOptionArray(int size) {
return new Map.Entry[size];
}

下面几个方法,没有什么好讲的,很简单:
@Override
@SuppressWarnings("CloneDoesntCallSuperClone")
public ServerBootstrap clone() {
return new ServerBootstrap(this);
}

/**
* Return the configured {@link EventLoopGroup} which will be used for the child channels or {@code null}
* if non is configured yet.
*
* @deprecated Use {@link #config()} instead.
*/
@Deprecated
public EventLoopGroup childGroup() {
return childGroup;
}

final ChannelHandler childHandler() {
return childHandler;
}

final Map<ChannelOption<?>, Object> childOptions() {
return copiedMap(childOptions);
}

final Map<AttributeKey<?>, Object> childAttrs() {
return copiedMap(childAttrs);
}

[size=medium][b]总结:[/b][/size]
[color=blue]
服务端Bootstrap虽然继承与抽象Bootstrap,但他有自己的child通道选项及属性集,事件循环组和通道处理器,这些是用于配置,当Server通道接收客户端的请求,创建与客户端交互的通道。当构造Server引导配置时,如果传递一个事件循环,则Server通道监听器和客户端交互的通道公用一个事件循环组,否则parentGroup事件循环组用于监听器ServerChannel接受连接,childGroup事件循环组用于处理与客户端交互的通道相关事件和IO操作。
Server引导配置绑定socket地址,首先初始化通道,对于Server引导配置,这个通道为NioServerSocketChannel,初始化通道,即初始化Server通道;初始化Server通道,首先将Server引导配置的父类抽象Bootstrap的选项和属性配置给Server通道,然后添加ServerBootstrapAcceptor到Server通道内部的Channel管道内,然后将Server通道注册到事件循环组parentGroup中,然后通过Server通道#bind方法完成实际socket地址;Server引导配置监听器实际为一个Inbound通道处理器,每当有客户端连接请求时,则创建一个与客户端交互的通道,将child通道选项及属性配置给通道,并将通道注册到childGroup事件循环组,然后将通道处理器添加到与客户端交互的通道内部的Channel管道中。
[/color]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值