Netty3之ClientBootstrap分析

概述

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);
}

分析方法过程:

  1. 使用PipelineFactory创建client ChannelPipeline;
  2. 使用ChannelFactory创建Channel,是client Channel;
  3. 调用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);
...

流程分析:

  1. NioClientSocketChannel构造函数,创建Java nio的SocketChannel;
  2. NioClientSocketChannel的connect()方法触发一个CONNECTED的DownstreamEvent;
  3. NioClientSocketPipelineSink,执行NIO SocketChannel的connect方法,有两种情况:
    1. connect立即成功
    2. connect没有立即成功
  4. connect立即成功,创建NioWorker的RegisterTask
  5. connect没有立即成功
  6. NioWorker中创建RegisterTask,RegisterTask是一个Runnable对象,run()方法中执行NIO的相关操作,真正处理IO的地方
  7. RegisterTask中,NIO SocketChannel注册到Selector

总体的看来,ServerBootstrap内部将任务交给了Binder和ChannelFactory,进一步说其实是Channel来处理。通过一系列的事件触发,最终调用JDK NIO的ServerSocketChannel完成启动并注册监听。

ServerBootstrap没做什么,都是ServerChannelFactory在处理,想要理清楚Netty的服务端,就得剖析NioServerSocketChannelFactory。

思考一下

为什么不直接在ServerBootstrap的bind方法里直接执行Channel的bind方法呢?我觉得是想把复杂的逻辑剥离出来到Binder这个内部类里。may be

相关阅读

NioServerSocketChannelFactory分析

转载于:https://my.oschina.net/cregu/blog/2963839

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值