Netty Bootstrapping

本章将介绍:

  • 引导 clients 和 servers
  • 在一个 Channel 里面引导 clients
  • 添加 ChannelHandlers
  • 使用 ChannelOptions 和 attributes

深入研究了ChannelPipeline,ChannelHandler和编解码器类(codec classes)后,您的下一个问题可能是“所有这些部分如何与工作应用程序相结合?”

答案? “Bootstrapping。”到目前为止,我们使用的术语有些模糊,现在是时候更准确地定义它了。 简单地说,引导应用程序是将其配置为运行的过程 - 尽管过程的细节可能不像其定义那么简单,尤其是在网络应用程序中。

与其应用程序体系结构方法一致,Netty以一种将应用程序(无论是客户端还是服务器)与网络层隔离开来的方式处理引导。 如您所见,所有框架组件都在后台连接并启用。 Bootstrapping是我们拼图中缺失的部分正在组装; 当你把它放到位时,你的Netty应用程序就完成了。

Figure 8.1 Bootstrapping class hierarchy

8.1 Bootstrap classes

bootstrapping类层次结构由一个抽象父类和两个具体的bootstrap子类组成,如图8.1所示。

不要将具体类视为服务器和客户端引导,而是要记住它们打算支持的独特应用程序功能。即,服务器将父通道用于接受来自客户端的连接并创建用于与之交谈的子通道。 它们,而客户端很可能只需要一个非父通道来进行所有网络交互。 (正如我们将看到的,这也适用于无连接传输,例如UDP,因为它们不需要每个连接都有一个通道。)

我们在前几章中研究过的几个Netty组件都参与了自举过程,其中一些组件在客户端和服务器中都有使用。 两种应用程序类型共同的启动步骤由 AbstractBootstrap 处理,而特定于客户端或服务器的引导步骤分别由 BootstrapServerBootstrap处理。

在本章的其余部分,我们将详细探讨这两个类,从较不复杂的Bootstrap开始。

Why are the bootstrap classes Cloneable?
您有时需要创建具有相似或相同设置的多个频道。 为了支持此模式而不需要为每个通道创建和配置新的引导实例,AbstractBootstrap已标记为**Cloneable**。 在已配置的引导程序上调用 clone() 将返回另一个可立即使用的引导程序实例。

请注意,这只会创建bootstrap的EventLoopGroup的浅表副本,因此后者将在所有克隆的通道之间共享。 这是可以接受的,因为克隆的通道通常是短暂的,典型的情况是创建用于发出HTTP请求的通道。

AbstractBootstrap的完整声明是

public abstract class AbstractBootstrap <B extends AbstractBootstrap<B, C>, C extends Channel>

在此签名中,子类B是超类的类型参数,因此可以返回对运行时实例的引用以支持方法链接(所谓的流畅语法)。

子类声明如下:

public class Bootstrap extends Abstrap<Bootstrap, Channel>

public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel>

8.2 Bootstrapping clients and connectionless protocols

Bootstrap用于客户端或使用无连接协议的应用程序中。表8.1给出了该类的概述,其中许多方法都是从中继承的AbstractBootstrap。

Bootstrapping process

以下列表中的代码引导使用NIO TCP传输的客户端。
Bootstrapping a client
此示例使用前面提到的流畅语法; 方法(connect()除外)由每个返回的Bootstrap实例的引用链接。

8.2.2 Channel and EventLoopGroup compatibility

以下目录列表来自包io.netty.channel。 您可以从包名称和匹配的类名称前缀中看到,NIOOIO 传输都有相关的 EventLoopGroupChannel 实现。
Listing 8.2 Compatible EventLoopGroups and Channels
必须保持这种兼容性; 您不能混合具有不同前缀的组件,例如NioEventLoopGroup和OioSocketChannel。 以下列表显示了尝试做到这一点。

在这里插入图片描述

此代码将导致IllegalStateException,因为它混合了不兼容的传输:
Exception

More on IllegalStateException
当引导程序(bootstrapping)的时候,在你调用 bind() 或者 connect() 之前,你必须按顺序调用下面的方法,以设置好必须的组件(components).

  • group()
  • channel() or channelFactory()
  • handler()
    如果不这样做将导致IllegalStateException。 handler()调用特别重要,因为需要配置ChannelPipeline。

8.3 Bootstrapping servers

我们将通过ServerBootstrap API概述开始概述服务器引导。 然后,我们将研究引导服务器所涉及的步骤,以及一些相关主题,包括从服务器通道引导客户端的特殊情况。

8.3.1 The ServerBootstrap class

表 8.2 列出了 ServerBootstrap 的方法。

Methods of the ServerBootstrap class

下一节将介绍服务器引导的详细信息。

8.3.2 Bootstrapping a server

您可能已经注意到表8.2列出了表8.1中没有的几种方法:childHandler(),childAttr() 和childOption()。 这些调用支持服务器应用程序的典型操作。 具体来说,ServerChannel 实现负责创建表示已接受连接的子通道。 因此,引导 ServerChannel 的 ServerBootstrap 提供了这些方法,以简化将设置应用于接受的 Channel 的 ChannelConfig 成员的任务。

图8.3显示了在 bind() 上创建 ServerChannel 的 ServerBootstrap,以及管理多个子Channel的ServerChannel。

ServerBootstrap and ServerChannel
此清单中的代码实现了图8.3中所示的服务器引导。
Listing 8.4 Bootstrapping a server

8.4 Bootstrapping clients from a Channel

假设您的服务器正在处理客户端请求,该请求要求它充当第三个系统的客户端。当代理服务器等应用程序必须与组织的现有系统(如Web服务或数据库)集成时,就会发生这种情况。在这种情况下,您需要从 ServerChannel 引导客户端 Channel。
您可以按照8.2.1节中的描述创建一个新的 Bootstrap,但这不是最有效的解决方案,因为它需要您为新的客户端 Channel 定义另一个 EventLoop。这将产生额外的线程,当在接受的信道和客户端信道之间交换数据时需要上下文切换。更好的解决方案是通过将其传递给Bootstrap 的 group() 方法来共享接受的 Channel 的 EventLoop。因为分配给 EventLoop 的所有 Channel 都使用相同的线程,所以这避免了前面提到的额外线程创建和相关的上下文切换。该共享解决方案如图8.4所示。

Figure 8.4 EventLoop shared between channels

实现 EventLoop 共享涉及通过调用 group() 方法设置 EventLoop,如下面的清单所示。
Bootstrapping a server
我们在本节中讨论的主题和提供的解决方案反映了编码Netty应用程序的一般准则:尽可能重用EventLoop以降低线程创建的成本。

8.5 Adding multiple ChannelHandlers during a bootstrap

在我们展示的所有代码示例中,我们在引导过程中调用了 handler() 或 childHandler() 来添加单个 ChannelHandler。 对于简单的应用程序来说,这可能就足够了,但它不能满足更复杂的应用程序的需求。 例如,一个必须支持多个协议的应用程序将拥有许多ChannelHandler,另一种选择是一个庞大而笨重的类。

正如您多次看到的那样,您可以通过在 ChannelPipeline 中将它们链接在一起来根据需要部署尽可能多的 ChannelHandler。 但是,如果在引导过程中只能设置一个ChannelHandler,那么如何才能这样做呢?对于这个用例,Netty提供了ChannelInboundHandlerAdapter 的特殊子类,

public abstract class ChannelInitializer<C extends Channel> 
              extends ChannelInboundHandlerAdapter

它定义了以下方法:

protected abstract void initChannel(C ch) throws Exception;

此方法提供了一种将多个 ChannelHandler 添加到 ChannelPipeline 的简便方法。 您只需将 ChannelInitializer 的实现提供给引导程序,并且一旦 Channel 通过其 EventLoop 注册,就会调用您的 initChannel() 版本。 方法返回后,ChannelInitializer 实例将自己从ChannelPipeline 中删除。

下面的清单定义了 ChannelInitializerImpl 类,并使用 bootstrapchildHandler() 注册它。 你可以看到这个看似复杂的操作非常简单。

Bootstrapping and using ChannelInitializer

如果您的应用程序使用了许多ChannelHandler,请定义您自己的ChannelInitializer以将它们安装在管道中。

在创建每个频道时手动配置它可能会变得非常繁琐。幸运的是,您没有必要。相反,您可以使用option()将ChannelOptions应用于引导程序。您提供的值将自动应用于引导程序中创建的所有通道。 ChannelOption可用包括低级连接
详细信息,例如保持活动或超时属性和缓冲区设置。

Netty应用程序通常与组织的专有软件集成,而Channel等组件甚至可以在正常的Netty生命周期之外使用。如果某些常用属性和数据不可用,Netty提供了AttributeMap抽象,一个由通道和引导类提供的集合,以及AttributeKey ,一个用于插入和检索属性值的泛型类。使用这些工具,您可以安全地将任何类型的数据项与客户端和服务器Channel相关联。

例如,考虑一个跟踪用户和频道之间关系的服务器应用程序。这可以通过将用户的ID存储为频道的属性来实现。可以使用类似的技术根据用户的ID将消息路由到用户,或者在活动较少时关闭频道。
下一个清单显示了如何使用ChannelOption配置Channel和存储整数值的属性。

Listing 8.7 Using attributes

8.7 Bootstrapping DatagramChannels

以前的引导代码示例使用 SocketChannel,它是基于TCP的,但 Bootstrap 也可用于无连接协议。 Netty为此提供了各种DatagramChannel实现。 唯一的区别是你
不要调用 connect() 但只调用 bind(),如下所示。

Listing 8.8 Using Bootstrap with DatagramChannel

8.8 Shutdown

Bootstrapping 可以启动并运行您的应用程序,但迟早您需要优雅地关闭它。 当然,您可以让JVM处理退出时的所有内容,但这不符合graceful的定义,它指的是干净地释放资源。 关闭Netty应用程序并不需要太多魔力,但有几点需要注意。

最重要的是,您需要关闭EventLoopGroup,它将处理任何挂起的事件和任务,然后释放所有活动的线程。 这是调用EventLoopGroup.shutdownGracefully()的问题。 此调用将返回Future,在关闭完成时通知。 请注意,shutdownGracefully()也是一个异步操作,因此您需要阻塞它,直到它完成或注册一个带有返回的Future的侦听器以通知完成。

以下列表符合正常关闭的定义。
Graceful shutdown
或者,您可以在调用 EventLoopGroup.shutdownGracefully() 之前在所有活动通道上显式调用 Channel.close()。 但在所有情况下,请记住关闭EventLoopGroup本身。

8.9 Summary

在本章中,您学习了如何引导Netty服务器和客户端应用程序,包括那些使用无连接协议的人。 我们报道了一些特别的案例,包括在服务器应用程序中引导客户端通道并使用
ChannelInitializer用于处理多个ChannelHandler的安装引导。 您了解了如何在通道上指定配置选项以及如何指定使用属性将信息附加到频道。 最后,你学会了如何关闭优雅地下载应用程序以有序的方式释放所有资源。在下一章中,我们将研究Netty提供的工具,以帮助您测试ChannelHandler实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值