Bootstrap类是Netty提供的一个工厂类,作用:Netty组件的组装,Netty程序的初始化。
一,概念
1.父子通道
在Netty中,每一个NioSocketChannel通道所封装的是Java Nio通道,java Nio下面就是操作系统封装的socket描述符。操作系统的底层socket描述符又分为:
1.连接监听类型:放在服务器端,负责接收客户端的套接字连接请求,一个监听类型的socket描述符可以监听成千上万个连接请求。
2.传输数据类型:负责传输数据,在同一条tcp中,服务器和客户端都有一个传输数据类型的scoket描述符
所以在Netty中,NioServersocketChannel就是监听类型是,作为父通道。
NioSocketChannel是传输类型的,作为子通套。
2.EventLoopGroup线程组
在Netty中,一个EventLoop对于一个SubReactor子反应器,我们高效的netty肯定不是单线程的reactor反应器模式,肯定是多线程的,所以这里必然存在多个子反应器,对于子反应器的管理,就推出了EventLoopGroup线程组,在构建的时候,传入参数,多少个线程,就初始化多少个线程,一个子反应器里面 有一个Thread还有一个selector。如果不传入参数,那么默认是子反应器数量是cpu数量的2倍。
对于netty,把服务端的监听,和处理分为了两个线程组,一个线程组专门负责监听和接收,被称为“包工头”另外一个专门负责Io事件的处理,也就是板砖,被称为“工人”,他们两个如何协调工作呢?这就是我们要引出的重点。
3.Bootstrap的启动流程
如同我们第一节中demo代码内容,Bootstrap的启动分为八个步骤,实现了组件的组装,配置,以及服务器/客户端的启动。
//1 设置反应器线程组
b.group(bossLoopGroup,workerLoopGroup);
//2 设置nio类型的通道
b.channel(NioServerSocketChannel.class);
//3 设置监听端口
b.localAddress(serverPort);
//4 设置通道的参数
b.option(ChannelOption.SO_KEEPALIVE,true);
b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
//5 装配子通道流水线
b.childHandler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel ch){
ch.pipeline().addLast(new NettyDiscardHandler());
}
});
//6 开始绑定服务器,通过调用sync同步方法阻塞直到绑定成功
ChannelFuture channelFuture=b.bind().sync();
//7 等待通道关闭的异步任务结束
ChannelFuture closeFuture =channelFuture.channel().closeFuture();
closeFuture.sync();
//8 关闭EventLoopGroup
workerLoopGroup.shutdownGracefully();
bossLoopGroup.shutdownGracefully();
4.设置通道参数:它使用了bootstrap的option()方法,对于服务端来说,它是给bossGroup设置的选项,比如第一个:是否开启tcp底层心跳机制,true为开启。如果给子通道设置属性,则使用ChildOption()方法。
5.装配子通道的Pipeline流水线:每个通道的子通道,都有一条流水线,它的内部有一个双向链表,装配流水线就是:将业务处理器handler实例加入到双向链表中。
当父通道 成功接收 一个请求连接,并成功创建一个子通道之后,就会初始化子通道,就会调用此处的ChannelInitializer的实例,调用初始化方法。
(注意,我们使用serversocket进行监听,当收到一个请求的时候,我们返回的是一个socket,这个时候,我们就把socket发送给handler进行处理,所以在服务端,我们就有父子通道的存在,不要误以为子通道只在客户端)
6.开始绑定服务器新连接的监听端口:返回一个端口绑定Netty的异步任务channelFuture,并没有给channelFuture异步任务添加回调监视器,而是阻塞异步任务,直到端口绑定任务执行完成。
二,源码
2.1 AbstractBootstrap
看一下bootStrap的结构,他分为client和server端的启动类,共同实现了AbstractBootStrap
我们先研究一下 父类AbstracBootStrap
重要字段:
volatile EventLoopGroup group; //acceptor操作的 group
@SuppressWarnings("deprecation") //这个注解表示已经过期,有更好的替代
private volatile ChannelFactory<? extends C> channelFactory; //channel工厂,生成channel
private volatile SocketAddress localAddress; //地址
validation purposes.
// 属性配置
private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();
//附加属性设置
private final Map<AttributeKey<?>, Object> attrs = new ConcurrentHashMap<AttributeKey<?>, Object>();
//处理类
private volatile ChannelHandler handler;
2.2 ServerBootStrap
字段简单:
private final Map<ChannelOption<?>, Object> childOptions = new LinkedHashMap<ChannelOption<?>, Object>();
private final Map<AttributeKey<?>, Object> childAttrs = new ConcurrentHashMap<AttributeKey<?>, Object>();
private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);
private volatile EventLoopGroup childGroup;
// 流水线 handler
private volatile ChannelHandler childHandler;
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
super.group(parentGroup);
this.childGroup = ObjectUtil.checkNotNull(childGroup, "childGroup");
return this;
}
public B group(EventLoopGroup group) {
this.group = group;
return self();
}
2 设置channel类型,这个是必须的,不可省略,方法存在于父类中,直接调用channelFactory()方法,参数是实现了ChannelFactory接口的类,将我们传入的 channel.class中的构造方法扣出来了。
public B channel(Class<? extends C> channelClass) {
return channelFactory(new ReflectiveChannelFactory<C>(
ObjectUtil.checkNotNull(channelClass, "channelClass")
));
}
参数源码:
public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {
private final Constructor<? extends T> constructor;
public ReflectiveChannelFactory(Class<? extends T> clazz) {
ObjectUtil.checkNotNull(clazz, "clazz");
try {
this.constructor = clazz.getConstructor(); //反射获取了参数的 构造方法
...
}
//通过反射,构造方法,获取实例
@Override
public T newChannel() {
return constructor.newInstance();
...抛异常
}
channelFactory()方法: 简单的把参数,保存到父类AbstractBootstrap中
public B channelFactory(ChannelFactory<? extends C> channelFactory) {
...
this.channelFactory = channelFactory;
return self();
}
小总结:b.child()方法,就是生成了一个channelFactory,可以通过反射实例化我们传入的channel.class对象。
3.设置监听端口,可以通过loadAddress放入,也可以通过Bind()放入
loadAddress()方式,不管放入什么,最后对包装成为SocketAddress存放与父类AbstractBootstrap中
public B localAddress(SocketAddress localAddress) {
this.localAddress = localAddress;
return self();
}
public B localAddress(int inetPort) {
return localAddress(new InetSocketAddress(inetPort));
}
public B localAddress(String inetHost, int inetPort) {
return localAddress(SocketUtils.socketAddress(inetHost, inetPort));
}
public B localAddress(InetAddress inetHost, int inetPort) {
return localAddress(new InetSocketAddress(inetHost, inetPort));
}
4.设置通道参数option(),准确说是父通道的参数,将参数放入options集合中
public <T> B option(ChannelOption<T> option, T value) {
ObjectUtil.checkNotNull(option, "option");
synchronized (options) {
if (value == null) { //通过null的方式,可以删除设置
options.remove(option);
} else {
options.put(option, value);
}
}
return self();
}
5.装配流水线,这个是必须的
public ServerBootstrap childHandler(ChannelHandler childHandler) {
this.childHandler = ObjectUtil.checkNotNull(childHandler, "childHandler");
return this;
}
这个参数是重点,内容就是直接把值赋给字段
我们放入的是实现了channelInitializer<SocketChannel>的类,并且实现initChannel()方法,他的继承关系图
他的顶部是是channelHandler接口,和ServerBootStrap中方法参数匹配上了,但是这里的范型也很特殊,因为它关系到了initChannel()的参数,之后在分析
6.bind() 重要内容到达,前方高能,位于父类AbstractBootStrap中
首先,最基本的套路,如果设置了SocketAddress,则直接走该Bind()入口,如果没有则需要包装参数,再走这里
public ChannelFuture bind(SocketAddress localAddress) {
validate();
return doBind(ObjectUtil.checkNotNull(localAddress, "localAddress"));
}
所以内容还是在doBind()中,参数是localAddress
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 {
...
}
}
重点是在第一个方法上面
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
//获得我们b.channel(...Class)传入的对象,也就是NioServerSocketChannel
channel = channelFactory.newChannel();
init(channel); // 调用初始化方法 委派模式,子类实现
} ...
//注解channel,调用的是 EventLoopGroup.register()方法 也就是把handler注册到selector中
ChannelFuture regFuture = config().group().register(channel);
if (regFuture.cause() != null) {
...
}
return regFuture;
}
ServerBootStrap.init()
void init(Channel channel) { //初始化 b.channel(传入的对象)NioServerSocketChannel
//参数配置
setChannelOptions(channel, newOptionsArray(), logger);
setAttributes(channel, attrs0().entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY));
//获得流水线
ChannelPipeline p = channel.pipeline();
//缓存4个字段
final EventLoopGroup currentChildGroup = childGroup;
final ChannelHandler currentChildHandler = childHandler; //b.childHandler()放入
final Entry<ChannelOption<?>, Object>[] currentChildOptions;
synchronized (childOptions) {
currentChildOptions = childOptions.entrySet().toArray(EMPTY_OPTION_ARRAY);
}
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();
ChannelHandler handler = config.handler(); //父类存放的handler 是一个LoginHandler
if (handler != null) {
pipeline.addLast(handler); //【加入】
}
ch.eventLoop().execute(new Runnable() {
@Override
public void run() { //使用acceptor对我们导入的进行封装
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}