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】 } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律