Netty in Action - Bootstrap

Bootstrap类型

Bootstrap(引导) 是 Netty 中配置程序的过程,当你需要连接客户端或服务器绑定指定端口时需要使用 Bootstrap。

如前面所述,Bootstrap 有两种类型,一种是用于客户端的Bootstrap,一种是用于服务端的ServerBootstrap。不管程序使用哪种协议,无论是创建一个客户端还是服务器都需要使用“引导”。

面向连接 vs. 无连接

请记住,这个讨论适用于 TCP 协议,它是“面向连接”的。这样协议保证该连接的端点之间的消息的有序输送。无连接协议发送的消息,无法保证顺序和成功性

两种 Bootstrap之间有一些相似之处,也有一些不同。Bootstrap 和 ServerBootstrap 之间的差异如下:

Table 3.1 Comparison of Bootstrap classes

分类BootstrapServerBootstrap
网络功能连接到远程主机和端口绑定本地端口
EventLoopGroup 数量12

Bootstrap用来连接远程主机,有1个EventLoopGroup

ServerBootstrap用来绑定本地端口,有2个EventLoopGroup

事件组(Groups),传输(transports)和处理程序(handlers)分别在本章后面讲述,我们在这里只讨论两种"引导"的差异(Bootstrap和ServerBootstrap)。第一个差异很明显,“ServerBootstrap”监听在服务器监听一个端口轮询客户端的“Bootstrap”或DatagramChannel是否连接服务器。通常需要调用“Bootstrap”类的connect()方法,但是也可以先调用bind()再调用connect()进行连接,之后使用的Channel包含在bind()返回的ChannelFuture中。

一个 ServerBootstrap 可以认为有2个 Channel 集合,第一个集合包含一个单例 ServerChannel,代表持有一个绑定了本地端口的 socket;第二集合包含所有创建的 Channel,处理服务器所接收到的客户端进来的连接。下图形象的描述了这种情况:

Figure 3.2 Server with two EventLoopGroups

与 ServerChannel 相关 EventLoopGroup 分配一个 EventLoop 是负责创建 Channels 用于传入的连接请求。一旦连接接受,第二个EventLoopGroup 分配一个 EventLoop 给它的 Channel。


引导客户端和无连接协议

当需要引导客户端或一些无连接协议时,需要使用Bootstrap类。在本节中,我们将回顾可用的各种方法引导客户端,引导线程,和可用的管道实现。

客户端引导方法

下表是 Bootstrap 的常用方法,其中很多是继承自 AbstractBootstrap。

Table 9.1 Bootstrap methods

名称描述
group设置 EventLoopGroup 用于处理所有的 Channel 的事件
channel channelFactorychannel() 指定 Channel 的实现类。如果类没有提供一个默认的构造函数,你可以调用 channelFactory() 来指定一个工厂类被 bind() 调用。
localAddress指定应该绑定到本地地址 Channel。如果不提供,将由操作系统创建一个随机的。或者,您可以使用 bind() 或 connect()指定localAddress
option设置 ChannelOption 应用于 新创建 Channel 的 ChannelConfig。这些选项将被 bind 或 connect 设置在通道,这取决于哪个被首先调用。这个方法在创建管道后没有影响。所支持 ChannelOption 取决于使用的管道类型。请参考9.6节和 ChannelConfig 的 API 文档 的 Channel 类型使用。
attr这些选项将被 bind 或 connect 设置在通道,这取决于哪个被首先调用。这个方法在创建管道后没有影响。请参考9.6节。
handler设置添加到 ChannelPipeline 中的 ChannelHandler 接收事件通知。
clone创建一个当前 Bootstrap的克隆拥有原来相同的设置。
remoteAddress设置远程地址。此外,您可以通过 connect() 指定
connect连接到远端,返回一个 ChannelFuture, 用于通知连接操作完成
bind将通道绑定并返回一个 ChannelFuture,用于通知绑定操作完成后,必须调用 Channel.connect() 来建立连接。

如何引导客户端

Bootstrap 类负责创建管道给客户或应用程序,利用无连接协议和在调用 bind() 或 connect() 之后。

下图展示了如何工作

  1. 当 bind() 调用时,Bootstrap 将创建一个新的管道, 当 connect() 调用在 Channel 来建立连接
  2. Bootstrap 将创建一个新的管道, 当 connect() 调用时
  3. 新的 Channel

Figure 9.2 Bootstrap process

下面演示了引导客户端,使用的是 NIO TCP 传输

Listing 9.1 Bootstrapping a client

EventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap(); //1
bootstrap.group(group) //2
    .channel(NioSocketChannel.class) //3
    .handler(new SimpleChannelInboundHandler<ByteBuf>() { //4
        @Override
        protected void channeRead0(
            ChannelHandlerContext channelHandlerContext,
            ByteBuf byteBuf) throws Exception {
                System.out.println("Received data");
                byteBuf.clear();
            }
        });
ChannelFuture future = bootstrap.connect(
    new InetSocketAddress("www.manning.com", 80)); //5
future.addListener(new ChannelFutureListener() {
    @Override
    public void operationComplete(ChannelFuture channelFuture)
        throws Exception {
            if (channelFuture.isSuccess()) {
                System.out.println("Connection established");
            } else {
                System.err.println("Connection attempt failed");
                channelFuture.cause().printStackTrace();
            }
        }
    });
  1. 创建一个新的 Bootstrap 来创建和连接到新的客户端管道
  2. 指定 EventLoopGroup
  3. 指定 Channel 实现来使用
  4. 设置处理器给 Channel 的事件和数据
  5. 连接到远端主机

注意 Bootstrap 提供了一个“流利”语法——示例中使用的方法(除了connect()) 由 Bootstrap 返回实例本身的引用链接他们。

兼容性

Channel 的实现和 EventLoop 的处理过程在 EventLoopGroup 中必须兼容,哪些 Channel 是和 EventLoopGroup 是兼容的可以查看 API 文档。经验显示,相兼容的实现一般在同一个包下面,例如使用NioEventLoop,NioEventLoopGroup 和 NioServerSocketChannel 在一起。请注意,这些都是前缀“Nio”,然后不会用这些代替另一个实现和另一个前缀,如“Oio”,也就是说 OioEventLoopGroup 和NioServerSocketChannel 是不相容的。

Channel 和 EventLoopGroup 的 EventLoop 必须相容,例如NioEventLoop、NioEventLoopGroup、NioServerSocketChannel是相容的,但是 OioEventLoopGroup 和 NioServerSocketChannel 是不相容的。从类名可以看出前缀是“Nio”的只能和“Nio”的一起使用。

EventLoop 和 EventLoopGroup

记住,EventLoop 分配给该 Channel 负责处理 Channel 的所有操作。当你执行一个方法,该方法返回一个 ChannelFuture ,它将在 分配给 Channel 的 EventLoop 执行。

EventLoopGroup 包含许多 EventLoops 和分配一个 EventLoop 通道时注册。我们将在15章更详细地讨论这个话题。

清单9.2所示的结果,试图使用一个 Channel 类型与一个 EventLoopGroup 兼容。

Listing 9.2 Bootstrap client with incompatible EventLoopGroup

EventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap(); //1
bootstrap.group(group) //2
    .channel(OioSocketChannel.class) //3
    .handler(new SimpleChannelInboundHandler<ByteBuf>() { //4
        @Override
        protected void channelRead0(
            ChannelHandlerContext channelHandlerContext,
                    ByteBuf byteBuf) throws Exception {
                System.out.println("Reveived data");
                byteBuf.clear();
            }
        });
ChannelFuture future = bootstrap.connect(
    new InetSocketAddress("www.manning.com", 80)); //5
future.syncUninterruptibly();
  1. 创建新的 Bootstrap 来创建新的客户端管道
  2. 注册 EventLoopGroup 用于获取 EventLoop
  3. 指定要使用的 Channel 类。通知我们使用 NIO 版本用于EventLoopGroup , OIO 用于 Channel
  4. 设置处理器用于管道的 I/O 事件和数据
  5. 尝试连接到远端。当 NioEventLoopGroup 和 OioSocketChannel 不兼容时,会抛出 IllegalStateException 异常

IllegalStateException 显示如下:

Listing 9.3 IllegalStateException thrown because of invalid configuration

Exception in thread "main" java.lang.IllegalStateException: incompatible event loop
type: io.netty.channel.nio.NioEventLoop
at
io.netty.channel.AbstractChannel$AbstractUnsafe.register(AbstractChannel.java:5
71)
...

出现 IllegalStateException 的其他情况是,在 bind() 或 connect() 调用前 调用需要设置参数的方法调用失败时,包括:

  • group()
  • channel() 或 channnelFactory()
  • handler()

handler() 方法尤为重要,因为这些 ChannelPipeline 需要适当配置。一旦提供了这些参数,应用程序将充分利用 Netty 的能力。


引导服务器

服务器的引导共用了客户端引导的一些逻辑。

引导服务器的方法

下表显示了 ServerBootstrap 的方法

Table 9.2 Methods of ServerBootstrap‘

名称描述
group设置 EventLoopGroup 用于 ServerBootstrap。这个 EventLoopGroup 提供 ServerChannel 的 I/O 并且接收 Channel
channel channelFactorychannel() 指定 Channel 的实现类。如果管道没有提供一个默认的构造函数,你可以提供一个 ChannelFactory。
localAddress指定 ServerChannel 实例化的类。如果不提供,将由操作系统创建一个随机的。或者,您可以使用 bind() 或 connect()指定localAddress
option指定一个 ChannelOption 来用于新创建的 ServerChannel 的 ChannelConfig 。这些选项将被设置在管道的 bind() 或 connect(),这取决于谁首先被调用。在此调用这些方法之后设置或更改 ChannelOption 是无效的。所支持 ChannelOption 取决于使用的管道类型。请参考9.6节和 ChannelConfig 的 API 文档 的 Channel 类型使用。
childOption当管道已被接受,指定一个 ChannelOption 应用于 Channel 的 ChannelConfig。
attr指定 ServerChannel 的属性。这些属性可以被 管道的 bind() 设置。当调用 bind() 之后,修改它们不会生效。
childAttr应用属性到接收到的管道上。后续调用没有效果。
handler设置添加到 ServerChannel 的 ChannelPipeline 中的 ChannelHandler。 具体详见 childHandler() 描述
childHandler设置添加到接收到的 Channel 的 ChannelPipeline 中的 ChannelHandler。handler() 和 childHandler()之间的区别是前者是接收和处理ServerChannel,同时 childHandler() 添加处理器用于处理和接收 Channel。后者代表一个套接字绑定到一个远端。
clone克隆 ServerBootstrap 用于连接到不同的远端,通过设置相同的原始 ServerBoostrap。
bind绑定 ServerChannel 并且返回一个 ChannelFuture,用于 通知连接操作完成了(结果可以是成功或者失败)

如何引导一个服务器

ServerBootstrap 中的 childHandler(), childAttr() 和 childOption() 是常用的服务器应用的操作。具体来说,ServerChannel实现负责创建子 Channel,它代表接受连接。因此 引导 ServerChannel 的 ServerBootstrap ,提供这些方法来简化接收的 Channel 对 ChannelConfig 应用设置的任务。

图9.3显示了 ServerChannel 创建 ServerBootstrap 在 bind(),后者管理大量的子 Channel。

  1. 当调用 bind() 后 ServerBootstrap 将创建一个新的管道,这个管道将会在绑定成功后接收子管道
  2. 接收新连接给每个子管道
  3. 接收连接的 Channel

Figure 9.3 ServerBootstrap

记住 child* 的方法都是操作在子的 Channel,被 ServerChannel 管理。

清单9.4 ServerBootstrap 时会创建一个 NioServerSocketChannel实例 bind() 。这个 NioServerChannel 负责接受新连接和创建NioSocketChannel 实例。

Listing 9.4 Bootstrapping a server

NioEventLoopGroup group = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap(); //1
bootstrap.group(group) //2
    .channel(NioServerSocketChannel.class) //3
    .childHandler(new SimpleChannelInboundHandler<ByteBuf>() { //4
        @Override
        protected void channelRead0(ChannelHandlerContext ctx,
            ByteBuf byteBuf) throws Exception {
                System.out.println("Reveived data");
                byteBuf.clear();
            }
        }
    );
ChannelFuture future = bootstrap.bind(new InetSocketAddress(8080)); //5
future.addListener(new ChannelFutureListener() {
    @Override
    public void operationComplete(ChannelFuture channelFuture)
        throws Exception {
            if (channelFuture.isSuccess()) {
                System.out.println("Server bound");
            } else {
                System.err.println("Bound attempt failed");
                channelFuture.cause().printStackTrace();
            }
        }
    }
);
  1. 创建要给新的 ServerBootstrap 来创建新的 SocketChannel 管道并绑定他们
  2. 指定 EventLoopGroup 用于从注册的 ServerChannel 中获取EventLoop 和接收到的管道
  3. 指定要使用的管道类
  4. 设置子处理器用于处理接收的管道的 I/O 和数据
  5. 通过配置引导来绑定管道


转载自:Netty in Action 《Netty 实战(精髓)》


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值