高并发网络框架Netty

Netty概述

  Netty是由Jboss提供的一个异步的,基于事件驱动的高性能网络通信开源框架,可以快速的开发高性能,高可靠的网络IO程序,其实就是对Java原生IO的优化和重写。

  Netty主要是针对TCP协议下(TCP/IP -> JDK原生IO -> NIO-> Netty),面向Client客户端的高并发应用,或者P2P场景下大量数据持续传输应用。TCP/IP是Netty的基石。

  Netty本质就是一个NIO框架,它是对NIO的封装和优化,适用于服务端通讯相关的多种应用场景,它简化了网络开发过程,所以可以快速开发一个网络应用。同时也提供了良好的稳定性和伸缩性以及易用性。

  Netty官网:https://netty.io/

  在了解学习Netty之前必须要对NIO熟悉 https://www.cnblogs.com/songgj/p/14398390.html

 

Netty应用场景
  Netty作为一个基础的网络框架应用很广,Netty正是具有高性能,异步非阻塞性能,所以可以作为很多高性能框架底层的通行基础,一般有如下一些场景会使用到:

  1. 互联网行业
    在分布式系统中各个节点之间需要远程服务调用, 高性能的 RPC框架必不可少, Netty 作为异步高性能的通信框架, 往往作为基础通信组件被这些 RPC 框架使用。
    典型的应用有阿里分布式服务框架 Dubbo RPC 框架使用 Dubbo 协议进行节点间通信, Dubbo 协议默认使用 Netty 作为基础通信组件, 用于实现各进程节点之间的内部通信
  2. 游戏行业
    无论是手游服务端还是大型的网络游戏, Java 语言得到了越来越广泛的应用,Netty 作为高性能的基础通信组件, 提供了 TCP/UDP HTTP 协议栈, 方便定制和开发私有协议栈,账号登录服务器,

用户上线/下线感知,比如用户的上线,下线等等。
  3. 地图服务器之间可以方便的通过 Netty 进行高性能的通信。

  4. 大数据领域

   经典的Hadoop的高性能通信和序列化组件Avro的RPC框架,默认采用Netty进行跨界点通信,它的Netty Service基于Netty框架二次封装实现。

   5. 多线程并发领域

   Netty采用Reactor线程模型,Reactor模型采用多路复用器,事件分发器,事件处理器组成。 

  其它的使用到netty项目可以看这个地址 https://netty.io/wiki/related-projects.html

   

 

 

为什么要使用Netty

 原生NIO存在的问题

  NIO 的类库和 API 繁杂,使用麻烦:需要熟练掌握 SelectorServerSocketChannelSocketChannelByteBuffer 等。

  1. 需要具备其他的额外技能:要熟悉 Java 多线程编程,因为 NIO 编程涉及到 Reactor 模式,你必须对多线程和网络编程非常熟悉,才能编写出高质量的 NIO 程序。

  2. 开发工作量和难度都非常大:例如客户端面临断连重连网络闪断半包读写失败缓存网络拥塞和异常流的处理等等。

  3. JDK NIO 的 Bug:例如臭名昭著的 Epoll Bug,它会导致 Selector 空轮询,最终导致 CPU 100%。直到 JDK 1.7 版本该问题仍旧存在,没有被根本解决。

 Netty的优势

  1. API使用简单,定制能力强,可以对框架进行灵活的扩展。

  2. 性能高。

  3. 成熟稳定,Netty修复了NIO的bug,不用担心原生NIO所存在的问题。

  4. 经历大规模商业公司的考验,得到了成功的商用。

 

Netty线程模型

  Netty线程模型也叫做Reactor模型。Reactor模式是基于事件驱动开发的,核心组成部分包括Reactor和线程池,其中Reactor负责监听和分配事件,线程池负责处理事件,而根据Reactor的数量和线程池的数量,Reactor模型分为三种:

  1.  单线程模型:

    所有的IO操作都是由一个NIO线程处理的。

 

   这里上图中Reactor是一个单线程,当三个蓝色客户端请求过来,由这个Reactor来处理,Reactor内部通过selector监控连接事件,收到事件后通过dispatch进行分发,如果是连接建立的事件,则由Acceptor(绿色的)处理,Acceptor通过accept接受连接,并创建一个Handler来处理连接后续的各种事件, 如果是读写事件,直接调用连接对应的Handler来处理,Handler就是完成一些具体的业务,比如编码,计算,获取数据等。

   这种线程模型使用的是异步非阻塞。这个模型仅仅限于用在一些小型应用场景下是没问题的,但是在一些高负载的情况下是不合适的,单线程会负荷过重会处理不过来,会导致客户端超时,当某个Handler阻塞时,会导致其他客户端的handler和accpetor都得不到执行,无法做到高性能,只适用于业务处理非常快速的场景。

  2. 多线程模型:

    由一组NIO线程处理IO操作。

    

 

  多线程模型比上面上面一个Reactor单线程增加了一个Reactor线程池,就是上图红色的Thread Pool。上面黄色的单个Reactor线程只用来监听处理客户端的连接请求,然后在转发给后面的Thread Pool线程池,线程池用来负责处理读写请求。主线程中Reactor对象通过selector监控连接事件, 收到事件后通过dispatch进行分发,如果是连接建立事件,则由Acceptor处理,Acceptor通过accept接收连接,并创建一个Handler来处理后续事件,而Handler只负责响应事件,不进行业务操作,也就是只进行read读取数据和write写出数据,业务处理交给一个线程池进行处理,线程池分配一个线程完成真正的业务处理,然后将响应结果交给主进程的Handler处理,Handler将结果send给客户端。

 

   3. 主从多线程模型

    

 

   这个模型下有一个主线程池和从线程池,服务端用于接收客户端的连接不再是一个单独的NIO线程,而是改成多线程,称为主线程池,主线程池用于接收客户端的请求,处理客户端连接,登陆,安全校验等,然后将请求转发给后面的从线程池。在netty这两个线程池都是NioEventLoopGroup。

   主线程中的mainReactor通过自己的selector监控连接建立事件,收到事件后通过Accpetor接收,将新的连接分配给某个子线程,子线程中的subReactor将mainReactor分配的连接加入连接队列中通过自己的selector进行监听,并创建一个Handler用于处理后续事件。Handler完成read->业务处理->send的完整业务流程。

      整个流程如下:

  • Reactor主线程 MainReactor 对象通过 select 监听连接事件,收到事件后,通过 Acceptor 处理连接事件。
  • 当 Acceptor 处理连接事件后,MainReactor 将连接分配给 SubReactor。
  • SubReactor 将连接加入到连接队列进行监听,并创建 Handler 进行各种事件处理。
  • 当有新事件发生时,SubReactor 就会调用对应的 Handler 进行处理。
  • Handler 通过 read 读取数据后,会分发给后面的worker线程池的某个线程处理业务。
  • worker线程池会分配一个独立线程完成真正的业务,并将结果返回给 Handler。
  • Handler 收到响应后,通过 send 将结果返回给客户端。
  • Reactor主线程可以对应多个 Reactor子线程,即 MainReactor 可以关联多个 SubReactor 。

 

  主从多线程模型也是Netty官方推荐的模型。

 

 

Netty服务开发

  先来看看使用netty开发步骤:

  1. 我们使用官方推荐的方式,构建一对主从线程池。

  2. 定义服务启动类。

  3. 为服务器设置channel。

  4. 设置处理从线程的助手类初始化。

  5. 监听启动和关闭服务器。

 

 

 

 

 

 

 

 

参考来源

  http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf

 

posted @ 2021-02-16 18:35  songguojun  阅读(856)  评论(0编辑  收藏  举报