Netty实战学习笔记
“引导”(Bootstrapping)。到目前为止,我们对这个术语的使用还比较含糊,现在已经到了精确定义它的时候了。简单来说, 引导一个应用程序是指对它进行配置, 并使它运行起来的过程。
Bootstrap 类
引导类的层次结构包括一个抽象的父类和两个具体的引导子类。

相对于将具体的引导类分别看作用于服务器和客户端的引导来说,记住它们的本意是用来支撑不同的应用程序的功能的将有所裨益。
- 服务器致力于使用一个父 Channel 来接受来自客户端的连接, 并创建子 Channel 以用于它们之间的通信;
- 客户端将最可能只需要一个单独的、没有父Channel的Channel来用于所有的网络交互。(正如同我们将要看到的,这也适用于无连接的传输协议,如UDP,因为它们并不是每个连接都需要一个单独的Channel。)
两种应用程序类型之间通用的引导步骤由AbstractBootstrap处理,而特定于客户端或者服务器的引导步骤则分别由Bootstrap或ServerBootstrap处理。
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable;
public final class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> ;
public final class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel>;
引导客户端和无连接协议
Bootstrap类被用于客户端或者使用了无连接协议的应用程序中。
Bootstrap API
它的API方法如下:

引导客户端
Bootstrap类负责为客户端和使用无连接协议的应用程序创建Channel.

Channel 和 EventLoopGroup 的兼容性
对于NIO以及OIO传输两者来说,都有相关的EventLoopGroup和Channel实现。
NioEventLoopGroup与NioXXChannel,OioSocketChannel与OioXXChanne配对使用,必须保持这种兼容性, 不能混用具有不同前缀的组件。
如果没有匹配使用,则会抛出异常将会导致 IllegalStateException,因为它混用了不兼容的传输。
关于 IllegalStateException 的更多讨论
在引导的过程中,在调用 bind()或者 connect()方法之前,必须调用以下方法来设置所需的组件:
- group();
- channel()或者 channelFactory();
- handler()。
如果不这样做, 则将会导致 IllegalStateException。对 handler()方法的调用尤其重要,因
为它需要配置好 ChannelPipeline。
引导服务器
ServerBootstrap类被用于服务器应用程序引导。
ServerBootstrap API
请注意 ServerBootstrap有一些Bootstrap不存在的方法: childHandler()、childAttr()和 childOption()。
这些调用支持特别用于服务器应用程序的操作。具体来说,ServerChannel的实现负责创建子Channel,这些子Channel代表了已被接受的连接。因此,负责引导ServerChannel的ServerBootstrap提供了这些方法,以简化将设置应用到已被接受的子Channel的ChannelConfig的任务。

从 Channel 引导客户端
假设你的服务器正在处理一个客户端的请求,这个请求需要它充当第三方系统的客户端。当一个应用程序(如一个代理服务器)必须要和组织现有的系统(如Web服务或者数据库)集成时,就可能发生这种情况。在这种情况下,将需要从已经被接受的子Channel中引导一个客户端Channel。
这种情况下,有一个准则:尽可能地重用 EventLoop,以减少线程创建所带来的开销。
解决方案是:通过将已被接受的子Channel的EventLoop传递给Bootstrap的group()方法来共享该EventLoop。因为分配给EventLoop的所有Channel都使用同一个线程,所以这避免了额外的线程创建,以及前面所提到的相关的上下文切换。这个共享的解决方案如图:
大致逻辑代码如下:
EventLoopGroup bossGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelHandlerAdapter() {
@Override
public void channelActive(ChannelHandlerContext ctx)
throws Exception {
Bootstrap bootstrap = new Bootstrap();
bootstrap.channel(NioSocketChannel.class);
bootstrap.group(ctx.channel().eventLoop()); //重用 EventLoop
ChannelFuture connectFuture = bootstrap.connect(new InetSocketAddress("x.com",80));
}
});
其他
* 在引导过程中添加多个 ChannelHandler *
使用ChannelInitializer将多个 ChannelHandler 添加到一个 ChannelPipeline 中。
public abstract class ChannelInitializer<C extends Channel>
extends ChannelInboundHandlerAdapter;
protected abstract void initChannel(C ch) throws Exception;
handler和childHandler的区别:
serverBootstrap.group(bossGroup)
.channel(NioServerSocketChannel.class)
.handler(new ChannelInitializer<NioServerSocketChannel>() {
@Override
protected void initChannel(NioServerSocketChannel ch) throws Exception {
//若作为服务端,用来处理: ServerSocketChannel ,
System.out.println("handler:"+ch);
}
})
.childHandler(new ChannelHandlerAdapter() {
@Override
public void channelActive(ChannelHandlerContext ctx)
throws Exception {
//设置用于处理已被接受的子Channel的I/O
ctx.pipeline().addLast("http", new HttpServerCodec());
ctx.pipeline().addLast("aggregator", new HttpObjectAggregator(65536));
}
});
ChannelOption 和属性
Netty可以使用 option()方法来将 ChannelOption 应用到引导。这些option会被自动应用到引导所创建的所有 Channel。
bootstrap.option(ChannelOption.SO_KEEPALIVE,true)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000);
UDP -DatagramChannel
UDP协议是无连接的, Bootstrap 类也可以被用于无连接的协议。
- 服务端
public class ChineseProverServer {
public static void main(String[] args) throws InterruptedException {
new ChineseProverServer().run(8080);
}
private void run(int port) throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup();
try {
//udp不存在服务端和客户端,所以
Bootstrap b = new Bootstrap();
b.group(bossGroup)
.channel(NioDatagramChannel.class)
//socket参数,支持广播
.option(ChannelOption.SO_BROADCAST, true)
.handler(new ChineseProverServerHandler());
b.bind(port).sync().channel().closeFuture().await();
} finally {
bossGroup.shutdownGracefully();
}
}
}
@ChannelHandler.Sharable
class ChineseProverServerHandler extends SimpleChannelInboundHandler<DatagramPacket> {
private static final String[] DIRECTORY = {"只要功夫深,铁杵磨成针", "旧时王谢堂前燕,飞入寻常百姓家",
"洛阳亲友如相问,一片冰心在玉壶", "一寸光阴一寸金,寸金难买寸光阴", "老骥伏枥,志在千里"};
private String nextQuote() {
int quoteId = ThreadLocalRandom.current().nextInt(DIRECTORY.length);
return DIRECTORY[quoteId];
}
@Override
protected void messageReceived(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
String req = packet.content().toString(CharsetUtil.UTF_8);
System.out.println(req);
if("谚语字典查询?".startsWith(req)) {
ctx.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer("查询结果:" + nextQuote(), CharsetUtil.UTF_8), packet.sender()));
}
}
}
- 客户端
public class ChineseProverClient {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
int port = 8080;
if (args != null && args.length > 0) {
try {
port = Integer.valueOf(args[0]);
} catch (NumberFormatException e) {
// 采用默认值
}
}
new ChineseProverClient().connect(port);
}
public void connect(int port) throws Exception {
// 配置客户端NIO线程组
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group).channel(NioDatagramChannel.class).option(ChannelOption.SO_BROADCAST, true).handler(new ChineseProverClientHandler());
Channel ch = b.bind(0).sync().channel();
//向网段内所有机器广播UDP消息
ch.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer("谚语字典查询", CharsetUtil.UTF_8),
new InetSocketAddress("255.255.255.255", port))).sync();
if(!ch.closeFuture().await(15000)) {
System.out.println("查询超时");
}
} finally {
// 优雅退出,释放NIO线程组
group.shutdownGracefully();
}
}
}
@ChannelHandler.Sharable
class ChineseProverClientHandler extends SimpleChannelInboundHandler<DatagramPacket> {
@Override
protected void messageReceived(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
String resp = packet.content().toString(CharsetUtil.UTF_8);
if(resp.startsWith("查询结果")) {
System.out.println(resp);
ctx.close();
}
}
}

本文介绍了Netty中的引导机制,包括Bootstrap和ServerBootstrap的使用方法,并详细解释了客户端和服务端引导过程中的关键配置。此外,还展示了如何利用Bootstrap进行UDP通信的实际案例。
271

被折叠的 条评论
为什么被折叠?



