概述
ServerBootstrap是Netty提供的一个服务端工具类,通过设置ChanneFactory,ChannelPipelineFactory,用户可以很方便的启动一个服务端。
ServerBootstrap是做什么的
ServerBootstrap是一个帮助类,用来创建服务端的Channel以监听来自客户端的连接。
面向连接
ServerBootstrap只支持面向连接的传输,比如TCP/IP,如果是没有连接的传输,比如UDP/IP这样,就需要使用ConnectionlessBootstrap。
Parent and Child Channel
ServerBootstrap通过_bind()_方法使用属性ChannelFactory来创建服务端的Channel,以监听并且accept客户端的连接。服务端的Channel被称为parent Channel,来自客户端的连接被称为child Channel。
配置Channel
使用Options来设置parent and child Channel的可选参数,其中child Channel需要使用child.前缀。
ServerBootstrap b = ...;
// Options for a parent channel
b.setOption("localAddress", new InetSocketAddress(8080));
b.setOption("reuseAddress", true);
// Options for its children
b.setOption("child.tcpNoDelay", true);
b.setOption("child.receiveBufferSize", 1048576);
设置Channel Pipeline
Netty使用ChannelPipeline来处理和流转IO事件。parent Channel的Pipeline是由ServerBootstrap内部创建的,并且加入Binder的内部类作为ChannelHandler。我们可以设置ServerBootstrap的属性parentHandler,一旦设置之后parentHandler就会被追加到parent Pipeline中用来我们自定义的ChannelHandler。
child Pipeline有两种方式可以设置,一种方式是通过_setPipelineFactory(ChannelPipelineFactory)_设置,每个child Channel都会通过ChannelPipelineFactory创建新的ChannelPipeline,是推荐的方式。另一种方式通过_setPipeline(ChannelPipeline)_设置,这个方法内部会创建ChannelPipelineFactory,复用设置的ChannelPipeline的内部ChannelHandler。
不同配置,不同Channel
ServerBootstrap本身并不是占用资源,而是交给ChannelFactory来处理,官网文档说可以使用同一个ChannelFactory来创建多个ServerBootstrap,每个ServerBootstrap都可以使用不同的配置,从而创建不同的Channel(没见过这种需要。)
ServerBootstrap创建和启动过程
Netty为我们提供ServerBootstrap作为工具来方便的启动服务端,使用起来也很简单,一般分为这几步:
- 创建ChannelFactory
- 创建ChannelPipelineFactory
- 创建ServerBootstrap实例、设置ChannelFactory和ChannelPipelineFactory
- 调用bind方法
1、创建ChannelFactory
这个ChannelFactory在启动时候会创建parent Channel也就是服务的Channel以监听来自客户端的连接。
ChannelFactory是用来创建parent Channel
2、创建ChannelPipelineFactory
在accept Channel之后会使用这个ChannelPipelineFactory创建ChannelPipeline用来处理IO事件。常见的使用方法是使用_Channels.pipeline()_创建一个DefaultChannelPipeline,我们往里添加自定义的ChannelHandler即可。
ChannelPipelineFactory是用来服务child Channel
3、设置ChannelFactory和ChannelPipelineFactory
创建ServerBootstrap实例,设置ChannelFactory和ChannelPipelineFactory属性。
4、调用_bind()_方法启动,绑定在指定端口。
官网示例(也是常用使用方式,Dubbo就是这样的,设计的简约而不简单)
// Configure the server.
ServerBootstrap bootstrap = new ServerBootstrap(
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
// Set up the pipeline factory.
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
return Channels.pipeline(new EchoServerHandler());
}
});
// Bind and start to accept incoming connections.
bootstrap.bind(new InetSocketAddress(port));
bind()绑定过程
ServerBootstrap通过调用_bind()_方法来启动服务端。绑定方法很简单,首先创建parent的ChannelPipeline,然后使用ChannelFactory创建Channel,结束。
public Channel bind(final SocketAddress localAddress) {
ChannelFuture future = bindAsync(localAddress);
// Wait for the future.
future.awaitUninterruptibly();
if (!future.isSuccess()) {
future.getChannel().close().awaitUninterruptibly();
throw new ChannelException("Failed to bind to: " + localAddress, future.getCause());
}
return future.getChannel();
}
public ChannelFuture bindAsync(final SocketAddress localAddress) {
if (localAddress == null) {
throw new NullPointerException("localAddress");
}
// 设置bossPipeline
Binder binder = new Binder(localAddress);
ChannelHandler parentHandler = getParentHandler();
ChannelPipeline bossPipeline = pipeline();
bossPipeline.addLast("binder", binder);
if (parentHandler != null) {
bossPipeline.addLast("userHandler", parentHandler);
}
// 创建parent Channel
Channel channel = getFactory().newChannel(bossPipeline);
final ChannelFuture bfuture = new DefaultChannelFuture(channel, false);
binder.bindFuture.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
bfuture.setSuccess();
} else {
// Call close on bind failure
bfuture.getChannel().close();
bfuture.setFailure(future.getCause());
}
}
});
return bfuture;
}
分析方法过程:
- 创建parent ChannelPipeline,添加Binder、外部设置的parentChannelHandler,这个Pipeline是给parent Channel服务的
- 使用ChannelFactory创建Channel,是parent Channel
Binder类是ServerBootstrap内部类,是一个UpstreamHandler,处理三个事件:channelOpen、childChannelOpen、exceptionCaught。
NioServerSocketChannelFactory创建NioServerSocketChannel,在NioServerSocketChannel的构造函数中会触发一个ChannelOpen的事件传入Pipeline中,这个Pipeline就是在ServerBootstrap的_bind()_方法里创建的parent Pipeline。而ChannelOpen是一个UpstreamEvent,因此Binder类就会执行相应的逻辑。
关键代码
// NioServerSocketChannel的构造函数中执行
socket = ServerSocketChannel.open();
socket.configureBlocking(false);
fireChannelOpen(this);
// Binder类的channelOpen方法,最后执行channel的bind方法
evt.getChannel().bind(localAddress).addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
bindFuture.setSuccess();
} else {
bindFuture.setFailure(future.getCause());
}
}
});
// NioServerSocketChannel的bind方法
channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent(channel, future, ChannelState.BOUND, localAddress));
// NioServerSocketPipelineSink,调用NioServerBoss的bind方法
case BOUND:
if (value != null) {
((NioServerBoss) channel.boss).bind(channel, future, (SocketAddress) value);
} else {
((NioServerBoss) channel.boss).close(channel, future);
}
break;
// NioServerBoss中创建RegisterTask
void bind(final NioServerSocketChannel channel, final ChannelFuture future,
final SocketAddress localAddress) {
registerTask(new RegisterTask(channel, future, localAddress));
}
// RegisterTask中执行nio的bind和注册到Selector,属性的nio知识点
channel.socket.socket().bind(localAddress, channel.getConfig().getBacklog());
bound = true;
future.setSuccess();
fireChannelBound(channel, channel.getLocalAddress());
channel.socket.register(selector, SelectionKey.OP_ACCEPT, channel);
流程分析:
- NioServerSocketChannel构造方法创建Java nio的ServerSocketChannel;
- NioServerSocketChannel构造方法中触发OPEN的UpstreamEvent;
- Binder类中_channelOpen()_方法中调用Channel的_bind()_方法;
- NioServerSocketChannel的_bind()_方法触发一个BOUND的DownstreamEvent;
- NioServerSocketPipelineSink,调用NioServerBoss的_bind()_方法;
- NioServerBoss中创建RegisterTask,RegisterTask是一个Runnable对象,_run()_方法中执行NIO的相关操作,真正处理IO的地方
- ServerSocketChannel的bind方法,ServerSocketChannel注册到Selector,事件是OP_ACCEPT
总体的看来,ServerBootstrap内部将任务交给了Binder和ChannelFactory,进一步说其实是Channel来处理。通过一系列的事件触发,最终调用JDK NIO的ServerSocketChannel完成启动并注册监听。
ServerBootstrap没做什么,都是ServerChannelFactory在处理,想要理清楚Netty的服务端,就得剖析NioServerSocketChannelFactory。
思考一下
为什么不直接在ServerBootstrap的bind方法里直接执行Channel的bind方法呢?我觉得是想把复杂的逻辑剥离出来到Binder这个内部类里。may be