【NIO系列】——之Netty

這是NIO系列的第四篇,歡迎繼續關注:java

  1. 【NIO系列】——之TCP探祕
  2. 【NIO系列】——之IO模型
  3. 【NIO系列】——之Reactor模型

若是你看過前面三篇文章,咱們從最低層來分解NIO底層原理和使用方式,幫忙咱們理解了NIO是什麼,解決了什麼問題,以及又有那些不足。web

原則上NIO的出現,已經提高和加快了網絡IO的處理方式,但它只能幫忙咱們解決了IO層次的讀寫問題,在軟件層次上咱們須要更好的編程架構模型,來解決擴展性以及高併發的問題。Netty正是用來解決這些問題的,這一篇咱們將詳細介紹Netty的知識點。編程

 

1、netty是什麼

一、是什麼

咱們採用官方的話來講:tomcat

Netty是一個高性能、異步事件驅動的網絡應用框架。 基於Netty,能夠快速的開發和部署高性能、 高可用的網絡服務端和客戶端應用。安全

簡單來講,它是一個網絡應用框架,幫你解決面向網絡開發中的三個問題:服務器

  • 面向網絡IO的讀寫,如TCP的socket鏈接網絡

  • 應用層協議編解碼,如HTTP協議多線程

  • 高併發架構架構

那麼他有哪些優勢?併發

  • Fast —性能強悍,高併發,低延遲

  • Easy —高擴展,API使用簡單,開發門檻低

  • Non-blocking — 無阻塞,支持NIO

 

二、有何不一樣

對於咱們用慣了web容器的人來講,第一個疑惑就是netty究竟能幹什麼,爲什麼要用它。既然Netty是一個網絡應用框架,這些明明tomcat也能幫咱們解決了,爲什麼還要用Netty?

netty和tomcat的不一樣,主要在如下幾點:

  • tomcat是一個「HTTP Server」,更準確的說是一個「Servlet/JSP」應用的容器,它主要解決的是 HTTP 協議層面的傳輸和訪問。

  • HTTP是一種應用層協議,應用層的協議除了HTTP之外,還有面向郵局協議的POP和IMAP,以及FTP、LDAP、SSH、TLS/SSL等其餘協議。

  • Netty 不只能夠支持HTTP,還能夠支持FTP、LDAP、SSH、TLS/SSL等應用層多數的協議,另外還支持自定義的應用層協議,只要你有這方面的需求,它足夠靈活。

  • Netty 雖然按歸類上算屬於OSI的第七層【應用層】,但它的存在是幫你支持第三層【傳輸層】的問題,好比面向TCP,面向UDP,以及SCTP網絡協議的開發,它都能很好的支持。因此他能夠稱爲一個通訊組件。

  • 原則上,Tomcat的網絡通訊組件也能夠採用Netty,但servlet3.0以前徹底是同步阻塞模型,tomcat要遵循servlet規範因此不能最大發揮NIO的特性,而Netty不用遵循servlet規範,能夠最大化發揮NIO的特性,性能更高一些。

 

2、爲什麼要用Netty

Netty的三大特性(fast,easy,no-blocking)能夠詳細分解爲如下特性:

  • 支持NIO,異步編程

  • 性能強悍,高併發,低延遲,更少的資源佔用和內存使用率帶來更快的性能支撐,NIO領域最強

  • 成熟、穩定,你想到的一切tcp 問題,都已經解決,特別是NIO bug,以及完美解決了。

  • 不只僅支持http,支持多種應用協議和網絡協議,如TCP/UDP/UDT/SCTP/FTP/SMTP等。

  • 功能強大,預製了多種編解碼處理器,支持多種主流應用協議

  • API使用簡單,開發門檻低

 

1.Fast

爲什麼快,能夠參考上篇文章(【NIO系列】——之Reactor模型)介紹的Reactor IO模型。

 

Netty正是基於NIO實現了這種Reactor模型,Boss線程用來專門處理鏈接的創建,SubReactor專門用來處理IO的讀寫以及任務的處理。這種線程模型在充分利用CPU性能的狀況下支撐大量的併發鏈接。

 

具體效果如何,咱們看下測試數據:

以上是在techempower獲取的關於webFrame的純文本響應測試,能夠看到netty在響應速度方面,排名第二,雖然近幾年高性能的web框架不斷的挑戰,netty排名有所降低,但依然還能保持前列位置。

 

以上是在dubbo的官方測試案例中,給定協議序列化TPS對比,能夠看到採用netty的dubbo,在tps方面名列前茅。

 

2.更少的內存使用

Netty由於要面對大量的網絡數據包處理,因此會有大量的網絡對象建立和銷燬,對JVM來講有很大的壓力。

Netty主要採用兩種方案來緩解JVM的壓力:

  • ByteBufAllocator 對象池。池化ByteBuf實例以提升性能並最小化內存碎片,後者每次調用時都返回一個新的實例。

  • 零拷貝。支持DirectBuffer的使用,經過JVM的本地調用分配內存,這可避免每次調用本地I / O操做以前(或以後)將緩衝區的內容複製到(或從)中間緩衝區。

如下是twitter對應Netty4的使用對象池應用壓測:

 

3.Easy,快速開發

咱們知道牽扯到網絡上的編程會比較複雜,除了有一堆須要設置的TPC參數之外,還有一堆IO讀寫的問題要處理,同時爲了提升併發能力,還採用多線程,這就又帶來了線程安全的問題,每每給開發者帶來了很大的挑戰和複雜度。

而Netty 正是針對這些難點統一作了簡易化的封裝,除了讓API更加簡單易用之外,好比:TCP服務器的配置啓動,數據包buffByte的讀寫等。另外基於事件模式,對網絡事件進行串行處理,在保證了高效的同時,又下降了編程的複雜度。

爲什麼串行化執行會提高netty的性能:

  • 串行無鎖化設計,在IO線程內部進行串行操做,避免多線程競爭致使的性能降低。

  • 經過調整NIO線程池的線程參數,能夠同時啓動多個串行化的線程並行運行,這種局部無鎖化的串行線程設計相比一個隊列-多個工做線程模型性能更優

  • 減小上下文切換,以及狀態數據的同步

 

如下 是ChannelPipeline用來處理網絡事件的職責鏈,負責管理和執行ChannelHandler。網絡事件以事件流的形式在ChannelPipeline中流轉。支持插拔模式,擴展性也很強。

 

4.可靠穩定

在網絡方面,應用經常要面對複雜的網絡環境,好比網絡環境差,常會出現一些網絡閃斷,單通以及網絡阻塞等等一系列問題。另外做爲基礎的通訊組件,還須要考慮健壯性的問題,由於一旦出現bug,則會致使依賴的大量業務中斷。

Netty 在版本迭代中不斷的加入一些可靠性特性來知足用戶日益高漲的可靠性與健壯性需求。好比NIO的select空轉bug,好比TCP的斷線重連,keep-alive檢測等問題,Netty已經幫你解決了。

特別是Netty在推出4.0以後,已經被各家大廠採納爲通訊組件,這是對其可靠性驗證,也是社區對其穩定性的承認。如下是在使用netty的一些公司,更多參考netty.Adopters

 

3、如何用

上面介紹了這麼多Netty如何厲害,到編碼階段不會出現一堆代碼來要處理吧。

咱們來一個簡單的TCPServer來進行示例.

 

需求:

Client:接收用戶輸入的內容,併發送給Server,同時收並顯示Server返回的內容

Server:監聽8088端口,接收並顯示Client發送的內容,並給Client相應的回覆

 

代碼:

// step 1 設置boss 和 work
      EventLoopGroup bossGroup = new NioEventLoopGroup();
      EventLoopGroup workerGroup = new NioEventLoopGroup();
      try {
          // step 2 服務啓動輔助類
          ServerBootstrap b = new ServerBootstrap(); 
          // 設置線程池
          b.group(bossGroup, workerGroup)
              // step3 設置channel
              .channel(NioServerSocketChannel.class) 
              // setp4 設置channel hanlder
              .childHandler(new ChildChannelHandler())
              //設置發送和接受緩衝區大小
              .option(ChannelOption.SO_SNDBUF,256)
              .option(ChannelOption.SO_RCVBUF,256)
              // 小封包自動鏈接
              .option(ChannelOption.TCP_NODELAY,true)
              .childOption(ChannelOption.SO_KEEPALIVE, true)
              .handler(new LoggingHandler(LogLevel.INFO));
          // step 7 Bind 監聽端口.
          ChannelFuture f = b.bind(PORT).sync(); 
          logger.info("tcp server start success... ");
          f.channel().closeFuture().sync();
      } finally {
      }

ChildChannelHander 擴展Pipeline的執行策略:

protected void initChannel(SocketChannel channel) throws Exception {
      ChannelPipeline pipeline = channel.pipeline();
      pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
      pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
      pipeline.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
      pipeline.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
      pipeline.addLast(new TcpHelloServerHandler());
​
  }

 

經過以上代碼,咱們大概須要經過7個步驟,就能建立一個TCP的Server服務器了,看似7步較多,其實看代碼量來講,已經不多了。

經過以上的執行流程圖,咱們能夠看到:

  • ServerBootstrap是服務啓動(引導)輔助類,採用無參構造器(Builder模式)提供一系統方法用於設置啓動相關的參數

  • EventLoopGroup:Netty的Reactor線程池,負責維護一組EventLoop的調度工做,一般是bossEventLoop用來維護鏈接,subEventLoop來維護全部已註冊的Channel的I/O操做處理用戶自定義的Task和定時任務的Task

  • Channel:對Java原生的NIO類庫進行了封裝對通常用戶而言,不須要關心底層實現細節和工做原理,只須要指定具體使用哪一種Channel,用以鏈接IO設備(socket)的紐帶,提供與IO設備異步I/O操做的支持(如讀、寫、鏈接和綁定)

  • ChildChannelHandler,用來配置ChannelPipeline的執行策略。用來擴展的關鍵接口,用戶能夠完成大多數的功能定製,如消息編解碼、心跳、安全認證、TSL/SSL認證、流量控制等。

 

4、最後

以上咱們經過Netty 是什麼、怎麼用、爲什麼用三個方面闡述了Netty的優點以及基本的介紹,想必可以對Netty作一個基本的瞭解。後續咱們將對Netty高性能架構,以及多線程高效編程方面,結合源碼,會有更多的剖析。咱們不只僅關注源碼是如何實現了,還會結合整個架構,來分析設計理念,以及探討魯棒性很強的代碼的編寫方式。如有興趣,歡迎持續關注。

 

更多架構知識,歡迎關注個人公衆號,大碼候(cool_wier)

相關文章
相關標籤/搜索