启动(bootstrapping)一个应用是配置并运行它的过程。
与其应用架构的方法一致,Netty将通过你的应用与网络层分离的方式(不需要关心网络层的实现细节)来处理启动。

启动类

启动类的结构如上图。包含一个抽象父类和两个具体启动子类。与其将这两个具体子类看成server和client的启动类,不如将它们看成是支持的不同应用程序功能。也就是,一个server提供一个父channel来接收客户端的连接,然后创建子channel来进行会话,而一个client可能只需要一个非父类的channel来进行所有的网络交互(也有可能不需要channel,比如无连接传输协议UDP)。

我们前面介绍过的几个Netty组件参与了启动过程,其中一些客户端和服务端都可以使用。这两种类型的共同启动步骤由AbstractBootstrap处理,而专用于客户端或服务端的是Bootstrap或ServerBootstrap。下面开始介绍Bootstrap。

为什么bootstrap类是Clonealbe的? 你有时需要创建多个channel,它们有相似或相同的配置。为了支持这种设计同时不需要一个新的bootstrap实例,AbstractBootstrap类被设计为Clonealbe的。在一个已经配置好的bootstrap实例调用clone()方法会返回另一个开箱即用的bootstrap实例;注意这只会创建bootstrap的EventLoopGroup的一个浅克隆,因此它会被所有克隆的channel共享。这是可接受的,因为被克隆的channel通常是生命周期很短的,一个典型的应用是HTTP请求。

AbstractBootstrap类的完整声明如下:

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

在这个签名中,子类B是超类的一个类型参数,因此可以返回一个运行时实例的引用来支持方法链接(method chaining)

子类如下定义:

public class Bootstrap extends AbstractBootstrap<Bootstrap,Channel>
public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap,ServerChannel>

在客户端和无连接协议中的启动

下表给出了Bootstrap方法的总览,有很多方法是继承自AbstractBootstrap的

名称 描述
Bootstrap group(EventLoopGroup) 设置EventLoopGroup,这个EventLoopGroup会为channel处理所有的事件
Bootstrap channel(Class)
Bootstrap channelFactory(ChannelFactory)
channel()指定Channel的实现类。如果这个实现类没有提供一个默认的构造函数,你可以调用channelFactory()来指定一个工厂类(被bind()方法调用)
Bootstrap localAddress(SocketAddress) 指定Channel要绑定的本地address。如果没有提供,OS会选择一个随机的地址(从你网卡中选择),另外,你可以通过bind()或connect()来指定address
Bootstrap option(ChannelOption option,T value) 设置一个ChannelOption应用到新创建Channel的ChannelConfig(Channel创建后再调用此方法无效)
Bootstrap attr(Attribute key, T value) 指定新创建的Channel的一个属性(Channel创建后再调用此方法无效)
Bootstrap handler(ChannelHandler) 设置加到ChannelPipeline中的ChannelHandler来接收事件通知
Bootstrap clone() 创建当前Bootstrap的一个克隆,它们共享同一个配置
Bootstrap remoteAddress(SocketAddress) 设置远程地址。或者你可以在调用connect()方法时指定
ChannelFuture connect() 连接到远端机器然后返回一个ChannelFuture,当连接操作完成时可以通过ChannelFuture来得到通知
ChannelFuture bind() 绑定Channel然后返回一个ChannelFutre,当连接操作完成时可以通过ChannelFuture来得到通知。在这个方法之后,必须调用Channel.connect()方法来建立连接

启动一个客户端

Bootstrap负责为客户端创建channel,如下图所示:

下面的代码展示了如何启动一个客户端:

        EventLoopGroup group = new NioEventLoopGroup();Bootstrap bootstrap = new Bootstrap();bootstrap.group(group)//设置EventLoopGroup提供处理Channel事件的EventLoop.channel(NioSocketChannel.class).handler(new SimpleChannelInboundHandler<ByteBuf>() {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {System.out.println("Received data");}} );ChannelFuture future = bootstrap.connect(new InetSocketAddress("www.baidu.com", 80));future.addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture channelFuture)throws Exception {if (channelFuture.isSuccess()) {System.out.println("Connection established");} else {System.err.println("Connection attempt failed");channelFuture.cause().printStackTrace();}}} );

Channel和EventLoopGroup的兼容性

下面列出了io.netty.channel包的树形结构,你可以从包名和类名前缀看出NIO和OIO传输服务都有相应的EventLoopGroup和Channel实现。

channel
├───nio
│         NioEventLoopGroup
├───oio
│         OioEventLoopGroup
└───socket├───nio│         NioDatagramChannel│         NioServerSocketChannel│         NioSocketChannel└───oioOioDatagramChannelOioServerSocketChannelOioSocketChannel

这种兼容性必须维护,你不能混合不同前缀的组件,比如NioEventLoopGroup和OioSocketChannel。下面的代码显示了一个错误的示范:

EventLoopGroup group = new NioEventLoopGroup();//指定了一个NIO EventLoopGroup实现Bootstrap bootstrap = new Bootstrap();bootstrap.group(group).channel(OioSocketChannel.class) //指定一个OIO Channel实现类.handler(new SimpleChannelInboundHandler<ByteBuf>() {@Overrideprotected void channelRead0(ChannelHandlerContext channelHandlerContext,ByteBuf byteBuf) throws Exception {System.out.println("Received data");}} );ChannelFuture future = bootstrap.connect(new InetSocketAddress("www.manning.com", 80));future.syncUninterruptibly();

这段代码会引起一个IllegalStateException

Exception in thread "main" java.lang.IllegalStateException: incompatible event loop type: io.netty.channel.nio.NioEventLoopat io.netty.channel.AbstractChannel$AbstractUnsafe.register(AbstractChannel.java:464)at io.netty.channel.SingleThreadEventLoop.register(SingleThreadEventLoop.java:80)at io.netty.channel.SingleThreadEventLoop.register(SingleThreadEventLoop.java:74)...

启动时,在你调用bind()或connect()之前,你必须调用下面的方法来设置必要的组件
* group()
* channel() 或 channnelFactory()
* handler()
如果没这么做也会导致一个IllegalStateException。handler()方法尤其重要,因为必须用它来配置ChannelPipeline。

启动服务端

ServerBootstrap类

下表列出了ServerBootstrap的方法:

名称 描述
group 设置ServerBootstrap使用的EventLoopGroup
channel 设置要实例化的ServerChannel类
channelFactory 如果Channel不能通过默认的构造函数创建,你就可以指定一个channelFactory
localAddress 指定Channel要绑定的本地address。如果没有提供,OS会选择一个随机的地址(从你网卡中选择),另外,你可以通过bind()或connect()来指定address
option 设置一个ChannelOption应用到新创建ServerChannel的ChannelConfig
childOption 指定一个ChannelOption来应用到接收的Channel的ChannelConfig
attr 指定新创建的ServerChannel的一个属性,通过bind()方法将属性设置到channel上,调用bind()后再调用此方法无效
childAttr 应用一个属性来接收Channel
handler 设置要加到ServerChannel的ChannelPipeline的ChannelHander
childHandler 设置要添加到接收的Channel的ChannelPipeline的ChannelHander,与上一个方法的区别是,这个方法为接收的Channel(代表与远端机器绑定的socket)增加handler而handler()为ServerChannel增加handler
clone 克隆ServerBootstrap来接收不同的远端机器连接。它的配置与源ServerBootstrap相同
bind 绑定ServerChannel然后返回一个ChannelFutre,当连接操作完成时可以通过ChannelFuture来得到通知。

启动一个Server

childHandler(), childAttr()和childOption()方法支持server应用特有的操作。下图显示了一个ServerBootstrap通过bind()方法创建一个ServerChannel,然后ServerChannel管理了几个子Channel。

下面的代码展示了这个过程:

        NioEventLoopGroup group = new NioEventLoopGroup();ServerBootstrap bootstrap = new ServerBootstrap();//创建一个ServerBootstrapbootstrap.group(group)//设置EventLoopGroup来提供处理Channel事件的EventLoop.channel(NioServerSocketChannel.class).childHandler(new SimpleChannelInboundHandler<ByteBuf>() {@Overrideprotected void channelRead0(ChannelHandlerContext ctx,ByteBuf byteBuf) throws Exception {System.out.println("Received data");}} );//通过配置好的bootstrap来绑定channelChannelFuture future = bootstrap.bind(new InetSocketAddress(8080));future.addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture channelFuture)throws Exception {if (channelFuture.isSuccess()) {System.out.println("Server bound");} else {System.err.println("Bound attempt failed");channelFuture.cause().printStackTrace();}}} );

从Channel中启动客户端

通过将EventLoop传入Bootstrap的group()方法来共享Channel的EventLoop。 因为所有Channel都分配给一个使用相同的线程EventLoop,这避免了额外的线程创建和相关的上下文切换。 这个共享解决方案下图所示:

实现EventLoop共享可以通过调用group()方法设置EventLoop来实现:

 ServerBootstrap bootstrap = new ServerBootstrap();//用来创建SocketChannelbootstrap.group(new NioEventLoopGroup(), new NioEventLoopGroup()).channel(NioServerSocketChannel.class).childHandler(new SimpleChannelInboundHandler<ByteBuf>() {ChannelFuture connectFuture;@Overridepublic void channelActive(ChannelHandlerContext ctx)throws Exception {Bootstrap bootstrap = new Bootstrap();//创建一个Bootstrap来与远端建立连接bootstrap.channel(NioSocketChannel.class).handler(new SimpleChannelInboundHandler<ByteBuf>() {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, ByteBuf in)throws Exception {System.out.println("Received data");}} );//使用同样的Eventloop设置给接收的(accepted)channelbootstrap.group(ctx.channel().eventLoop());connectFuture = bootstrap.connect(new InetSocketAddress("www.manning.com", 80));}@Overrideprotected void channelRead0(ChannelHandlerContext channelHandlerContext,ByteBuf byteBuf) throws Exception {if (connectFuture.isDone()) {// do something with the data}}} );ChannelFuture future = bootstrap.bind(new InetSocketAddress(8080));future.addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture channelFuture)throws Exception {if (channelFuture.isSuccess()) {System.out.println("Server bound");} else {System.err.println("Bind attempt failed");channelFuture.cause().printStackTrace();}}} );

在一个启动过程中增加多个ChannelHandler

当你的应用比较复杂时就需要增加多个handler了,比如需要支持多种传输协议的应用。
你可以部署很多个ChannelHandler到一个ChannelPipeline中。但是如果你只能设置一个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类然后通过bootstrap的childHandler()来注册它:

ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(new NioEventLoopGroup(), new NioEventLoopGroup()).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializerImpl());//注册一个ChannelInitializerImpl的实例来设置ChannelPipeline
ChannelFuture future = bootstrap.bind(new InetSocketAddress(8080));
future.sync();
final class ChannelInitializerImpl extends ChannelInitializer<Channel> {//增加handler到ChannelPipeline@Overrideprotected void initChannel(Channel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();//下面增加了2个ChannelHandler到pipelinepipeline.addLast(new HttpClientCodec());pipeline.addLast(new HttpObjectAggregator(Integer.MAX_VALUE));}
}//创建一个AttributeKey以指定一个属性final AttributeKey<Integer> id = new AttributeKey<Integer>("ID");Bootstrap bootstrap = new Bootstrap();bootstrap.group(new NioEventLoopGroup()).channel(NioSocketChannel.class).handler(new SimpleChannelInboundHandler<ByteBuf>() {@Overridepublic void channelRegistered(ChannelHandlerContext ctx)throws Exception {Integer idValue = ctx.channel().attr(id).get();//获取id的值// do something with the idValue}@Overrideprotected void channelRead0(ChannelHandlerContext channelHandlerContext,ByteBuf byteBuf) throws Exception {System.out.println("Received data");}});bootstrap.option(ChannelOption.SO_KEEPALIVE,true).option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000);bootstrap.attr(id, 123456);//将123456保存到idChannelFuture future = bootstrap.connect(new InetSocketAddress("www.manning.com", 80));future.syncUninterruptibly();

启动DatagramChannels

下面展示无连接协议的使用(UDP)。Netty提供了不同的DatagramChannel的实现,它们唯一的区别是你不用调用connect(),只需要bind(),如下所示:

 Bootstrap bootstrap = new Bootstrap();bootstrap.group(new OioEventLoopGroup()).channel(OioDatagramChannel.class).handler(//指定了OioDatagramChannelnew SimpleChannelInboundHandler<DatagramPacket>() {@Overridepublic void channelRead0(ChannelHandlerContext ctx,DatagramPacket msg) throws Exception {// Do something with the packet}});//因为是无连接的协议,所有调用bindChannelFuture future = bootstrap.bind(new InetSocketAddress(0));future.addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture channelFuture)throws Exception {if (channelFuture.isSuccess()) {System.out.println("Channel bound");} else {System.err.println("Bind attempt failed");channelFuture.cause().printStackTrace();}}});

关闭

如果你想优雅的关闭你的应用,指的是干净的释放资源。你只需要注意几点,首先,你需要关闭EventLoopGroup,它会处理队列中的事件和任务然后释放活跃的线程。这可以通过调用EventLoopGroup.shutdownGracefully()来实现。这个调用会返回一个Future,当关闭操作完成时可以得到通知。注意,shutdownGracefully()方法是一个异步方法(所以会返回Future)。下面的代码展示了这个过程:

EventLoopGroup group = new NioEventLoopGroup();Bootstrap bootstrap = new Bootstrap();bootstrap.group(group).channel(NioSocketChannel.class);...Future<?> future = group.shutdownGracefully();//调用syncUninterruptibly()来阻塞直到group关闭future.syncUninterruptibly();

Netty in action—Bootstraping相关推荐

  1. Netty in Action 读书

    Netty in Action 作者: Norman Maurer / Marvin Allen Wolfthal 出版社:Manning Publications 出版年:2015-12-31 页数 ...

  2. Netty In Action中文版

    第一章:Netty介绍 本章介绍 Netty介绍 为什么要使用non-blocking IO(NIO) 阻塞IO(blocking IO)和非阻塞IO(non-blocking IO)对比 Java ...

  3. Netty In Action中文版 - 第十二章:SPDY

    Netty In Action中文版 - 第十二章:SPDY 本章我将不会直接翻译Netty In Action书中的原文,感觉原书中本章讲的很多废话,我翻译起来也吃力.所以,本章内容我会根据其他资料 ...

  4. 《Netty IN ACTION》中文版《Netty实战》翻译手记——不负好时光

    不负好时光--<Netty in Action>中文版<Netty实战>翻译手记 引子 "书中自有黄金屋,书中自有颜如玉",这句话从小我老爸就给我讲,当然那 ...

  5. 《Netty In Action》第二章:第一个Netty程序

    2019独角兽企业重金招聘Python工程师标准>>> 源码地址: GitHub 2.3 编写一个应答服务器 写一个Netty服务器主要由两部分组成: 配置服务器功能,如线程.端口 ...

  6. 《Netty 实战》Netty In Action中文版 第2章——你的第一款Netty应用程序(一)

    第2章 你的第一款Netty应用程序 本章主要内容 设置开发环境 编写Echo服务器和客户端 构建并测试应用程序 在本章中,我们将展示如何构建一个基于Netty的客户端和服务器.应用程序很简单:客户端 ...

  7. Netty in Action (十九) 第九章节 单元测试

    本章内容包括: 1)单元测试 2)EmbeddedChannel的说明 3)使用EmbeddedChannel测试ChannelHandler 对于一个Netty应用来说,ChannelHandler ...

  8. 不负好时光——《Netty in Action》中文版《Netty实战》翻译手记

    引子 "书中自有黄金屋,书中自有颜如玉",这句话从小我老爸就给我讲,当然那个时候真的以为书中真的会有黄金做的屋子和很多玉件.后来慢慢长大了,也渐渐懂得了这句话背后的真正含义.当然, ...

  9. netty集成ssl完整参考指南(含完整源码)

    虽然我们在内部rpc通信中使用的是基于认证和报文头加密的方式实现安全性,但是有些时候仍然需要使用SSL加密,可能是因为对接的三方系统需要,也可能是由于open的考虑.中午特地测了下netty下集成ss ...

  10. 再有人问你Netty是什么,就把这篇文章发给他

    点击上方"方志朋",选择"置顶或者星标" 你的关注意义重大! 本文转载于公众号:Hollis 本文基于Netty4.1展开介绍相关理论模型,使用场景,基本组件. ...

最新文章

  1. 【转载】pycharm远程调试配置
  2. Google发布神经天气模型,几秒钟预测整个美国的降水量
  3. 用mansard对cell的子控件设置约束,并且自动计算cell高度的问题,ios7警告
  4. Tomcat设置虚拟目录的方法, 不修改server.xm
  5. VTK:PolyData之ConnectivityFilter_LargestRegion
  6. linux下camera驱动分析_《Linux设备驱动程序》(五)——字符设备驱动(下)
  7. Mysql,ERROR 1044 (42000): Access denied for user ''@'localhost' to database 'mysql'
  8. MySQL中的char与varchar详解
  9. sqlyog for MySQL远程连接的时候报错mysql 1130的解决方法
  10. 配置ssh信任(不通过密码验证ssh直接访问目标机器)
  11. 实践解决跨域问题的三种方式剖析
  12. 计算机组成原理课后答案(唐朔飞第三版) 第四章
  13. 空间频率 MTF和 SFR
  14. 代理ARP产生路由环路问题分析
  15. 搭建一款属于自己的,微信/抖音小程序,通过广告赚钱
  16. 记一次烧毁AO3400的过程
  17. 回顾+纪念:离开帝都的第一年
  18. 相机技术--摄像机720p、1080p、2mp、3mp、5mp;VGA, QHD, FHD, 2K,4K对应的分辨率分别是什么
  19. 会火吗?苹果软件新专利:用户自拍照可组成合影
  20. 【MYsql触发器】

热门文章

  1. C# GUID format参数说明
  2. 高校表白App-团队冲刺第十天
  3. ABAP术语-Business Components
  4. Swing 显示良好JPanel保存为图片
  5. Attributes(2): Displaying attributes for a class.(显示类属性)
  6. python图片二值化提高识别率
  7. [bzoj3809]Gty的二逼妹子序列/[bzoj3236][Ahoi2013]作业
  8. 转场动画UINavigationControllerDelegate
  9. CentOS 7 重装mysql编译过程报错解决方法
  10. py2exe使用方法