netty(三)从启动流程看NettyNIOServer

一、服务端启动的最小化代码分析

  前面一章我们学习了netty实现的NIO服务端与客户端,今天我们单独把服务端代码拿出来好好分析一下,经过精简后,服务端启动的最小化代码如下:

复制代码
/**
 * Netty实现的NIO服务端【服务端启动的最小化代码】
 * 
 * 三要素:
 * --1、线程模型
 * --2、IO模型
 * --3、连接读写处理逻辑
 *
 * @author 有梦想的肥宅
 * @date 2022/5/2
 */
public class NettyNIOServer {
    public static void main(String[] args) {
        //1、创建服务端启动引导类,用于引导服务端的启动工作
        ServerBootstrap serverBootstrap = new ServerBootstrap();

        //2、创建NioEventLoopGroup对象,作用类似线程组
        NioEventLoopGroup boss = new NioEventLoopGroup();//用于获取链接的线程组
        NioEventLoopGroup worker = new NioEventLoopGroup();//用于获取数据的线程组

        //3、配置服务端启动引导类
        serverBootstrap
                .group(boss, worker)//3.1 配置两大线程组【要素1:配置线程模型】
                .channel(NioServerSocketChannel.class)//3.2 配置NIO模型【要素2:配置IO模型】
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    //3.3 定义每个链接的数据读写逻辑【要素3:配置连接读写处理逻辑】
                    @Override
                    protected void initChannel(NioSocketChannel ch) {

                    }
                }).bind(8000);//3.4 绑定8000端口
    }
}
复制代码

  从上面的代码我们可以看到,启动流程中最核心的部分主要就是三要素的配置,简单归纳为:

  • 1、线程模型NioEventLoopGroup
  • 2、IO模型NioServerSocketChannel
  • 3、连接读写处理逻辑重写ChannelInitializer的抽象方法

二、动态绑定端口

  当端口冲突的时候,一般服务器会自动进行端口递增并尝试重启,那么代码内部是怎么实现的呢?如下所示~

复制代码
/**
 * Netty实现的NIO服务端【动态绑定端口】
 *
 * @author 有梦想的肥宅
 * @date 2022/5/2
 */
public class NettyNIOServerDynamicBind {
    public static void main(String[] args) {
        //1、创建服务端启动引导类,用于引导服务端的启动工作
        ServerBootstrap serverBootstrap = new ServerBootstrap();

        //2、创建NioEventLoopGroup对象,作用类似线程组
        NioEventLoopGroup boss = new NioEventLoopGroup();//用于获取链接的线程组
        NioEventLoopGroup worker = new NioEventLoopGroup();//用于获取数据的线程组

        //3、配置服务端启动引导类
        serverBootstrap
                .group(boss, worker)//3.1 配置两大线程组【要素1:配置线程模型】
                .channel(NioServerSocketChannel.class)//3.2 配置NIO模型【要素2:配置IO模型】
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    //3.3 定义每个链接的数据读写逻辑【要素3:配置连接读写处理逻辑】
                    @Override
                    protected void initChannel(NioSocketChannel ch) {

                    }
                });

        //4、动态绑定端口
        bind(serverBootstrap, 8000);
    }

    /**
     * 动态绑定端口的方法
     *
     * @param serverBootstrap
     * @param port
     */
    public static void bind(ServerBootstrap serverBootstrap, int port) {
        //1、由于bind方法是异步调用的,这里我们设置一个监听器,来监听端口是否绑定成功
        serverBootstrap.bind(port).addListener(new GenericFutureListener<Future<? super Void>>() {
            //2、监听方法内对端口绑定事件的结果进行处理
            @Override
            public void operationComplete(Future<? super Void> future) throws Exception {
                if (future.isSuccess()) {
                    System.out.println("端口【" + port + "】绑定成功!");
                } else {
                    System.out.println("端口【" + port + "】绑定失败!重新进行绑定~");
                    //3、绑定失败时,端口+1后进行递归调用
                    bind(serverBootstrap, port + 1);
                }
            }
        });
    }
}
复制代码

  可以看到,当8000端口被占用时,程序会自动将端口+1并尝试重新绑定,这样服务端就不会因为相同端口被占用而导致直接启动失败了。

三、服务端启动的主干方法之外的一些方法解析

  除了上面的主干方法以外,netty的服务端还为我们提供了一些其余方法来丰富我们的服务端功能,具体使用方式如下,由于使用频率不高,这里我们做个简单了解即可:

复制代码
/**
 * Netty实现的NIO服务端【其余方法用法解析】
 *
 * @author 有梦想的肥宅
 * @date 2022/5/2
 */
public class NettyNIOServerOtherMethod {
    public static void main(String[] args) {
        //1、创建服务端启动引导类,用于引导服务端的启动工作
        ServerBootstrap serverBootstrap = new ServerBootstrap();

        /*todo 中间代码省略,除了启动的核心代码外,一些其他方法的使用介绍如下*/

        //2、handler()【比较少用】
        //可以与childHandler【指定新连接数据的读写处理逻辑】可以一起联想理解,handler【指定服务端启动时执行的一些方法】
        serverBootstrap.handler(new ChannelInitializer<NioSocketChannel>() {
            @Override
            protected void initChannel(NioSocketChannel ch) {
                System.out.println("服务端正在启动~");
            }
        });

        //3、attr()【比较少用】
        //用于给服务端的IO模型设置属性,即给NioServerSocketChannel设置属性
        serverBootstrap.attr(AttributeKey.newInstance("serverName"), "有梦想的肥宅的服务器");

        //4、childAttr()【比较少用】
        //用于给每一个链接设置属性
        serverBootstrap.childAttr(AttributeKey.newInstance("connectName"), "有梦想的肥宅的链接");

        //5、option()【调优用】
        //用于给服务端的IO模型设置TCP属性
        serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024);//临时存放已完成3次握手的TCP请求队列的最大长度

        //6、childOption()【调优用】
        //可以给每个链接设置TCP属性
        serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);//开启TCP底层心跳机制
        serverBootstrap.childOption(ChannelOption.TCP_NODELAY, true);//开启Nagle算法【Nagle算法通过减少需要传输的数据包,来优化网络;提高实时性:true 提高网络利用率:false】
    }
}
复制代码

 

posted @   有梦想的肥宅  阅读(191)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示