一个 Netty 应用通常由一个 Bootstrap 开始,主要作用是配置整个 Netty 程序,串联各个组件,而客户端和服务端这两种应用程序间通用的引导步骤有AbstractBootstrap处理,Netty 中 Bootstrap 类是客户端程序的启动引导类,ServerBootstrap 是服务端启动引导类;

 

引导类的层次结构

Netty 启动引导类组件作用

 

为何引导类要实现Cloneable接口?

有时可能会需要创建多个具有类似配置或者完全相同配置的Channel(如链式调用那里添加配置);为了支持这种模式而又不需要为每个Channel都创建并配置一个新的引导类实例,AbstractBootstrap被标记为Cloneable的引导类实例;

注意,这种方式只会创建引导类实例的EventLoopGroup的一个浅拷贝,所以被浅拷贝的EventLoopGroup将在所有克隆的Channel实例之间共享;这是可以接受的,因为通常这些克隆的Channel的生命周期都很短暂;如创建一个Channel以进行一次HTTP请求;

 

Netty 启动引导类组件作用

对于AbstractBootstrap的子类型 B 是其父类型的一个类型参数,因此可以返回到运行时实例的引用以支持方法的链式调用(也就是所谓的流式语法

 

引导客户端

Bootstrap 类负责为客户端和使用无连接协议的应用程序创建 Channel;
Netty 启动引导类组件作用

 

使用如下:

public class NettyClient {
    public static void main(String[] args) {
        final int port = 19000;
        final String local = "127.0.0.1";
        EventLoopGroup group = new NioEventLoopGroup();

        try {
            // 创建一个BootStrap类的实例以创建和连接新的Channel
            Bootstrap bootstrap = new Bootstrap();

            // 链式写法
            // 设置EventLoopGroup,提供用于处理Channel事件的EventLoop
            bootstrap.group(group)
                    // 指定要使用的Channel实现
                    .channel(NioSocketChannel.class)
                    // 设置用于用于处理Channel事件的Handler
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new NettyClientHandler());
                        }
                    });

            // 连接服务端
            ChannelFuture channelFuture = bootstrap.connect(local, port).sync();
            channelFuture.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            group.shutdownGracefully();
        }
    }
}

除了 connect()方法以外, BootStrap的其他方法将通过每次方法调用所返回Bootstrap 实例的引用;

在引导的过程中,在调用 bind()或者 connect()方法之前,必须调用以下方法来设置所需的组件(如果不这样做, 则将会导致 IllegalStateException):

  • group();
  • channel()或者 channelFactory();
  • handler();需要通过该方法配置 ChannelPipeline;

 

引导服务器

ServerBootstrap 是服务端启动引导类;

ServerBootstrap 在 bind()方法被调用时创建了一个 ServerChannel,并且该 ServerChannel 管理了多个子 Channel;

 

Netty 启动引导类组件作用

 

 

ServerChannel的实现负责创建子 Channel,这些子Channel 代表了已被接受的连接(已连接的客户端)

负责引导 ServerChannel 的 ServerBootstrap 提供了的方法,以简化将设置应用到已被接受的子Channel的ChannelConfig 的任务;

 

使用如下:

public class NettyServer {

    private final static Logger logger = LoggerFactory.getLogger(NettyServer.class);

    public static void main(String[] args) {
        // 创建两个线程组bossGroup和workerGroup,含有子线程NioEventLoop的个数默认为cpu核数的两倍
        // bossGroup只处理连接请求,业务处理交由wokerGroup
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        final int port = 19000;

        try {
            // 创建服务的启动对象
            ServerBootstrap bootstrap = new ServerBootstrap();

            // 设置两个线程组
            bootstrap.group(bossGroup, workerGroup)
                    // 指定使用NioServerSocketChannel作为服务器的通道实现
                    .channel(NioServerSocketChannel.class)
                    // 初始化服务器连接队列大小,服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接
                    // 多个客户端同时来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        //创建通道初始化对象,设置初始化参数
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            //对workerGroup的SocketChannel设置处理器codec
                            ch.pipeline().addLast(new NettyServerHandler());
                        }
                    });

            logger.info("netty server start...");

            // 通过配置好ServerBootStrap的实例绑定该Channel
            // 启动服务器(并绑定端口),bind是异步操作,sync方法是等待异步操作执行完毕,这里会阻塞住
            ChannelFuture channelFuture = bootstrap.bind(port).sync();

            // 添加监听器
            channelFuture.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    if (future.isSuccess()) {
                        logger.info("监听端口" + port + "成功");
                    } else {
                        logger.info("监听失败...");
                    }
                }
            });

            channelFuture.channel().closeFuture().sync();
        } catch (Exception e) {
            logger.error("异常..", e);
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

 

如果服务器正在处理一个客户端的请求,这个请求需要它充当第三方系统的客户端;
当一个应用程序(如一个代理服务器)必须要和组织现有的系统(如 Web 服务或者数据库)集成时,就可能发生这种情况; 在这种情况下,将需要从已经被接受的子 Channel 中引导一个客户端 Channel;

 

如果创建新的BootStrap实例,并且每个新创建的客户端 Channel 定义另一个 EventLoop,这会产生额外的线程,以及在已被接受的子 Channel 和客户端 Channel 之间交换数据时不可避免的上下文切换;


通过将已被接受的子 Channel(已连接的客户端) EventLoop 传递给 Bootstrap的 group()方法来共享该 EventLoop;因为分配给 EventLoop 的所有 Channel 都使用同一个线程,所以这避免了额外的线程创建和Channel之间数据交换时的上下文切换;如下图;

Netty 启动引导类组件作用

  

写法如下:

Netty 启动引导类组件作用

 

 

相关文章:

  • 2022-12-23
  • 2021-08-11
  • 2022-12-23
  • 2022-12-23
  • 2021-12-02
  • 2021-04-17
  • 2022-12-23
  • 2021-12-05
猜你喜欢
  • 2021-09-26
  • 2022-02-19
  • 2021-10-20
  • 2021-12-06
  • 2021-09-24
  • 2021-08-07
  • 2021-08-15
相关资源
相似解决方案