概述
ClientBootstrap是Netty提供的一个客户端工具类,通过设置ChanneFactory,ChannelPipelineFactory,用户可以很方便的启动一个客户端。
ClientBootstrap是做什么的
和ServerBootstrap一样,ClientBootstrap也是是一个帮助类,用来创建客户端的Channel以用来连接到服务端。
配置Channel
使用Options来设置client Channel的可选参数,其中child Channel需要使用child.前缀。
ClientBootstrap b = ...;
// Options for a new channel
b.setOption("remoteAddress", new InetSocketAddress("example.com", 8080));
b.setOption("tcpNoDelay", true);
b.setOption("receiveBufferSize", 1048576);
设置Channel Pipeline
Netty使用ChannelPipeline来处理和流转IO事件。和ServerBootstrap一样ClientBootstrap Pipeline有两种方式可以设置,一种方式是通过setPipelineFactory(ChannelPipelineFactory)设置,每个child Channel都会通过ChannelPipelineFactory创建新的ChannelPipeline,是推荐的方式。另一种方式通过setPipeline(ChannelPipeline)设置,这个方法内部会创建ChannelPipelineFactory,复用设置的ChannelPipeline的内部ChannelHandler。
不同配置,不同Channel
ClientBootstrap本身并不是占用资源,而是交给ChannelFactory来处理,官网文档说可以使用同一个ChannelFactory来创建多个ClientBootstrap,每个ClientBootstrap都可以使用不同的配置,从而创建不同的Channel(没见过这种需要。)
总结
ServerBootstrap和ClientBootstrap是一样的,都是由对象的ChannelFactory来处理负责的逻辑,通过调用ServerBootstrap的bind()启动服务端绑定一个端口,ClientBootstrap的connect()来连接服务端。
ClientBootstrap创建和启动过程
Netty为我们提供ClientBootstrap作为工具来方便的启动客户端,使用起来也很简单,一般分为几步:
- 创建ChannelFactory
- 创建ChannelPipelineFactory
- 创建ClientBootstrap实例、设置ChannelFactory和ChannelPipelineFactory
- 调用connect方法
1、创建ChannelFactory
这个ChannelFactory在connect()方法中会创建client Channel也就是客户端的Channel去连接到远程地址。
2、创建ChannelPipelineFactory
在创建client Channel时候使用这个ChannelPipelineFactory创建ChannelPipeline用来处理IO事件。常见的使用方法是使用Channels.pipeline()创建一个DefaultChannelPipeline,我们往里添加自定义的ChannelHandler即可。
ChannelFactory和ChannelPipelineFactory都是用来服务client Channel
3、设置ChannelFactory和ChannelPipelineFactory
创建ClientBootstrap实例,设置ChannelFactory和ChannelPipelineFactory属性。
4、调用connect()方法启动,连接到远程地址端口。
官网示例(也是常用使用方式,Dubbo就是这样的,设计的简约而不简单)
// Configure the client.
ClientBootstrap bootstrap = new ClientBootstrap(
new NioClientSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
// Set up the pipeline factory.
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
return Channels.pipeline(
new EchoClientHandler(firstMessageSize));
}
});
// Start the connection attempt.
ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port));
connect()绑定过程
ClientBootstrap通过调用connect()方法来启动客户端并且连接到远程地址。connect()方法很简单,就是使用ChannelFactory创建Channel,然后调用client Channel的connect()方法。
public ChannelFuture connect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
if (remoteAddress == null) {
throw new NullPointerException("remoteAddress");
}
ChannelPipeline pipeline;
try {
pipeline = getPipelineFactory().getPipeline();
} catch (Exception e) {
throw new ChannelPipelineException("Failed to initialize a pipeline.", e);
}
// Set the options.
Channel ch = getFactory().newChannel(pipeline);
boolean success = false;
try {
ch.getConfig().setOptions(getOptions());
success = true;
} finally {
if (!success) {
ch.close();
}
}
// Bind.
if (localAddress != null) {
ch.bind(localAddress);
}
// Connect.
return ch.connect(remoteAddress);
}
分析方法过程:
- 使用PipelineFactory创建client ChannelPipeline;
- 使用ChannelFactory创建Channel,是client Channel;
- 调用client Channel的connect();
关键代码
// NioClientSocketChannel的构造函数
SocketChannel socket;
try {
socket = SocketChannel.open();
} catch (IOException e) {
throw new ChannelException("Failed to open a socket.", e);
}
...
socket.configureBlocking(false);
...
}
...
fireChannelOpen(this);
...
// ClientBootstrap的connect方法
ch.connect(remoteAddress)
// NioClientSocketChannel的connect方法
channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent(channel, future, ChannelState.CONNECTED, remoteAddress));
// NioClientSocketPipelineSink
...
if (channel.channel.connect(remoteAddress)) {
channel.worker.register(channel, cf);
}
...
// NioWorker的register会先创建RegisterTask
protected Runnable createRegisterTask(Channel channel, ChannelFuture future) {
boolean server = !(channel instanceof NioClientSocketChannel);
return new RegisterTask((NioSocketChannel) channel, future, server);
}
// RegisterTask会执行nio的逻辑,注册到Selector
...
channel.channel.register(selector, channel.getRawInterestOps(), channel);
...
流程分析:
- NioClientSocketChannel构造函数,创建Java nio的SocketChannel;
- NioClientSocketChannel的connect()方法触发一个CONNECTED的DownstreamEvent;
- NioClientSocketPipelineSink,执行NIO SocketChannel的connect方法,有两种情况:
- connect立即成功
- connect没有立即成功
- connect立即成功,创建NioWorker的RegisterTask
- connect没有立即成功,
- NioWorker中创建RegisterTask,RegisterTask是一个Runnable对象,run()方法中执行NIO的相关操作,真正处理IO的地方
- RegisterTask中,NIO SocketChannel注册到Selector
总体的看来,ServerBootstrap内部将任务交给了Binder和ChannelFactory,进一步说其实是Channel来处理。通过一系列的事件触发,最终调用JDK NIO的ServerSocketChannel完成启动并注册监听。
ServerBootstrap没做什么,都是ServerChannelFactory在处理,想要理清楚Netty的服务端,就得剖析NioServerSocketChannelFactory。
思考一下
为什么不直接在ServerBootstrap的bind方法里直接执行Channel的bind方法呢?我觉得是想把复杂的逻辑剥离出来到Binder这个内部类里。may be
相关阅读
NioServerSocketChannelFactory分析