一、前言

  前面已经学习了Netty的EventLoop以及线程模型,接着学习Netty的Bootstrapping。

二、Bootstrapping

  在学习了Netty中的很多组件后,如何将这些组件有效的组合至应用程序中,这需要使用应用引导程序,引导应用程序是将其配置为运行的过程,Netty以一种绝对应用程序的方式处理引导。

  2.1 Bootstrap类

  Bootstrap类的继承结构图如下图所示。

  

  一个服务器使用一个父通道来接受来自客户端的连接并创建子通道来与它们进行通信,而一个客户端很可能只需要一个非父通道来进行所有的网络交互,而前面讨论的组件会参与引导过程,有些在客户端和服务端都会被使用。两个应用程序类型共同的引导步骤由AbstractBootstrap处理,而特定于客户端或服务器的引导步骤分别由Bootstrap或ServerBootstrap处理。

  为何Bootstrap为Cloneable?因为有时需要创建具有类似或相同设置的多个通道,为了支持此模式,不需要为每个通道创建和配置新的引导实例,因此将AbstractBootstrap标记为Cloneable,在已配置的引导程序上调用clone()方法将返回可立即使用的另一个引导实例,这只会创建引导程序的EventLoopGroup的浅层副本,所以其被所有克隆的通道共享

  2.2 引导客户端和无连接协议

  Bootstrap在客户端或无连接协议的应用程序中使用。

  1. 引导客户端

  Bootstrap类负责为客户端和使用无连接协议的应用程序创建通道,如下图所示。

  

  以下代码引导使用NIO TCP传输的客户端。  

EventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new SimpleChannelInboundHandler<ByteBuf>() {
@Override
protected void channeRead0(
ChannelHandlerContext channelHandlerContext,
ByteBuf byteBuf) throws Exception {
System.out.println("Received data");
}
} ); ChannelFuture future = bootstrap.connect(
new InetSocketAddress("www.manning.com", 80));
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();
}
}
} );

  2. Channel和EventLoopGroup兼容性

  io.netty.channel包中的类结构如下。

   

       

  可以看到对于NIO和OIO传输,都有相关的EventLoopGroup和Channel实现,不能混合具有不同前缀的组件,例如NioEventLoopGroup和OioSocketChannel。如下代码展示不兼容的使用用法。  

EventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(OioSocketChannel.class)
.handler(new SimpleChannelInboundHandler<ByteBuf>() {
@Override
protected 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();

  其中NIOEventLooop和OioSocketChannel不兼容,将会抛出IllegalStateException异常。

  在调用bind或者connect方法之前,需要调用group、channel或channelFactory、handler方法,否则会抛出IllegalStateException异常。

  2.3 引导服务器

  ServerChannel的实现负责创建接受连接的子通道,ServerBootstrap通过bind()方法创建一个ServerChannel,ServerChannel可管理多个子通道,具体如下图所示。

  

  下面代码展示了如何引导服务器。  

NioEventLoopGroup group = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(group)
.channel(NioServerSocketChannel.class)
.childHandler(new SimpleChannelInboundHandler<ByteBuf>() {
@Override
protected void channelRead0(ChannelHandlerContext ctx,
ByteBuf byteBuf) throws Exception {
System.out.println("Received data");
}
} );
ChannelFuture future = bootstrap.bind(new InetSocketAddress(8080));
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();
}
}
} );

  2.4 由通道引导客户端

  假设服务器正在处理客户端请求,并要求服务器作为第三个系统的客户端,如代理服务器。此时需要从ServerChannel引导客户端通道。一个较好的方法是通过Bootstrap类的group方法传递Channel对应的EventLoop,因为所有分配给EventLoop的通道都使用相同的线程,这避免了额外的线程创建和相关的上下文切换。具体如下图所示。

  

  通过group方法共享EventLoop的代码如下。  

ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(new NioEventLoopGroup(), new NioEventLoopGroup())
.channel(NioServerSocketChannel.class)
.childHandler(
new SimpleChannelInboundHandler<ByteBuf>() {
ChannelFuture connectFuture;
@Override
public void channelActive(ChannelHandlerContext ctx)
throws Exception {
Bootstrap bootstrap = new Bootstrap();
bootstrap.channel(NioSocketChannel.class).handler(
new SimpleChannelInboundHandler<ByteBuf>() {
@Override
protected void channelRead0(
ChannelHandlerContext ctx, ByteBuf in)
throws Exception {
System.out.println("Received data");
}
} );
bootstrap.group(ctx.channel().eventLoop());
connectFuture = bootstrap.connect(
new InetSocketAddress("www.manning.com", 80));
}
@Override
protected 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() {
@Override
public void operationComplete(ChannelFuture channelFuture)
throws Exception {
if (channelFuture.isSuccess()) {
System.out.println("Server bound");
} else {
System.err.println("Bind attempt failed");
channelFuture.cause().printStackTrace();
}
}
} );

  2.5 在引导过程中添加多个ChannelHandler

  在上面示例中,在引导过程中通过调用handler或者childHandler方法添加单个ChannelHandler,并且我们知道在ChannelPipeline中可以有多个ChannelHandler链,但是在引导过程中只添加了一个ChannelHandler。Netty提供了ChannelInboundHandlerAdapter,其提供了initChannel方法,该方法可以将多个ChannelHandler添加至ChannelPipeline中,你只需要将ChannelInitializer的实现提供给引导程序,而一旦Channel注册了EventLoop,那么initChannel方法将被调用,当方法返回后,ChannelInitializer将自身从ChannelPipeline中移除,如下代码展示了具体的操作。 

ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(new NioEventLoopGroup(), new NioEventLoopGroup())
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializerImpl());
ChannelFuture future = bootstrap.bind(new InetSocketAddress(8080));
future.sync();
final class ChannelInitializerImpl extends ChannelInitializer<Channel> {
@Override
protected void initChannel(Channel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new HttpClientCodec());
pipeline.addLast(new HttpObjectAggregator(Integer.MAX_VALUE));
}
}

  2.6 使用Netty的ChannelOptions和attributes

  当创建通道时手动配置非常麻烦,可以使用option方法将ChannelOptions提供给引导程序,你提供的值将自动应用于在引导中创建的所有通道。ChannelOptions包括连接详细信息,如保持活动、超时属性和缓冲区设置等。Netty还可使用AttributeKey抽象类来配置属性值,如下代码展示了具体使用。 

final AttributeKey<Integer> id = new AttributeKey<Integer>("ID");
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(new NioEventLoopGroup())
.channel(NioSocketChannel.class)
.handler(
new SimpleChannelInboundHandler<ByteBuf>() {
@Override
public void channelRegistered(ChannelHandlerContext ctx)
throws Exception {
Integer idValue = ctx.channel().attr(id).get();
// do something with the idValue @Override
protected 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);
ChannelFuture future = bootstrap.connect(
new InetSocketAddress("www.manning.com", 80));
future.syncUninterruptibly();

  2.7 引导DatagramChannels

  前面的示例使用的SocketChannel,是基于TCP协议,但是Bootstrap也可以用于无连接的协议,如UDP协议,唯一的区别在于不调用connect方法,只使用bind方法,具体如下代码所示。 

Bootstrap bootstrap = new Bootstrap();
bootstrap.group(new OioEventLoopGroup()).channel(
OioDatagramChannel.class).handler(
new SimpleChannelInboundHandler<DatagramPacket>(){
@Override
public void channelRead0(ChannelHandlerContext ctx,
DatagramPacket msg) throws Exception {
// Do something with the packet
}
}
);
ChannelFuture future = bootstrap.bind(new InetSocketAddress(0));
future.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture)
throws Exception {
if (channelFuture.isSuccess()) {
System.out.println("Channel bound");
} else {
System.err.println("Bind attempt failed");
channelFuture.cause().printStackTrace();
}
}
})

  2.8 关闭

  引导使得应用运行,但是之后需要优雅的关闭引导。需要关闭EventLoopGroup,可调用EventLoopGroup.shutdownGracefully() 方法,其是异步的,会返回ChannelFuture,在观泉关闭后会收到通知,下面是使用示例。  

EventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class);
...
Future<?> future = group.shutdownGracefully();
// block until the group has shutdown
future.syncUninterruptibly();

  也可以在调用shutdownGracefully方法之前显示调用close方法,要让EventLoopGroup自己主动关闭。

三、总结

  本篇博文讲解了Bootstrap,包括客户端和服务端的引导,以及如何启动标准的客户端和服务端程序。也谢谢各位园友的观看~

【Netty】Netty之Bootstrapping的更多相关文章

  1. [Netty] - Netty IN ACTION(导言)

    最近没什么事儿做,刚好看到有需要网络编程的知识,java中有NIO和IO两种不同的方式,但是NIO的编写比较麻烦,刚好找到一个成熟的网络框架Netty.接下来的一个月就准备将Netty IN ACTI ...

  2. [Netty] - Netty入门(最简单的Netty客户端/服务器程序)

    Java中的NIO是一种解决阻塞式IO问题的基本技术,但是NIO的编写对java程序员是有比较高的要求的.那么Netty就是一种简化操作的一个成熟的网络IO编程框架.这里简单介绍一个程序,代码是< ...

  3. NetCore Netty 框架 BT.Netty.RPC 系列随讲 二 WHO AM I 之 NETTY/NETTY 与 网络通讯 IO 模型之关系?

    一:NETTY 是什么? Netty 是什么?  这个问题其实百度上一搜一堆. 这是官方话的描述:Netty 是一个基于NIO的客户.服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个 ...

  4. NIO学习笔记,从Linux IO演化模型到Netty—— Netty零拷贝

    Netty的中零拷贝与上述零拷贝是不一样的,它并不是系统层面上的零拷贝,只是相对于ByteBuf而言的,更多的是偏向于数据操作优化这样的概念. Netty中的零拷贝: 1.CompositeByteB ...

  5. 从netty-example分析Netty组件续

    上文我们从netty-example的Discard服务器端示例分析了netty的组件,今天我们从另一个简单的示例Echo客户端分析一下上个示例中没有出现的netty组件. 1. 服务端的连接处理,读 ...

  6. 源码分析netty服务器创建过程vs java nio服务器创建

    1.Java NIO服务端创建 首先,我们通过一个时序图来看下如何创建一个NIO服务端并启动监听,接收多个客户端的连接,进行消息的异步读写. 示例代码(参考文献[2]): import java.io ...

  7. 从netty-example分析Netty组件

    分析netty从源码开始 准备工作: 1.下载源代码:https://github.com/netty/netty.git 我下载的版本为4.1 2. eclipse导入maven工程. netty提 ...

  8. Netty源码分析之客户端启动过程

    一.先来看一下客户端示例代码. public class NettyClientTest { public void connect(int port, String host) throws Exc ...

  9. Netty源码分析之服务端启动过程

    一.首先来看一段服务端的示例代码: public class NettyTestServer { public void bind(int port) throws Exception{ EventL ...

  10. netty研究【1】:编译源代码

    netty作为异步通信底层框架,其优异的性能让我产生了研究他的源码的决定. 代码研究之前,第一步就是要准备环境,至少可以编译通过,下面,就拿github上的4.1分支进行.我的IDE是Intellij ...

随机推荐

  1. jquery的冒泡事件event.stopPropagation()

    js中的冒泡事件与事件监听 冒泡事件 js中“冒泡事件”并不是能实际使用的花哨技巧,它是一种对js事件执行顺序的机制,“冒泡算法”在编程里是一个经典问题,冒泡算法里面的冒泡应该 说是交换更加准确:js ...

  2. Java事物基础总结

    1.什么是事物? 事物是逻辑上的的一种操作,这个操作过程中的每一个元素要么全部成功,要么全部失败.例如,银行转账过程视为一个事物,转出过程和转入过程要求全部成功或全部失败,通过提交事物或者回滚事物实现 ...

  3. Jquery对复选框CheckBox的操作

    checkbox: 多选框 //获取选中值  checkbox:$("#checkbox_id").attr("value"): 多选框checkbox,打勾: ...

  4. 有关DeadLock的文章列表

    SET STATISTICS PROFILE ON 显示query的执行过程   Troubleshooting Deadlocks   Understanding Locking in SQL Se ...

  5. Linux Shell——bash shell 脚本简介

    bash shell 脚本简介 shell 运行环境 如果你运行的是 Unix 或 Linux 系统,例如 Ubuntu,Red Hat,SUSE Linux,还有macOS,都是内置了 bash s ...

  6. 使用Eclipse Memory Analyzer Tool(MAT)分析线上故障(一) - 视图&功能篇

    Eclipse Memory Analyzer Tool(MAT)相关文章目录: 使用Eclipse Memory Analyzer Tool(MAT)分析线上故障(一) - 视图&功能篇 使 ...

  7. 篇5 python自动化测试应用-Selenium环境篇

    篇5                            python自动化测试应用-Selenium环境篇 --lamecho 1.1概要 大家好!我是lamecho(辣么丑),从本篇开始我将开始 ...

  8. openresty源码剖析——lua代码的加载

    ##Openresty是什么 OpenResty是一个基于 Nginx 与 Lua 的高性能 Web 平台,通过把lua嵌入到Nginx中,使得我们可以用轻巧的lua语言进行nginx的相关开发,处理 ...

  9. 使用 rsync 同步

    原文地址 http://www.howtocn.org/rsync:use_rsync 选项 说明 -a, ––archive 归档模式,表示以递归方式传输文件,并保持所有文件属性,等价于 -rlpt ...

  10. [Splay模版1]

    输入 第1行:1个正整数n,表示操作数量,100≤n≤200,000 第2..n+1行:可能包含下面3种规则: 1个字母'I',紧接着1个数字k,表示插入一个数字k到树中,1≤k≤1,000,000, ...