经过上一篇【Netty源码系列】服务端启动流程(一)创建线程池组 可知大致创建线程池组的流程。而这一篇文章则是剖析 ServerBootstrap 的创建流程。这一个类是Netty服务端的“灵魂”!可以通过 ServerBootstrap 定制化配置线程池组、TCP参数、Handler等等,最后绑定端口即可启动Netty服务端。所以毫不夸张的说,ServerBootstrap 就是Netty服务端的灵魂人物!
先看下Netty服务端是如何创建并初始化ServerBootstrap的。
// 创建 ServerBootstrap 对象
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup) // 绑定线程池组
.channel(NioServerSocketChannel.class) // 服务端channel类型
.option(ChannelOption.SO_BACKLOG, 100) // TCP配置
.handler(new LoggingHandler(LogLevel.INFO)) // 服务端Handler
.childHandler(new ChannelInitializer() { // 客户端Handler
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
//p.addLast(new LoggingHandler(LogLevel.INFO));
p.addLast(serverHandler);
}
});
先简单介绍一下 ServerBootstrap 几个重要的方法:
group():设置主从线程池
channel():指定Channel类型
handler():配置ServerSocketChannel相关的handler(处理器)
option():配置ServerSocketChannel相关的TCP参数
attr(): 设置ServerSocketChannel的属性
childHandler():配置SocketChannel相关的handler
childOption():配置SocketChannel相关的TCP参数
childAttr():设置SocketChannel的属性
接下来,看一下上面几个核心的方法源码
设置主从线程池组——group()
group方法在ServerBootstrap中有两个重载方法,区别在于:一个参数的group方法指的是接收和处理客户端请求都是同一个线程池组;两个参数的group方法指的是第一个参数是bossGroup,第二个参数是workerGroup
/**
* 同一个线程池组负责接收和处理客户端请求
*/
@Override
public ServerBootstrap group(EventLoopGroup group) {
return group(group, group);
}
/**
* @param parentGroup 指的是bossGroup,负责接收客户端请求
* @param childGroup 指的是workerGroup,负责处理客户端请求
*/
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
super.group(parentGroup);
if (this.childGroup != null) {
throw new IllegalStateException("childGroup set already");
}
this.childGroup = ObjectUtil.checkNotNull(childGroup, "childGroup");
return this;
}
可以看出,group(EventLoopGroup group)方法实际上也是调用group(EventLoopGroup parentGroup, EventLoopGroup childGroup),所以重点剖析两个参数的group方法
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
// 调用父类的group方法
super.group(parentGroup);
if (this.childGroup != null) {
throw new IllegalStateException("childGroup set already");
}
// 赋值负责处理客户端请求的线程池组
this.childGroup = ObjectUtil.checkNotNull(childGroup, "childGroup");
return this;
}
// AbstractBootstrap类
public B group(EventLoopGroup group) {
ObjectUtil.checkNotNull(group, "group");
if (this.group != null) {
throw new IllegalStateException("group set already");
}
// 赋值负责接收客户端请求的线程池组
this.group = group;
return self();
}
其实可以看出,group方法并没有啥特别的逻辑,只不过就是赋值两个线程池组到对应的属性而已...
指定Channel类型——channel()
channel方法实际上是调用ServerBootstrap的父类AbstractBootstrap,通过channel方法赋值channelFactory对象,当ServerBootstrap启动的时候,就会通过channelFactory创建指定Channel对象(例子中指定的是NioServerSocketChannel,则Netty服务端启动的时候就会创建NioServerSocketChannel对象)
public B channel(Class extends C> channelClass) {
// 实际上,返回对象的类型是ReflectiveChannelFactory
return channelFactory(new ReflectiveChannelFactory(
ObjectUtil.checkNotNull(channelClass, "channelClass")
));
}
public B channelFactory(ChannelFactory extends C> channelFactory) {
ObjectUtil.checkNotNull(channelFactory, "channelFactory");
if (this.channelFactory != null) {
throw new IllegalStateException("channelFactory set already");
}
// 赋值channelFactory对象(实际上是ReflectiveChannelFactory)
this.channelFactory = channelFactory;
return self();
}
// ReflectiveChannelFactory类
public ReflectiveChannelFactory(Class extends T> clazz) {
ObjectUtil.checkNotNull(clazz, "clazz");
try {
// 获取指定类的无参构造器
this.constructor = clazz.getConstructor();
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException("Class " + StringUtil.simpleClassName(clazz) +
" does not have a public non-arg constructor", e);
}
}
配置ServerSocketChannel相关的TCP参数——option()
这个方法就更简单了,其实就是将Netty服务端自定义的TCP参数“塞”进一个Map集合保存下来,当Netty服务端启动之后,就会根据自定义的服务端TCP参数创建ServerScoketChannel。
// ServerSocketChannel相关TCP配置
private final Map, Object> options = new LinkedHashMap, Object>();
public B option(ChannelOption option, T value) {
ObjectUtil.checkNotNull(option, "option");
synchronized (options) {
if (value == null) {
options.remove(option);
} else {
options.put(option, value);
}
}
return self();
}
设置ServerSocketChannel的属性——attr()
这个和上面的option逻辑一样,通过Map集合保存自定义的ServerSocketChannel属性,当服务端启动的时候就会从这个map中取出来创建对应的ServerSocketChannel。
// ServerSocketChannel 的相关属性
private final Map, Object> attrs = new ConcurrentHashMap, Object>();
public B attr(AttributeKey key, T value) {
ObjectUtil.checkNotNull(key, "key");
if (value == null) {
attrs.remove(key);
} else {
attrs.put(key, value);
}
return self();
}
以上都是针对ServerSocketChannel的相关配置,其实和SocketChannel的配置方法逻辑基本一致,所以不再重复赘述...
虽然ServerBootstrap对象的创建和配置源码简单,但只要开发者合理的配置,优化Netty服务端配置,其性能是十分强大的!正是通过这些“简单”的源码,使我更加佩服Netty框架的设计者,通过这么优雅,简单的方式就能够搭建起网络应用程序框架,不再像以前使用nio甚至使用io编程那样,苦恼性能和编码复杂性等问题。这是十分值得后人去学习和研究的!
以上就是创建并初始化ServerBootstrap对象大致流程,接下来就是服务端启动的核心源码,欲知后事如何,请看下篇:【Netty源码系列】服务端启动流程(三)绑定端口并启动
如果觉得文章不错的话,麻烦点个赞哈,你的鼓励就是我的动力!对于文章有哪里不清楚或者有误的地方,欢迎在评论区留言~