Code Monkey home page Code Monkey logo

nettytree's Introduction

NettyTree 搭建一个基于Netty的通信框架

NIO:非阻塞式IO

BIO:阻塞式IO

  1) TCP粘包、拆包
  2)编解码技术
     1)Java序列化
     2)业界主流的编解码框架 
           Thrift Protobuf
  3) Websocket
  5)Netty协议栈功能设计
  6)Netty源码分析
        ByteBuf工作原理
        Channel, Unsafe
        ChannelPipline, ChannelHandler
        EventLoop, EventLoopGroup
        Future, Promise
  7) Netty逻辑架构
  8)Netty中的多线程编程
  9)Netty与RPC
  10)Netty的可靠性
Reactor模型:基于事件驱动,适合处理海量I/O事件

    1)单线程模型,所有的IO操作都在一个NIO线程上完成
       存在性能和可靠性上的问题
    2)多线程模型,有一组NIO线程处理IO操作
       有一个专门的NIO线程-Acceptor线程用于监听服务端,接收客户端的TCP连接请求;
       有一个NIO线程池,负责消息的读取、发送、编码、解码;
       一个NIO线程能负责N条链路,一条链路只能由一个线程负责(防止发生并发操作问题)
    3)主从多线程模型
       添加主线程池用于处理客户端的连接请求,一旦链路建立成功(经过握手、认证等过程)
          ,就将链路注册到从线程池的IO线程上,由IO线程负责后续的IO操作

Netty是典型的Reactor模型结构

Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具, 用以快速开发高性能、高可靠性的网络服务器和客户端程序。也就是说,Netty是一个基于NIO的客户, 服务器端编程框架,通俗的讲,Netty封装了Socket处理,方便使用了。

          传统RPC框架或者基于RMI等方式的远程调用采用了同步阻塞IO,当客户端的并发压力或者
      网络时延增大之后,同步阻塞IO会由于频繁的wait导致IO线程经常性的阻塞,由于线程无法高效
      的工作,IO处理能力自然下架。

          采用BIO通信模型的服务端,通常由一个独立的Acceptor线程负责监听客户端的连接,接收
      到客户端连接之后为客户端连接创建一个新的线程处理请求消息,处理完成之后,返回应答消息
      给客户端,线程销毁,这就是典型的一请求一应答模型。该架构最大的问题就是不具备弹性伸缩能
      力,当并发访问量增加后,服务端的线程个数和并发访问数成线性正比,由于线程是JAVA虚拟机
      非常宝贵的系统资源,当线程数膨胀之后,系统的性能急剧下降,随着并发量的继续增加,可能
      会发生句柄溢出、线程堆栈溢出等问题,并导致服务器最终宕机。

      序列化方式问题:Java序列化存在如下几个典型问题:
          1) Java序列化机制是Java内部的一种对象编解码技术,无法跨语言使用;例如对于异构系
             统之间的对接,Java序列化后的码流需要能够通过其它语言反序列化成原始对象(副
             本),目前很难支持;

          2) 相比于其它开源的序列化框架,Java序列化后的码流太大,无论是网络传输还是持久化
             到磁盘,都会导致额外的资源占用;

          3) 序列化性能差(CPU资源占用高)。

      高性能的三个主题
          1) 传输:用什么样的通道将数据发送给对方,BIO、NIO或者AIO,IO模型在很大程度上决
             定了框架的性能。

          2) 协议:采用什么样的通信协议,HTTP或者内部私有协议。协议的选择不同,性能模型也
             不同。相比于公有协议,内部私有协议的性能通常可以被设计的更优。

          3) 线程:数据报如何读取?读取之后的编解码在哪个线程进行,编解码后的消息如何派
             发,Reactor线程模型的不同,对性能的影响也非常大。
Netty的“零拷贝”主要体现在如下三个方面:

    1) Netty的接收和发送ByteBuffer采用DIRECT BUFFERS,使用堆外直接内存进行Socket读写,
       不需要进行字节缓冲区的二次拷贝。如果使用传统的堆内存(HEAP BUFFERS)进行Socket读
       写,JVM会将堆内存Buffer拷贝一份到直接内存中,然后才写入Socket中。相比于堆外直接内
       存,消息在发送过程中多了一次缓冲区的内存拷贝。

    2) Netty提供了组合Buffer对象,可以聚合多个ByteBuffer对象,用户可以像操作一个Buffer
       那样方便的对组合Buffer进行操作,避免了传统通过内存拷贝的方式将几个小Buffer合并成一
       个大的Buffer。

    3) Netty的文件传输采用了transferTo方法,它可以直接将文件缓冲区的数据发送到目标 Channel,
       避免了传统通过循环write方式导致的内存拷贝问题。
高效的Reactor线程模型

      Reactor多线程模型的特点:

         1) 有专门一个NIO线程-Acceptor线程用于监听服务端,接收客户端的TCP连接请求;

         2) 网络IO操作-读、写等由一个NIO线程池负责,线程池可以采用标准的JDK线程池实现,它包含一个
            任务队列和N个可用的线程,由这些NIO线程负责消息的读取、解码、编码和发送;

         3) 1个NIO线程可以同时处理N条链路,但是1个链路只对应1个NIO线程,防止发生并发操作问题。

         在绝大多数场景下,Reactor多线程模型都可以满足性能需求;但是,在极特殊应用场景中,一个
         NIO线程负责监听和处理所有的客户端连接可能会存在性能问题。例如百万客户端并发连接,或者服务
         端需要对客户端的握手消息进行安全认证,认证本身非常损耗性能。在这类场景下,单独一个
         Acceptor线程可能会存在性能不足问题,为了解决性能问题,产生了第三种Reactor线程模型-主从
         Reactor多线程模型。

         主从Reactor线程模型的特点是:服务端用于接收客户端连接的不再是个1个单独的NIO线程,而是一
         个独立的NIO线程池。Acceptor接收到客户端TCP连接请求处理完成后(可能包含接入认证等),将新
         创建的SocketChannel注册到IO线程池(sub reactor线程池)的某个IO线程上,由它负责
         SocketChannel的读写和编解码工作。Acceptor线程池仅仅只用于客户端的登陆、握手和安全认证,
         一旦链路建立成功,就将链路注册到后端subReactor线程池的IO线程上,由IO线程负责后续的IO操作
无锁化的串行设计理念

      在大多数场景下,并行多线程处理可以提升系统的并发性能。但是,如果对于共享资源的并发访问处理不
      当,会带来严重的锁竞争,这最终会导致性能的下降。为了尽可能的避免锁竞争带来的性能损耗,可以通
      过串行化设计,即消息的处理尽可能在同一个线程内完成,期间不进行线程切换,这样就避免了多线程竞
      争和同步锁。

      为了尽可能提升性能,Netty采用了串行无锁化设计,在IO线程内部进行串行操作,避免多线程竞争导致
      的性能下降。表面上看,串行化设计似乎CPU利用率不高,并发程度不够。但是,通过调整NIO线程池的线
      程参数,可以同时启动多个串行化的线程并行运行,这种局部无锁化的串行线程设计相比一个队列-多个
      工作线程模型性能更优。
高效的并发编程

      Netty的高效并发编程主要体现在如下几点:

      1) volatile的大量、正确使用;

      2) CAS和原子类的广泛使用;

      3) 线程安全容器的使用;

      4) 通过读写锁提升并发性能。
高性能的序列化框架

      影响序列化性能的关键因素总结如下:
          1) 序列化后的码流大小(网络带宽的占用);
          2) 序列化&反序列化的性能(CPU资源占用);
          3) 是否支持跨语言(异构系统的对接和开发语言切换)
灵活的TCP参数配置能力

     合理设置TCP参数在某些场景下对于性能的提升可以起到显著的效果,例如SO_RCVBUF和SO_SNDBUF。如果设置不当,对性能的影响是非常大的。下面我们总结下对性能影响比较大的几个配置项:

        1) SO_RCVBUF和SO_SNDBUF:通常建议值为128K或者256K;

        2) SO_TCPNODELAY:NAGLE算法通过将缓冲区内的小封包自动相连,组成较大的封包,阻止大量小
           封包的发送阻塞网络,从而提高网络应用效率。但是对于时延敏感的应用场景需要关闭该优化算法;

        3) 软中断:如果Linux内核版本支持RPS(2.6.35以上版本),开启RPS后可以实现软中断,提升网
           络吞吐量。RPS根据数据包的源地址,目的地址以及目的和源端口,计算出一个hash值,然后根据
           这个hash值来选择软中断运行的cpu,从上层来看,也就是说将每个连接和cpu绑定,并通过这个
           hash值,来均衡软中断在多个cpu上,提升网络并行处理性能。

nettytree's People

Contributors

qiuqiuxiaomaomi avatar

Watchers

James Cloos avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.