为什么要使用Netty

有兴趣的同学可以移步笔者的个人博客 更多博客

为什么使用netty

Netty是一个网络通信框架,其出现的原因主要是为了解决NIO的不足。如:

  1. NIO的类库和API繁杂,使用麻烦,你需要熟练掌握Selector、ServerSocketChannel、SocketChannel、ByteBuffer等;
  2. 需要具备其它的额外技能做铺垫,例如熟悉Java多线程编程,因为NIO编程涉及到Reactor模式,你必须对多线程和网路编程非常熟悉,才能编写出高质量的NIO程序;
  3. 可靠性能力补齐,工作量和难度都非常大。例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常码流的处理等等,NIO编程的特点是功能开发相对容易,但是可靠性能力补齐工作量和难度都非常大;

NIO(Non-blocking I/O,在Java领域,也称为New I/O),是一种同步非阻塞的I/O模型,也是I/O多路复用的基础,已经被越来越多地应用到大型应用服务器,成为解决高并发与大量连接、I/O处理问题的有效方式。

本文会从传统的阻塞I/O和线程池模型面临的问题讲起,然后对比几种常见I/O模型,一步步分析NIO怎么利用事件模型处理I/O,解决线程池瓶颈处理海量连接,包括利用面向事件的方式编写服务端/客户端程序。

传统BIO模型分析
{
 ExecutorService executor = Excutors.newFixedThreadPollExecutor(100);//线程池
 ServerSocket serverSocket = new ServerSocket();
 serverSocket.bind(8088);
 while(!Thread.currentThread.isInturrupted()){//主线程死循环等待新连接到来
 Socket socket = serverSocket.accept();
 executor.submit(new ConnectIOnHandler(socket));//为新的连接创建新的线程
}
class ConnectIOnHandler extends Thread{
    private Socket socket;
    public ConnectIOnHandler(Socket socket){
       this.socket = socket;
    }
    public void run(){
      while(!Thread.currentThread.isInturrupted()&&!socket.isClosed()){死循环处理读写事件
          String someThing = socket.read()//读取数据
          if(someThing!=null){
             //处理数据
             socket.write()//写数据
          }
      }
    }
}

上面的代码就是典型的传统BIO服务端监听代码,由于accept()read()write() 这三个方法是阻塞的、耗时的。如果是单线程的话,在执行阻塞代码是会使cpu空等,并且最重要的是,如果有10000个并发的话,那么等待时间是不能够接受的。

使用多线程的本质就是:

  1. 利用多核。
  2. 当I/O阻塞系统,但CPU空闲的时候,可以利用多线程使用CPU资源。

但是传统IO在使用多线程解决上面的问题时会严重依赖于线程,意味着每个请求都要建立一个线程,当客户端数量达到万级时,就需要建立万级的线程。线程占用的内存,切换线程资源昂贵,最终导致这种方案无法保证系统的伸缩性。

传统NIO模型分析

所有的系统I/O都分为两个阶段:等待就绪和操作。举例来说,读函数,分为等待系统可读和真正的读;同理,写函数分为等待网卡可以写和真正的写。

需要说明的是等待就绪的阻塞是不使用CPU的,是在“空等”;而真正的读写操作的阻塞是使用CPU的,真正在"干活",而且这个过程非常快,属于memory copy,带宽通常在1GB/s级别以上,可以理解为基本不耗时。


interface ChannelHandler{
     void channelReadable(Channel channel);
     void channelWritable(Channel channel);
  }
  class Channel{
    Socket socket;
    Event event;//读,写或者连接
  }

  //IO线程主循环:
  class IoThread extends Thread{
  public void run(){
  Channel channel;
  while(channel=Selector.select()){//选择就绪的事件和对应的连接
     if(channel.event==accept){
        registerNewChannelHandler(channel);//如果是新连接,则注册一个新的读写处理器
     }
     if(channel.event==write){
        getChannelHandler(channel).channelWritable(channel);//如果可以写,则执行写事件
     }
     if(channel.event==read){
         getChannelHandler(channel).channelReadable(channel);//如果可以读,则执行读事件
     }
   }
  }
  Map<Channel,ChannelHandler> handlerMap;//所有channel的对应事件处理器
 }

NIO由原来的阻塞读写(占用线程)变成了单线程轮询事件,找到可以进行读写的网络描述符进行读写。除了事件的轮询是阻塞的(没有可干的事情必须要阻塞),剩余的I/O操作都是纯CPU操作,没有必要开启多线程。

### Netty 的功能及使用原因 Netty 是一个由 JBOSS 提供的 Java 开源网络编程框架,它基于 NIO(非阻塞 I/O)实现,提供异步和基于事件驱动的网络通信能力。该框架的设计目标是简化和流程化网络应用的开发,使得开发者能够快速构建高性能、高可靠性的网络应用程序[^1]。 Netty 的核心功能包括: - **异步通信支持**:Netty 使用事件驱动模型处理网络请求,允许非阻塞方式处理大量的并发连接[^2]。 - **高性能网络处理**:通过封装底层的 NIO API,Netty 使得开发者可以专注于业务逻辑的实现,而无需过多关注底层网络细节。 - **灵活的协议支持**:Netty 支持多种网络协议,包括 TCP、UDP 和 HTTP 等,并且可以轻松地进行扩展以支持自定义协议[^2]。 - **丰富的功能模块**:Netty 提供了诸如编解码器、SSL/TLS 支持、流量整形等模块,以满足不同场景下的网络通信需求[^4]。 使用 Netty 的主要原因包括: - **简化 NIO 编程**:Netty 对 Java NIO 进行了高度封装,提供了更简洁的 API,使得开发者能够更容易地编写网络应用程序。 - **提高开发效率**:由于 Netty 提供了大量的工具类和组件,开发者可以快速搭建起一个功能完善的网络服务[^4]。 - **高性能**:Netty 被设计为高性能的网络框架,它能够有效地利用系统资源,提供低延迟和高吞吐量的数据传输[^3]。 - **高可靠性与可维护性**:Netty 的设计模式和架构有助于构建易于维护和扩展的应用程序[^4]。 综上所述,Netty 不仅简化了网络编程的复杂度,还提供了构建现代高性能网络应用所需的多种特性,这使得它成为了许多大型分布式系统和服务的首选网络框架。 ```java // 示例代码:Netty 创建一个简单的服务器 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 Exception { // 创建两个事件循环组 EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new MyServerHandler()); } }) .option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true); // 绑定端口,同步等待成功 ChannelFuture f = b.bind(8080).sync(); // 等待服务端监听端口关闭 f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值