Bootstrap 类
引导类的层次结构
注:为什么引导类是Cloneable 的?
只会创建引导类实例的EventLoopGroup的一个浅拷贝,所以,将在所有克隆的Channel实例之间共享。这是可以接受的,因为通常这些克隆的Channel的生命周期都很短暂,一个典型的场景是——创建一个Channel以进行一次HTTP请求。
引导客户端和无连接协议
API
- 引导客户端
引导过程
EventLoopGroup group = new NioEventLoopGroup(); // 设置EventLoopGroup,提供用于处理Channel事件的EventLoop
Bootstrap bootstrap = new Bootstrap(); // 创建一个Bootstrap类的实例以创建和连接新的客户端
Channel
bootstrap.group(group)
.channel(NioSocketChannel.class) // 指定要使用的Channel 实现
.handler(new SimpleChannelInboundHandler<ByteBuf>() { // 设置用于Channel 事件和数据的ChannelInboundHandler
@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();
}
}
} );
- Channel 和EventLoopGroup 的兼容性
channel // 相互兼容的EventLoopGroup 和Channel
├───nio
│ NioEventLoopGroup
├───oio
│ OioEventLoopGroup
└───socket
├───nio
│ NioDatagramChannel
│ NioServerSocketChannel
│ NioSocketChannel
└───oio
│ OioDatagramChannel
│ OioServerSocketChannel
│ OioSocketChannel
引导服务器
- ServerBootstrap 类
ServerBootstrap 类的方法
- 引导服务器
ServerBootstrap 和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();
}
}
} );
从Channel 引导客户端
在两个Channel 之间共享EventLoop
通过将已被接受的子Channel 的EventLoop 传递给Bootstrap的group()方法来共享该EventLoop。因为分配给EventLoop 的所有Channel 都使用同一个线程,所以这避免了额外的线程创建,以及前面所提到的相关的上下文切换。
ServerBootstrap bootstrap = new ServerBootstrap(); // 创建ServerBootstrap 以创建ServerSocketChannel,并绑定它
bootstrap.group(new NioEventLoopGroup(), new NioEventLoopGroup()) // 设置EventLoopGroup,其将提供用 以处理Channel 事件的EventLoop
.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( // 指定Channel的实现
new SimpleChannelInboundHandler<ByteBuf>() {
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
System.out.println("Received data");
}
} );
bootstrap.group(ctx.channel().eventLoop()); // 使用与分配给已被接受的子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();
}
}
} );
编写Netty 应用程序的一个一般准则:尽可能地重用EventLoop,以减少线程创建所带来的开销。
在引导过程中添加多个ChannelHandler
利用ChannelInitializer的initChannel方法添加,添加完之后自动移除ChannelInitializer.
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));
}
}
使用Netty 的ChannelOption 和属性
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(); // 设置属性id
// 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); // 利用ChannelOption 设置channel的属性
bootstrap.attr(id, 123456); // 设置用户ID为123456
ChannelFuture future = bootstrap.connect(new InetSocketAddress("www.manning.com", 80));
future. syncUninterruptibly();
引导DatagramChannel
前面的引导代码示例使用的都是基于TCP 协议的SocketChannel,但是Bootstrap 类也可以被用于无连接的协议。为此,Netty 提供了各种DatagramChannel 的实现。唯一区别就是,不再调用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)); // 调用bind()方法,因为该协议是无连接的
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();
}
}
});
关闭
需要优雅地将它关闭,调用EventLoopGroup.shutdownGracefully()方法.
网友评论