一个 Netty 应用通常由一个 Bootstrap 开始,主要作用是配置整个 Netty 程序,串联各个组件,而客户端和服务端这两种应用程序间通用的引导步骤有AbstractBootstrap处理,Netty 中 Bootstrap 类是客户端程序的启动引导类,ServerBootstrap 是服务端启动引导类;
引导类的层次结构

为何引导类要实现Cloneable接口?
有时可能会需要创建多个具有类似配置或者完全相同配置的Channel(如链式调用那里添加配置);为了支持这种模式而又不需要为每个Channel都创建并配置一个新的引导类实例,AbstractBootstrap被标记为Cloneable的引导类实例;
注意,这种方式只会创建引导类实例的EventLoopGroup的一个浅拷贝,所以被浅拷贝的EventLoopGroup将在所有克隆的Channel实例之间共享;这是可以接受的,因为通常这些克隆的Channel的生命周期都很短暂;如创建一个Channel以进行一次HTTP请求;
![]()
对于AbstractBootstrap的子类型 B 是其父类型的一个类型参数,因此可以返回到运行时实例的引用以支持方法的链式调用(也就是所谓的流式语法);
引导客户端
Bootstrap 类负责为客户端和使用无连接协议的应用程序创建 Channel;
使用如下:
public class NettyClient {
public static void main(String[] args) {
final int port = 19000;
final String local = "127.0.0.1";
EventLoopGroup group = new NioEventLoopGroup();
try {
// 创建一个BootStrap类的实例以创建和连接新的Channel
Bootstrap bootstrap = new Bootstrap();
// 链式写法
// 设置EventLoopGroup,提供用于处理Channel事件的EventLoop
bootstrap.group(group)
// 指定要使用的Channel实现
.channel(NioSocketChannel.class)
// 设置用于用于处理Channel事件的Handler
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new NettyClientHandler());
}
});
// 连接服务端
ChannelFuture channelFuture = bootstrap.connect(local, port).sync();
channelFuture.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
group.shutdownGracefully();
}
}
}
除了 connect()方法以外, BootStrap的其他方法将通过每次方法调用所返回Bootstrap 实例的引用;
在引导的过程中,在调用 bind()或者 connect()方法之前,必须调用以下方法来设置所需的组件(如果不这样做, 则将会导致 IllegalStateException):
- group();
- channel()或者 channelFactory();
- handler();需要通过该方法配置 ChannelPipeline;
引导服务器
ServerBootstrap 是服务端启动引导类;
ServerBootstrap 在 bind()方法被调用时创建了一个 ServerChannel,并且该 ServerChannel 管理了多个子 Channel;

ServerChannel的实现负责创建子 Channel,这些子Channel 代表了已被接受的连接(已连接的客户端);
负责引导 ServerChannel 的 ServerBootstrap 提供了的方法,以简化将设置应用到已被接受的子Channel的ChannelConfig 的任务;
使用如下:
public class NettyServer {
private final static Logger logger = LoggerFactory.getLogger(NettyServer.class);
public static void main(String[] args) {
// 创建两个线程组bossGroup和workerGroup,含有子线程NioEventLoop的个数默认为cpu核数的两倍
// bossGroup只处理连接请求,业务处理交由wokerGroup
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
final int port = 19000;
try {
// 创建服务的启动对象
ServerBootstrap bootstrap = new ServerBootstrap();
// 设置两个线程组
bootstrap.group(bossGroup, workerGroup)
// 指定使用NioServerSocketChannel作为服务器的通道实现
.channel(NioServerSocketChannel.class)
// 初始化服务器连接队列大小,服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接
// 多个客户端同时来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理
.option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChannelInitializer<SocketChannel>() {
//创建通道初始化对象,设置初始化参数
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//对workerGroup的SocketChannel设置处理器codec
ch.pipeline().addLast(new NettyServerHandler());
}
});
logger.info("netty server start...");
// 通过配置好ServerBootStrap的实例绑定该Channel
// 启动服务器(并绑定端口),bind是异步操作,sync方法是等待异步操作执行完毕,这里会阻塞住
ChannelFuture channelFuture = bootstrap.bind(port).sync();
// 添加监听器
channelFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
logger.info("监听端口" + port + "成功");
} else {
logger.info("监听失败...");
}
}
});
channelFuture.channel().closeFuture().sync();
} catch (Exception e) {
logger.error("异常..", e);
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
如果服务器正在处理一个客户端的请求,这个请求需要它充当第三方系统的客户端;
当一个应用程序(如一个代理服务器)必须要和组织现有的系统(如 Web 服务或者数据库)集成时,就可能发生这种情况; 在这种情况下,将需要从已经被接受的子 Channel 中引导一个客户端 Channel;
如果创建新的BootStrap实例,并且每个新创建的客户端 Channel 定义另一个 EventLoop,这会产生额外的线程,以及在已被接受的子 Channel 和客户端 Channel 之间交换数据时不可避免的上下文切换;
通过将已被接受的子 Channel(已连接的客户端) 的 EventLoop 传递给 Bootstrap的 group()方法来共享该 EventLoop;因为分配给 EventLoop 的所有 Channel 都使用同一个线程,所以这避免了额外的线程创建和Channel之间数据交换时的上下文切换;如下图;

写法如下:
