在深入地学习了 ChannelPipeline、ChannelHandler 和 EventLoop 之后,你接下来的问题可能是:“如何将这些部分组织起来,成为一个可实际运行的应用程序呢?”
答案是, 引导(Bootstrapping)。
Bootstrap 类
引导类的层次结构包括一个抽象的父类和两个具体的引导子类,如图 8-1 所示。
服务器致力于使用一个父 Channel 来接受 来自客户端的连接,并创建子 Channel 以用于它们之间的通信;而客户端将最可能只需要一个 单独的、没有父 Channel 的 Channel 来用于所有的网络交互。
AbstractBootstrap 类的完整声明是:
public abstract class AbstractBootstrap <B extends AbstractBootstrap<B,C>,C extends Channel>
在这个签名中,子类型 B 是其父类型的一个类型参数,因此可以返回到运行时实例的引用以支持方法的链式调用(也就是所谓的流式语法)。
其子类的声明如下:
public class Bootstrap extends AbstractBootstrap<Bootstrap,Channel>
public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap,ServerChannel>
引导客户端
Bootstrap 类负责为客户端和使用无连接协议的应用程序创建 Channel,如图 8-2 所示。
Channel 和 EventLoopGroup 的兼容性
代码清单 8-2 所示的目录清单来自 io.netty.channel 包。你可以从包名以及与其相对应 的类名的前缀看到,对于 NIO 以及 OIO 传输两者来说,都有相关的 EventLoopGroup 和 Channel 实现。
必须保持这种兼容性,不能混用具有不同前缀的组件,如 NioEventLoopGroup 和 OioSocketChannel,否则会导致 IllegalStateException。
引导服务器
ServerBootstrap类有一些Bootstrap类没有的方法:childHandler()、 childAttr()和 childOption()。这些调用支持特别用于服务器应用程序的操作。
具体来说, ServerChannel 的实现负责创建子 Channel,这些子 Channel 代表了已被接受的连接。因此,负责引导 ServerChannel 的 ServerBootstrap 提供了这些方法,以简化将设置应用到 已被接受的子 Channel 的 ChannelConfig 的任务。
从 Channel 引导客户端
假设你的服务器正在处理一个客户端的请求,这个请求需要它充当第三方系统的客户端。当 一个应用程序(如一个代理服务器)必须要和组织现有的系统(如 Web 服务或者数据库)集成 时,就可能发生这种情况。在这种情况下,将需要从已经被接受的子 Channel 中引导一个客户 端 Channel。
你可以创建新的 Bootstrap 实例,但是这并不是最高效的解 决方案,因为它将要求你为每个新创建的客户端 Channel 定义另一个 EventLoop。这会产生 额外的线程,以及在已被接受的子 Channel 和客户端 Channel 之间交换数据时不可避免的上下文切换。
一个更好的解决方案是:通过将已被接受的子 Channel 的 EventLoop 传递给 Bootstrap 的 group()方法来共享该 EventLoop。因为分配给 EventLoop 的所有 Channel 都使用同一 个线程,所以这避免了额外的线程创建,以及前面所提到的相关的上下文切换。这个共享的解决 方案如图 8-4 所示。
实现 EventLoop共享涉及通过调用 group()方法来设置 EventLoop。