Netty学习笔记06-引导Bootstrap

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

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();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值