Netty入门以及粘包拆包

NIO编程

关于NIO相关的文章网上也有很多,这里不打算详细深入分析,下面简单描述一下NIO是如何解决BIO的线程资源受限,线程切换效率低下,以字节为单位三个问题的。

1. 解决线程资源受限

NIO编程模型中,新来一个连接不再创建一个新的线程,而是可以把这条连接直接绑定到某个固定的线程,然后这条连接所有的读写都由这个线程来负责,那么他是怎么做到的?我们用一幅图来对比一下IO与NIO

在这里插入图片描述

​ 如上图所示,IO模型中,一个连接来了,会创建一个线程,对应一个while死循环,死循环的目的就是不断监测这条连接上是否有数据可以读,大多数情况下,1w个连接里面同一时刻只有少量的连接有数据可读,因此,很多个while死循环都白白浪费掉了,因为读不出啥数据。

​ 而在NIO模型中,他把这么多while死循环变成一个死循环,这个死循环由一个线程控制,那么他又是如何做到一个线程,一个while死循环就能监测1w个连接是否有数据可读的呢? 这就是NIO模型中selector的作用,一条连接来了之后,现在不创建一个while死循环去监听是否有数据可读了,而是直接把这条连接注册到selector上,然后,通过检查这个selector,就可以批量监测出有数据可读的连接,进而读取数据,下面我再举个非常简单的生活中的例子说明IO与NIO的区别。

在一家幼儿园里,小朋友有上厕所的需求,小朋友都太小以至于你要问他要不要上厕所,他才会告诉你。幼儿园一共有100个小朋友,有两种方案可以解决小朋友上厕所的问题:

  1. 每个小朋友配一个老师。每个老师隔段时间询问小朋友是否要上厕所,如果要上,就领他去厕所,100个小朋友就需要100个老师来询问,并且每个小朋友上厕所的时候都需要一个老师领着他去上,这就是IO模型,一个连接对应一个线程。
  2. 所有的小朋友都配同一个老师。这个老师隔段时间询问所有的小朋友是否有人要上厕所,然后每一时刻把所有要上厕所的小朋友批量领到厕所,这就是NIO模型,所有小朋友都注册到同一个老师,对应的就是所有的连接都注册到一个线程,然后批量轮询。

2. 解决线程切换效率低下

由于NIO模型中线程数量大大降低,线程切换效率因此也大幅度提高

3. 解决IO读写以字节为单位

NIO解决这个问题的方式是数据读写不再以字节为单位,而是以字节块为单位。IO模型中,每次都是从操作系统底层一个字节一个字节地读取数据,而NIO维护一个缓冲区,每次可以从这个缓冲区里面读取一块的数据, 这就好比一盘美味的豆子放在你面前,你用筷子一个个夹(每次一个),肯定不如要勺子挖着吃(每次一批)效率来得高。

简单讲完了JDK NIO的解决方案之后,我们接下来使用NIO的方案替换掉IO的方案,我们先来看看,如果用JDK原生的NIO来实现服务端,该怎么做。

Netty编程

1.netty简介

在这里插入图片描述

用一句简单的话来说就是:Netty封装了JDK的NIO,让你用得更爽,你不用再写一大堆复杂的代码了。 用官方正式的话来说就是:Netty是一个异步事件驱动的网络应用框架,用于快速开发可维护的高性能服务器和客户端。

下面是我总结的使用Netty不使用JDK原生NIO的原因

  • 使用JDK自带的NIO需要了解太多的概念,编程复杂,一不小心bug横飞
  • Netty底层IO模型随意切换,而这一切只需要做微小的改动,改改参数,Netty可以直接从NIO模型变身为IO模型
  • Netty自带的拆包解包,异常检测等机制让你从NIO的繁重细节中脱离出来,让你只需要关心业务逻辑
  • Netty解决了JDK的很多包括空轮询在内的bug
  • Netty底层对线程,selector做了很多细小的优化,精心设计的reactor线程模型做到非常高效的并发处理
  • 自带各种协议栈让你处理任何一种通用协议都几乎不用亲自动手
  • Netty社区活跃,遇到问题随时邮件列表或者issue
  • Netty已经历各大rpc框架,消息中间件,分布式通信中间件线上的广泛验证,健壮性无比强大

2.netty的使用

  1. 首先引入maven依赖
    在这里插入图片描述

也可以 fail ->project Structure -> Modules ->dependencies 右侧的+号

Library ->new Library -> from maven 输入 io.netty:netty-all

  1. 服务端的实现:

    NettyServer.java
    
   package java_netty.simple;
   import io.netty.bootstrap.ServerBootstrap;
   import io.netty.channel.ChannelFuture;
   import io.netty.channel.ChannelInitializer;
   import io.netty.channel.ChannelOption;
   import io.netty.channel.EventLoopGroup;
   import io.netty.channel.nio.NioEventLoopGroup;
   import io.netty.channel.socket.SocketChannel;
   import io.netty.channel.socket.nio.NioServerSocketChannel;
   public class NettyServer {
   
       public static void main(String[] args) throws InterruptedException {
   
           //创建BossGroup 和WokerGroup
           //说明:
           //1.创建两个线程组 bossgroup和workergroup
           //2.BossGroup 只是处理连接请求
           //3.wokergroup 真正和客户端业务处理
           //4.两个都是无限循环
           //5. bossGroup, 和 workerGroup 含有的子线程(NioEventLoop)的个数
           // 默认实际( cpu核数 * 2)
          EventLoopGroup bossGroup=new NioEventLoopGroup(1);
          EventLoopGroup workerGroup=new NioEventLoopGroup();
          try {
   
          //创建服务器端的启动对象,配置参数
           ServerBootstrap bootstrap = new ServerBootstrap();
           //使用链式编程进行设置
           bootstrap.group(bossGroup,workerGroup)//设置两个线程组
                    .channel(NioServerSocketChannel.class)//使用NioSocketChannel 作为服务器的通道实现
                    .option(ChannelOption.SO_BACKLOG,128)//设置线程队列得到连接个数
                    .childOption(ChannelOption.SO_KEEPALIVE,true)//设置保持活动连接状态
                    .childHandler(new ChannelInitializer<SocketChannel>() {
   //创建一个通道测试对象(匿名类)
                        //给pipeline 设置处理器
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
   
                            socketChannel.pipeline().addLast(new NettyServerHandler());//给管道的最后添加一个处理器(即写的NettyServerHandler)
                        }
                    });// 给我们的workergroup 的EventLoopGroup 对应的管道设置处理器
           System.out.println("服务器 is ready");
           //绑定一个端口,并且同步处理,生成了一个ChannelFuture对象。
           //相当于启动服务器并把端口端口
           ChannelFuture channelFuture = bootstrap.bind(6668).sync();
           //对关闭通道进行监听(当有关闭操作的时候会进行监听
           channelFuture.channel().closeFuture().sync();
       }finally {
   
              bossGroup.shutdownGracefully();//优雅的关闭
              workerGroup.shutdownGracefully();//优雅的关闭
          }
          }
   }
  • boos对应了 IOServer.java中的接收新连接线程,主要负责创建新连接

  • worker对应 IOClient.java中的负责读取数据的线程,主要用于读取数据以及业务逻辑处理

        <!--说明:-->
        <!--1.创建两个线程组 bossgroup和workergroup-->
        <!--2.`BossGroup` 只是处理连接请求-->
        <!--3.`wokergroup` 真正和客户端业务处理-->
        <!--4.两个都是无限循环-->
    
     <!--5. bossGroup`, 和 workerGroup 含有的子线程(NioEventLoop)的个数-->
        <!--( cpu核数 * 2)-->
    
  1. 服务端的handler处理器

    NettyServerHandler.java

   package java_netty.simple;
   
   import io.netty.buffer.ByteBuf;
   import io.netty.buffer.Unpooled;
   import io.netty.channel.ChannelHandlerContext;
   import io.netty.channel.ChannelInboundHandlerAdapter;
   import io.netty.util.CharsetUtil;
   
   /*
   说明:
   1.自定义一个Handler 需要继续netty, 规定好某个HandlerAdapter
   2.这时的Handler 才能算一个Hander
    */
   public class NettyServerHandler extends ChannelInboundHandlerAdapter {
   
   
       //读取数据实际,&#x
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值